diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..359bb5307e8535ab7d59faf27a7377033291821e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/Adapted-game-snake.iml b/.idea/Adapted-game-snake.iml new file mode 100644 index 0000000000000000000000000000000000000000..09bfacfed288b64e9f46bfe2e599d5bf0101045a --- /dev/null +++ b/.idea/Adapted-game-snake.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac208eec27ee4d864be399db22e1e97eeb874a96 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..4754f78d7964e15cab9c96605b62bbc1396b4f6f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..94a25f7f4cb416c083d265558da75d457237d671 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/audio/chat1.ogg b/audio/chat1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..dbf3923696f1f2c53de84bb84ca6eef9eab85c2b Binary files /dev/null and b/audio/chat1.ogg differ diff --git a/audio/gamebg01.ogg b/audio/gamebg01.ogg new file mode 100644 index 0000000000000000000000000000000000000000..971cbb67a12893975139d54144dfd4aa59d0ed2b Binary files /dev/null and b/audio/gamebg01.ogg differ diff --git a/audio/gamebg02.ogg b/audio/gamebg02.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f2b3d1a72b9b805bf5f940d92a892244420adcb0 Binary files /dev/null and b/audio/gamebg02.ogg differ diff --git a/audio/map1.ogg b/audio/map1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6045c9714a42569142ba4909e2fbbf157dc9b74c Binary files /dev/null and b/audio/map1.ogg differ diff --git a/audio/menubg1.ogg b/audio/menubg1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..8b8492172c656dd7cd3ca4af50b1597bd739405c Binary files /dev/null and b/audio/menubg1.ogg differ diff --git a/chat.py b/chat.py index 9b1d181f803d318e0b5c2f2ca6aaa2c03500b598..59ec7e709dd34f1e2609ca8701ead989a993c475 100644 --- a/chat.py +++ b/chat.py @@ -1,87 +1,87 @@ # 所有对话信息 -level_chats = (((1, "Hello there new challenger!"), - (1, "Welcome to Snake Quest, where you will face multiple challenges!"), - (2, "What's the prize?"), - (1, "The prize is endless food!"), - (2, "yeye how do I start"), - (1, "But first I need to go through the controls with ya"), - (1, "The snake will follow your cursor"), - (1, "Going outside of the boundary will kill you"), - (1, "When your hp drops to 0, you are basically dead"), - (1, "On the top left corner, is your current level. The higher the level the longer the snake"), - (1, "In the first stage, you only need to reach level 3"), - (2, "ezpz"), - (1, "You will regret saying that :^)"), - (1, "Finish this dialog whenever you are ready!")), +level_chats = (((1, "你好,新的挑战者!"), + (1, "欢迎来到蛇蛇闯关,在这里你将面临多重挑战!"), + (2, "奖品是什么?"), + (1, "奖品是无穷无尽的食物!"), + (2, "ye~ye~,我该如何开始"), + (1, "但首先我需要叫你怎么进行控制"), + (1, "蛇会跟随你的光标移动"), + (1, "别越过边界,你会寄的"), + (1, "你血条降到0你就彻底寄了"), + (1, "左上角是你当前的级别。等级越高,蛇越长"), + (1, "在第一阶段,你只需要达到3级"), + (2, "ezpz(蛇语)"), + (1, "你会后悔这么说的:^)"), + (1, "只要你准备好了,就完成这个对话框!")), - ((1, "Congrats on finishing stage one!"), - (1, "The real challenge begins now"), + ((1, "恭喜你完成了第一阶段!你小子不简单啊 "), + (1, "别高兴的太早,真正的挑战从现在开始"), (2, "git gud too ez"), - (1, "In stage two you will need to dodge cannonballs."), - (1, "Similarly, walking outside of the boundary will kill you."), - (2, "that's it?"), - (1, "yep that's it"), - (1, "But you will need to reach level 5 this time."), - (1, "Finish this dialog whenever you are ready!")), + (1, "在第二阶段,你需要躲避炮弹。"), + (1, "同样,走出边界也会杀死你。"), + (2, "就这?"), + (1, "对,就这"), + (1, "但这次你要干到 5 级。"), + (1, "只要你准备好了,就完成这个对话框!")), - ((1, "Looks like you are adapting to this."), - (1, "Here comes level 3."), - (2, "What's in level 3?"), - (1, "Lasers, four of them"), - (1, "It will be right in the middle, get ready to dodge"), - (1, "You gotta dodge them!"), - (2, "well that's all?"), - (1, "Not only that! In this stage each food gives double the xp"), - (1, "You will also need to reach level 10"), - (1, "In other words, you will grow longer"), - (2, "gotcha, I'm ready"), - (1, "Finish this dialog whenever you are ready!")), + ((1, "看来你已经准备好了"), + (1, "欢迎来到第三关"), + (2, "这关又有什么"), + (1, "激光,有四个,屌不屌"), + (1, "它们分布在地图中间,你得躲掉,不然碰到会掉血"), + (1, "害怕没?"), + (2, "嗯,仅此而已?"), + (1, "当然不是!在这个阶段,每种食物都会提供双倍的经验值,你就偷着乐把臭小子"), + (1, "而且这次你要升到十级"), + (1, "换种说法,你得变得更长"), + (2, "知道了,来吧"), + (1, "只要你准备好了,就完成这个对话框!")), - ((1, "Stage 4, is the hardest of them all in my opinion"), - (2, "yeyeye?"), - (1, "Do you remember the lazers?"), - (2, "Too ez with my dodging skills"), - (1, "But in this stage the lazers move as well~"), - (2, "?say what"), - (1, "Finish this dialog whenever you are ready!")), + ((1, "在我看来,第 4 阶段是其中最难的"), + (2, "确定?"), + (1, "你还记得激光炮吗?"), + (2, "在我的身法面前不足为惧"), + (1, "但是这关它们会动哦"), + (2, "?!"), + (1, "只要你准备好了,就完成这个对话框!")), - ((1, "We are starting stage 5!"), - (2, "Stage 4 was too ez, twas not hard at all"), - (1, "In that case, stage 5 will be even easier!"), - (1, "There's only one trick: run in circles"), - (2, "What does that mean?"), - (1, "You will see~"), - (1, "Hint: try not to stay in the middle after the first 2 seconds"), - (2, "I should stay at the edges?"), - (1, "Exactly~Finish this dialog whenever you are ready!")), + ((1, "我们正在开始第 5 阶段!"), + (2, "你就是瞎扯,第 4 阶段一点也不难"), + (1, "这么说来,对你来说第 5 阶段将更加容易!"), + (1, "只有一个诀窍:绕圈子跑"), + (2, "你说集贸呢?"), + (1, "你会知道的~"), + (1, "提示:前 2 秒后尽量不要停留在中间提示:前 2 秒后尽量不要停留在中间"), + (2, "那我留在边缘吗?"), + (1, "聪明~只要你准备好了,就完成这个对话框!")), - ((1, "Congrats on finishing level 5!"), - (2, "<_< that first laser scared me a lil"), - (1, "lol I will make stage 6 clearer"), - (2, "you'd better be <_<"), - (1, "you'll still get the laser, but an ultra large one!"), - (1, "It's so big that it covers half of the field!"), - (2, "but, how am I supposed to dodge that?"), - (1, "It's also so large that it needs a long time to recharge"), - (1, "Just dodge it while it's charging!"), - (2, "gocha"), - (1, "GL, end this dialog whenever you are ready!")), + ((1, "不错不错不错,竟然过了第五关"), + (2, "<_< 第一束激光吓坏死老子了"), + (1, "哈哈,我会让第 6 阶段更清晰"), + (2, "你最好是 <_<"), + (1, "你仍然看到激光,而且一个超大的激光!"), + (1, "它是如此之大,以至于它覆盖了一半的场地!"), + (2, "那我应该如何躲避呢?"), + (1, "没事它很大,所以它要费很长时间去充能"), + (1, "在它充电时躲避它!"), + (2, "yeah sir!!!!!"), + (1, "很好!只要你准备好了,就结束这个对话!")), - ((1, "Welcome to the last stage!"), - (2, "Finally!"), - (1, "It was a long way here! So I made this level easier."), - (2, "yeyeye?"), - (1, "You will need to reach level 15, and dodge a few things"), - (2, "ye?"), - (1, "GL, end this dialog whenever you are ready!")), + ((1, "欢迎来到最后阶段!"), + (2, "终于"), + (1, "这关很长,所以我给这个关卡设计的更容易。"), + (2, "哦豁?"), + (1, "你需要达到 15 级,并躲避一些东西"), + (2, "哦豁?"), + (1, "很好!只要你准备好了,就结束这个对话!")), - ((1, "Congrats! You finished the whole game!"), - (2, "Yea!!"), - (1, "Now you have gained access to infinite amount of food!"), + ((1, "恭喜!你完成了整个游戏!"), + (2, "ohohohohohoohohohoohoh!!"), + (1, "现在你已经获得了无限量的食物!"), (2, "yayay :DDDD"), - (1, "Thank you for playing!"), - (2, "It's a small game made with pygame by Jeff Yan"), - (2, "Thank you very much for playing!"), - (1, "Thank you!!!")) + (1, "感谢您的参与!"), + (2, "这是Jeff Yan用pygame制作的一个小游戏"), + (2, "非常感谢你们的参与!"), + (1, "非常感谢!!!")) ) diff --git a/config.py b/config.py index 49e5b56696d47f3459874ec2c533f14bdf1b9279..97b9d073c5245eb031cc910ecbde0d6f7b80fafb 100644 --- a/config.py +++ b/config.py @@ -23,7 +23,7 @@ level_font = pygame.font.Font("sanji.ttf", 40) clock = pygame.time.Clock() # 设置显示title -pygame.display.set_caption('SnakeQuest') +pygame.display.set_caption('蛇蛇闯关') # helper函数,获取两点间的距离 @@ -87,11 +87,11 @@ class AudioPlayer: # 初始化所有背景音乐 -bgs = (AudioPlayer("menubg", "bg"), - AudioPlayer("gamebg", "bg"), - AudioPlayer("gamebg2", "bg"), - AudioPlayer("chat", "bg"), - AudioPlayer("map", "bg", volume=0.06)) +bgs = (AudioPlayer("menubg1", "bg", volume=0.3), + AudioPlayer("gamebg01", "bg"), + AudioPlayer("gamebg02", "bg"), + AudioPlayer("chat1", "bg"), + AudioPlayer("map1", "bg")) # 初始化所有音效 ses = (AudioPlayer("sebigcannon", "se"), diff --git a/hud.py b/hud.py index 14ae4ba7b3f1a15dd396a9cbc330672cf1f3808f..e7691cd3ba31a605399a2aeaf2d5a235c9e32d8f 100644 --- a/hud.py +++ b/hud.py @@ -1,5 +1,20 @@ from config import * +import pygame # 添加 +class Timer: # 添加计时器类 + def __init__(self): + self.time = 0 + + def update(self): + self.time += 1 + + def draw(self): + hours = self.time // 3600 # 计算时 + minutes = (self.time % 3600) // 60 # 计算分 + seconds = self.time % 60 # 计算秒 + + time_text = chat_font.render(f"时间: {hours:02d}:{minutes:02d}:{seconds:02d}", False, (255, 255, 255)) + screen.blit(time_text, (30, 80)) class InfoBar: """ @@ -8,7 +23,7 @@ class InfoBar: def __init__(self, snake): self.snake = snake self.hp_lag = 300 - + self.timer = Timer() # 创建计时器对象(添加) self.level_text = None self.update() @@ -19,6 +34,8 @@ class InfoBar: self.hp_lag -= 2 else: self.hp_lag = self.snake.hp*3 + self.timer.update() # 更新计时器的状态(添加) + # 将当前数值绘制出来 def draw(self): @@ -29,3 +46,4 @@ class InfoBar: screen.blit(texture_lib["hp_bar"], (150, 370), pygame.Rect(300 - self.snake.hp * 3, 20, self.snake.hp * 3, 20)) screen.blit(texture_lib["hp_bar"], (150, 370), pygame.Rect(0, 0, 300, 20)) + self.timer.draw() # 绘制计时器组件(添加) \ No newline at end of file diff --git a/map.py b/map.py index 3c7c79f0034b74f967d1811f25da4667deae2c63..4a1f12fc314894ac15078b2f32ed686b15723592 100644 --- a/map.py +++ b/map.py @@ -96,7 +96,7 @@ class Map(Level): # 绘制背景底色 def draw_background(self, offset): - pygame.draw.rect(screen, (70, 70, 70), + pygame.draw.rect(screen, (200, 200, 70), pygame.Rect(offset[0] - self.deme[0], offset[1] - self.deme[1], self.deme[0] * 2, self.deme[1] * 2)) @@ -115,7 +115,7 @@ class Map(Level): screen.blit(texture_lib["danger"], (0, 0)) self.draw_boarder(offset) - pass_render = chat_font.render("Target:"+str(self.pass_level), False, (255, 255, 255)) + pass_render = chat_font.render("目标:升到"+str(self.pass_level)+"级", False, (255, 255, 255)) screen.blit(pass_render, (450, 30)) # 更新地图上的所有物品,若有物品为dead则删除物品,同时判断攻击tick,以及经验球与蛇的碰撞 diff --git a/menu.py b/menu.py index a765a45cca6cf94700df84a8f33fcabfaa35c33a..9bb2297d44463f05acee2a1bd8cf57a603e7e836 100644 --- a/menu.py +++ b/menu.py @@ -78,7 +78,7 @@ class MainMenu: # 绘制当前菜单 def draw(self): screen.blit(texture_lib["title_shadow"], (0, 46 + self.float_cycle.get())) - screen.blit(texture_lib["title"], (0, 47 + self.float_cycle2.get())) + screen.blit(texture_lib["title"], (0+60, 47 + self.float_cycle2.get())) if self.hovered == 1: screen.blit(texture_lib["start_button_hovered"], (0, 197)) else: @@ -147,8 +147,8 @@ class LevelSelect: for i in range(len(self.level_points)): screen.blit(self.status_texture[self.point_status[i]], (self.level_points[i][0] - self.scroll, self.level_points[i][1] + self.c_float)) - title = chat_font.render("Stage" + str(i+1), False, (255, 255, 255)) - title2 = chat_font.render("Stage" + str(i + 1), False, (0, 0, 0)) + title = chat_font.render("第" + str(i+1)+"关", False, (255, 255, 255)) + title2 = chat_font.render("第" + str(i + 1)+"关", False, (0, 0, 0)) screen.blit(title, (self.level_points[i][0] - self.scroll + 10, self.level_points[i][1] + self.c_float + 35)) screen.blit(title2, (self.level_points[i][0] - self.scroll + 8, diff --git a/scene.py b/scene.py index 02cc592e0871e7d089a1d6493fe2228b4e1a4412..a3e5a8a6c0e6bfea07d31fb7e1cd5793c2ff9730 100644 --- a/scene.py +++ b/scene.py @@ -38,6 +38,7 @@ class ChatBox: # 根据当前状态绘制聊天框,在进入最后阶段一段时间后会显示鼠标点击的提示 def draw(self): + if self.stage == 0: if self.tick < self.show_time: dif1 = ((self.pos[0] + self.width / 2 - 15 - self.start[0]) / self.show_time * self.tick, @@ -72,6 +73,7 @@ class ChatBox: pygame.Rect(0, 25 * self.click_cycle.get(), 25, 25)) + class TalkScene: """ 一个对话场景,左边的材质是character1,右边的材质是character2,chats的 diff --git a/screenshots/sc2.png b/screenshots/sc2.png index b1311bab009c966e89046abe6aa67110f2db80b9..e9a2222a43fd81e8f0f5277c301b302e63ac3fa3 100644 Binary files a/screenshots/sc2.png and b/screenshots/sc2.png differ diff --git a/snake.py b/snake.py index bd9f421f76f321e09ec136ef3c8fd0809cd07c6d..04c7f8a865db3c599dfede3c7e8542439a90ef01 100644 --- a/snake.py +++ b/snake.py @@ -1,4 +1,5 @@ from animations import * +import math class Segment: @@ -117,6 +118,10 @@ class Snake: for i in range(self.length - 1, -1, -1): self.poly_points.append(self.segments[i].get_point2(self.game.get_offset())) + + '''每升一级就能够回复损失的三分之一血量(向上取整),如果没有损失血量就不回复血量''' + + if self.current_exp > 100: ses[4].play_once() self.level += 1 @@ -125,8 +130,19 @@ class Snake: for point in self.hit_points: self.game.map.animations.append(LevelUp(point)) +#恢复 + # 计算回复的血量 + if self.hp < 100: # 如果当前血量小于最大血量 + restore_hp = min(math.ceil((100 - self.hp) / 3), 100 - self.hp) # 计算要回复的血量,同时考虑不超过最大血量 + self.hp = min(self.hp + restore_hp, 100) # 增加回复的血量,同时确保不超过最大血量 + self.outofbound = False + if self.outofbound: - self.hp -= 1 + self.position[0] = 0 + self.position[1] = 0 + self.hp = self.hp / 2 + if self.hp <= 1: + self.hp = -1 if self.hp < 0: self.dead = True diff --git a/texture/close_snake_1.png b/texture/close_snake_1.png index 6f537641e89109dee92ab8b05c001b172de141a4..22d2ecbc26f1f3f6adfe80e4ae1db55f68698ec6 100644 Binary files a/texture/close_snake_1.png and b/texture/close_snake_1.png differ diff --git a/texture/close_snake_2.png b/texture/close_snake_2.png index dedc22665d1af776ca6d04b09a2fe5a9b9b5f3c7..d9afc4c1e5e029c292ac7e40723f56d0dffaaebf 100644 Binary files a/texture/close_snake_2.png and b/texture/close_snake_2.png differ diff --git a/texture/title.png b/texture/title.png index b4b90a12fb04d58af86b0629c21b0afbc4a62e90..e5adfcab9afba1467fadc9bf44567e89d8fbf6d2 100644 Binary files a/texture/title.png and b/texture/title.png differ diff --git a/venv/Include/site/python3.7/pygame/_camera.h b/venv/Include/site/python3.7/pygame/_camera.h deleted file mode 100644 index 68ae98940674c3ea5d7a261777cc1a66ff58c1ef..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/_camera.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - pygame - Python Game Library - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -#ifndef _CAMERA_H -#define _CAMERA_H - -#include "_pygame.h" -#include "camera.h" - -#endif - diff --git a/venv/Include/site/python3.7/pygame/_pygame.h b/venv/Include/site/python3.7/pygame/_pygame.h deleted file mode 100644 index 68962fce0331bd2617914c8d3650a949f6e199f6..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/_pygame.h +++ /dev/null @@ -1,864 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -#ifndef _PYGAME_H -#define _PYGAME_H - -/** This header file includes all the definitions for the - ** base pygame extensions. This header only requires - ** SDL and Python includes. The reason for functions - ** prototyped with #define's is to allow for maximum - ** python portability. It also uses python as the - ** runtime linker, which allows for late binding. For more - ** information on this style of development, read the Python - ** docs on this subject. - ** http://www.python.org/doc/current/ext/using-cobjects.html - ** - ** If using this to build your own derived extensions, - ** you'll see that the functions available here are mainly - ** used to help convert between python objects and SDL objects. - ** Since this library doesn't add a lot of functionality to - ** the SDL libarary, it doesn't need to offer a lot either. - ** - ** When initializing your extension module, you must manually - ** import the modules you want to use. (this is the part about - ** using python as the runtime linker). Each module has its - ** own import_xxx() routine. You need to perform this import - ** after you have initialized your own module, and before - ** you call any routines from that module. Since every module - ** in pygame does this, there are plenty of examples. - ** - ** The base module does include some useful conversion routines - ** that you are free to use in your own extension. - ** - ** When making changes, it is very important to keep the - ** FIRSTSLOT and NUMSLOT constants up to date for each - ** section. Also be sure not to overlap any of the slots. - ** When you do make a mistake with this, it will result - ** is a dereferenced NULL pointer that is easier to diagnose - ** than it could be :] - **/ -#if defined(HAVE_SNPRINTF) /* defined in python.h (pyerrors.h) and SDL.h \ - (SDL_config.h) */ -#undef HAVE_SNPRINTF /* remove GCC redefine warning */ -#endif - -// This must be before all else -#if defined(__SYMBIAN32__) && defined(OPENC) -#include - -#if defined(__WINS__) -void * -_alloca(size_t size); -#define alloca _alloca -#endif -#endif - -#define PG_STRINGIZE_HELPER(x) #x -#define PG_STRINGIZE(x) PG_STRINGIZE_HELPER(x) -#define PG_WARN(desc) message(__FILE__ "(" PG_STRINGIZE(__LINE__) "): WARNING: " #desc) - -/* This is unconditionally defined in Python.h */ -#if defined(_POSIX_C_SOURCE) -#undef _POSIX_C_SOURCE -#endif - -#include - -/* the version macros are defined since version 1.9.5 */ -#define PG_MAJOR_VERSION 1 -#define PG_MINOR_VERSION 9 -#define PG_PATCH_VERSION 6 -#define PG_VERSIONNUM(MAJOR, MINOR, PATCH) (1000*(MAJOR) + 100*(MINOR) + (PATCH)) -#define PG_VERSION_ATLEAST(MAJOR, MINOR, PATCH) \ - (PG_VERSIONNUM(PG_MAJOR_VERSION, PG_MINOR_VERSION, PG_PATCH_VERSION) >= \ - PG_VERSIONNUM(MAJOR, MINOR, PATCH)) - -/* Cobjects vanish in Python 3.2; so we will code as though we use capsules */ -#if defined(Py_CAPSULE_H) -#define PG_HAVE_CAPSULE 1 -#else -#define PG_HAVE_CAPSULE 0 -#endif -#if defined(Py_COBJECT_H) -#define PG_HAVE_COBJECT 1 -#else -#define PG_HAVE_COBJECT 0 -#endif -#if !PG_HAVE_CAPSULE -#define PyCapsule_New(ptr, n, dfn) PyCObject_FromVoidPtr(ptr, dfn) -#define PyCapsule_GetPointer(obj, n) PyCObject_AsVoidPtr(obj) -#define PyCapsule_CheckExact(obj) PyCObject_Check(obj) -#endif - -/* Pygame uses Py_buffer (PEP 3118) to exchange array information internally; - * define here as needed. - */ -#if !defined(PyBUF_SIMPLE) -typedef struct bufferinfo { - void *buf; - PyObject *obj; - Py_ssize_t len; - Py_ssize_t itemsize; - int readonly; - int ndim; - char *format; - Py_ssize_t *shape; - Py_ssize_t *strides; - Py_ssize_t *suboffsets; - void *internal; -} Py_buffer; - -/* Flags for getting buffers */ -#define PyBUF_SIMPLE 0 -#define PyBUF_WRITABLE 0x0001 -/* we used to include an E, backwards compatible alias */ -#define PyBUF_WRITEABLE PyBUF_WRITABLE -#define PyBUF_FORMAT 0x0004 -#define PyBUF_ND 0x0008 -#define PyBUF_STRIDES (0x0010 | PyBUF_ND) -#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) -#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) -#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) -#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) - -#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE) -#define PyBUF_CONTIG_RO (PyBUF_ND) - -#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE) -#define PyBUF_STRIDED_RO (PyBUF_STRIDES) - -#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT) -#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT) - -#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT) -#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT) - -#define PyBUF_READ 0x100 -#define PyBUF_WRITE 0x200 -#define PyBUF_SHADOW 0x400 - -typedef int (*getbufferproc)(PyObject *, Py_buffer *, int); -typedef void (*releasebufferproc)(Py_buffer *); -#endif /* #if !defined(PyBUF_SIMPLE) */ - -/* Flag indicating a pg_buffer; used for assertions within callbacks */ -#ifndef NDEBUG -#define PyBUF_PYGAME 0x4000 -#endif - -#define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F)) - -/* Array information exchange struct C type; inherits from Py_buffer - * - * Pygame uses its own Py_buffer derived C struct as an internal representation - * of an imported array buffer. The extended Py_buffer allows for a - * per-instance release callback, - */ -typedef void (*pybuffer_releaseproc)(Py_buffer *); - -typedef struct pg_bufferinfo_s { - Py_buffer view; - PyObject *consumer; /* Input: Borrowed reference */ - pybuffer_releaseproc release_buffer; -} pg_buffer; - -/* Operating system specific adjustments - */ -// No signal() -#if defined(__SYMBIAN32__) && defined(HAVE_SIGNAL_H) -#undef HAVE_SIGNAL_H -#endif - -#if defined(HAVE_SNPRINTF) -#undef HAVE_SNPRINTF -#endif - -#ifdef MS_WIN32 /*Python gives us MS_WIN32, SDL needs just WIN32*/ -#ifndef WIN32 -#define WIN32 -#endif -#endif - -/// Prefix when initializing module -#define MODPREFIX "" -/// Prefix when importing module -#define IMPPREFIX "pygame." - -#ifdef __SYMBIAN32__ -#undef MODPREFIX -#undef IMPPREFIX -// On Symbian there is no pygame package. The extensions are built-in or in -// sys\bin. -#define MODPREFIX "pygame_" -#define IMPPREFIX "pygame_" -#endif - -#include - -/* Pygame's SDL version macros: - * IS_SDLv1 is 1 if SDL 1.x.x, 0 otherwise - * IS_SDLv2 is 1 if at least SDL 2.0.0, 0 otherwise - */ -#if (SDL_VERSION_ATLEAST(2, 0, 0)) -#define IS_SDLv1 0 -#define IS_SDLv2 1 -#else -#define IS_SDLv1 1 -#define IS_SDLv2 0 -#endif - -/*#if IS_SDLv1 && PG_MAJOR_VERSION >= 2 -#error pygame 2 requires SDL 2 -#endif*/ - -#if IS_SDLv2 -/* SDL 1.2 constants removed from SDL 2 */ -typedef enum { - SDL_HWSURFACE = 0, - SDL_RESIZABLE = SDL_WINDOW_RESIZABLE, - SDL_ASYNCBLIT = 0, - SDL_OPENGL = SDL_WINDOW_OPENGL, - SDL_OPENGLBLIT = 0, - SDL_ANYFORMAT = 0, - SDL_HWPALETTE = 0, - SDL_DOUBLEBUF = 0, - SDL_FULLSCREEN = SDL_WINDOW_FULLSCREEN, - SDL_HWACCEL = 0, - SDL_SRCCOLORKEY = 0, - SDL_RLEACCELOK = 0, - SDL_SRCALPHA = 0, - SDL_NOFRAME = SDL_WINDOW_BORDERLESS, - SDL_GL_SWAP_CONTROL = 0, - TIMER_RESOLUTION = 0 -} PygameVideoFlags; - -/* the wheel button constants were removed from SDL 2 */ -typedef enum { - PGM_BUTTON_LEFT = SDL_BUTTON_LEFT, - PGM_BUTTON_RIGHT = SDL_BUTTON_RIGHT, - PGM_BUTTON_MIDDLE = SDL_BUTTON_MIDDLE, - PGM_BUTTON_WHEELUP = 4, - PGM_BUTTON_WHEELDOWN = 5, - PGM_BUTTON_X1 = SDL_BUTTON_X1 + 2, - PGM_BUTTON_X2 = SDL_BUTTON_X2 + 2, - PGM_BUTTON_KEEP = 0x80 -} PygameMouseFlags; - -typedef enum { - SDL_NOEVENT = 0, - /* SDL 1.2 allowed for 8 user defined events. */ - SDL_NUMEVENTS = SDL_USEREVENT + 8, - SDL_ACTIVEEVENT = SDL_NUMEVENTS, - PGE_EVENTBEGIN = SDL_NUMEVENTS, - SDL_VIDEORESIZE, - SDL_VIDEOEXPOSE, - PGE_KEYREPEAT, - PGE_EVENTEND -} PygameEventCode; - -#define PGE_NUMEVENTS (PGE_EVENTEND - PGE_EVENTBEGIN) - -typedef enum { - SDL_APPFOCUSMOUSE, - SDL_APPINPUTFOCUS, - SDL_APPACTIVE -} PygameAppCode; - -/* Surface flags: based on SDL 1.2 flags */ -typedef enum { - PGS_SWSURFACE = 0x00000000, - PGS_HWSURFACE = 0x00000001, - PGS_ASYNCBLIT = 0x00000004, - - PGS_ANYFORMAT = 0x10000000, - PGS_HWPALETTE = 0x20000000, - PGS_DOUBLEBUF = 0x40000000, - PGS_FULLSCREEN = 0x80000000, - PGS_OPENGL = 0x00000002, - PGS_OPENGLBLIT = 0x0000000A, - PGS_RESIZABLE = 0x00000010, - PGS_NOFRAME = 0x00000020, - PGS_SHOWN = 0x00000040, /* Added from SDL 2 */ - PGS_HIDDEN = 0x00000080, /* Added from SDL 2 */ - - PGS_HWACCEL = 0x00000100, - PGS_SRCCOLORKEY = 0x00001000, - PGS_RLEACCELOK = 0x00002000, - PGS_RLEACCEL = 0x00004000, - PGS_SRCALPHA = 0x00010000, - PGS_PREALLOC = 0x01000000 -} PygameSurfaceFlags; - -typedef struct { - Uint32 hw_available:1; - Uint32 wm_available:1; - Uint32 blit_hw:1; - Uint32 blit_hw_CC:1; - Uint32 blit_hw_A:1; - Uint32 blit_sw:1; - Uint32 blit_sw_CC:1; - Uint32 blit_sw_A:1; - Uint32 blit_fill:1; - Uint32 video_mem; - SDL_PixelFormat *vfmt; - SDL_PixelFormat vfmt_data; - int current_w; - int current_h; -} pg_VideoInfo; - -#endif /* IS_SDLv2 */ -/* macros used throughout the source */ -#define RAISE(x, y) (PyErr_SetString((x), (y)), (PyObject *)NULL) - -#ifdef WITH_THREAD -#define PG_CHECK_THREADS() (1) -#else /* ~WITH_THREAD */ -#define PG_CHECK_THREADS() \ - (RAISE(PyExc_NotImplementedError, \ - "Python built without thread support")) -#endif /* ~WITH_THREAD */ - -#define PyType_Init(x) (((x).ob_type) = &PyType_Type) -#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API" - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -#ifndef ABS -#define ABS(a) (((a) < 0) ? -(a) : (a)) -#endif - -/* test sdl initializations */ -#define VIDEO_INIT_CHECK() \ - if (!SDL_WasInit(SDL_INIT_VIDEO)) \ - return RAISE(pgExc_SDLError, "video system not initialized") - -#define CDROM_INIT_CHECK() \ - if (!SDL_WasInit(SDL_INIT_CDROM)) \ - return RAISE(pgExc_SDLError, "cdrom system not initialized") - -#define JOYSTICK_INIT_CHECK() \ - if (!SDL_WasInit(SDL_INIT_JOYSTICK)) \ - return RAISE(pgExc_SDLError, "joystick system not initialized") - -/* BASE */ -#define VIEW_CONTIGUOUS 1 -#define VIEW_C_ORDER 2 -#define VIEW_F_ORDER 4 - -#define PYGAMEAPI_BASE_FIRSTSLOT 0 -#if IS_SDLv1 -#define PYGAMEAPI_BASE_NUMSLOTS 19 -#else /* IS_SDLv2 */ -#define PYGAMEAPI_BASE_NUMSLOTS 23 -#endif /* IS_SDLv2 */ -#ifndef PYGAMEAPI_BASE_INTERNAL -#define pgExc_SDLError ((PyObject *)PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT]) - -#define pg_RegisterQuit \ - (*(void (*)(void (*)(void)))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 1]) - -#define pg_IntFromObj \ - (*(int (*)(PyObject *, int *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 2]) - -#define pg_IntFromObjIndex \ - (*(int (*)(PyObject *, int, \ - int *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 3]) - -#define pg_TwoIntsFromObj \ - (*(int (*)(PyObject *, int *, \ - int *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 4]) - -#define pg_FloatFromObj \ - (*(int (*)(PyObject *, float *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 5]) - -#define pg_FloatFromObjIndex \ - (*(int (*)(PyObject *, int, \ - float *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 6]) - -#define pg_TwoFloatsFromObj \ - (*(int (*)(PyObject *, float *, \ - float *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 7]) - -#define pg_UintFromObj \ - (*(int (*)(PyObject *, \ - Uint32 *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 8]) - -#define pg_UintFromObjIndex \ - (*(int (*)(PyObject *, int, \ - Uint32 *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 9]) - -#define pgVideo_AutoQuit \ - (*(void (*)(void))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 10]) - -#define pgVideo_AutoInit \ - (*(int (*)(void))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 11]) - -#define pg_RGBAFromObj \ - (*(int (*)(PyObject *, \ - Uint8 *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 12]) - -#define pgBuffer_AsArrayInterface \ - (*(PyObject * (*)(Py_buffer *)) \ - PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 13]) - -#define pgBuffer_AsArrayStruct \ - (*(PyObject * (*)(Py_buffer *)) \ - PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 14]) - -#define pgObject_GetBuffer \ - (*(int (*)(PyObject *, pg_buffer *, \ - int))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 15]) - -#define pgBuffer_Release \ - (*(void (*)(pg_buffer *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 16]) - -#define pgDict_AsBuffer \ - (*(int (*)(pg_buffer *, PyObject *, \ - int))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 17]) - -#define pgExc_BufferError \ - ((PyObject *)PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 18]) - -#if IS_SDLv2 -#define pg_GetDefaultWindow \ - (*(SDL_Window * (*)(void)) PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 19]) - -#define pg_SetDefaultWindow \ - (*(void (*)(SDL_Window *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 20]) - -#define pg_GetDefaultWindowSurface \ - (*(PyObject * (*)(void)) PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 21]) - -#define pg_SetDefaultWindowSurface \ - (*(void (*)(PyObject *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 22]) - -#endif /* IS_SDLv2 */ - -#define import_pygame_base() IMPORT_PYGAME_MODULE(base, BASE) -#endif - -/* RECT */ -#define PYGAMEAPI_RECT_FIRSTSLOT \ - (PYGAMEAPI_BASE_FIRSTSLOT + PYGAMEAPI_BASE_NUMSLOTS) -#define PYGAMEAPI_RECT_NUMSLOTS 4 - -#if IS_SDLv1 -typedef struct { - int x, y; - int w, h; -} GAME_Rect; -#else -typedef SDL_Rect GAME_Rect; -#endif - -typedef struct { - PyObject_HEAD GAME_Rect r; - PyObject *weakreflist; -} pgRectObject; - -#define pgRect_AsRect(x) (((pgRectObject *)x)->r) -#ifndef PYGAMEAPI_RECT_INTERNAL -#define pgRect_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 0]) -#define pgRect_Type \ - (*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 0]) -#define pgRect_New \ - (*(PyObject * (*)(SDL_Rect *)) PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 1]) -#define pgRect_New4 \ - (*(PyObject * (*)(int, int, int, int)) \ - PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 2]) -#define pgRect_FromObject \ - (*(GAME_Rect * (*)(PyObject *, GAME_Rect *)) \ - PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 3]) - -#define import_pygame_rect() IMPORT_PYGAME_MODULE(rect, RECT) -#endif - -/* CDROM */ -#define PYGAMEAPI_CDROM_FIRSTSLOT \ - (PYGAMEAPI_RECT_FIRSTSLOT + PYGAMEAPI_RECT_NUMSLOTS) -#define PYGAMEAPI_CDROM_NUMSLOTS 2 - -typedef struct { - PyObject_HEAD int id; -} pgCDObject; - -#define pgCD_AsID(x) (((pgCDObject *)x)->id) -#ifndef PYGAMEAPI_CDROM_INTERNAL -#define pgCD_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_CDROM_FIRSTSLOT + 0]) -#define pgCD_Type \ - (*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_CDROM_FIRSTSLOT + 0]) -#define pgCD_New \ - (*(PyObject * (*)(int)) PyGAME_C_API[PYGAMEAPI_CDROM_FIRSTSLOT + 1]) - -#define import_pygame_cd() IMPORT_PYGAME_MODULE(cdrom, CDROM) -#endif - -/* JOYSTICK */ -#define PYGAMEAPI_JOYSTICK_FIRSTSLOT \ - (PYGAMEAPI_CDROM_FIRSTSLOT + PYGAMEAPI_CDROM_NUMSLOTS) -#define PYGAMEAPI_JOYSTICK_NUMSLOTS 2 - -typedef struct { - PyObject_HEAD int id; -} pgJoystickObject; - -#define pgJoystick_AsID(x) (((pgJoystickObject *)x)->id) - -#ifndef PYGAMEAPI_JOYSTICK_INTERNAL -#define pgJoystick_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_JOYSTICK_FIRSTSLOT + 0]) - -#define pgJoystick_Type \ - (*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_JOYSTICK_FIRSTSLOT + 0]) -#define pgJoystick_New \ - (*(PyObject * (*)(int)) PyGAME_C_API[PYGAMEAPI_JOYSTICK_FIRSTSLOT + 1]) - -#define import_pygame_joystick() IMPORT_PYGAME_MODULE(joystick, JOYSTICK) -#endif - -/* DISPLAY */ -#define PYGAMEAPI_DISPLAY_FIRSTSLOT \ - (PYGAMEAPI_JOYSTICK_FIRSTSLOT + PYGAMEAPI_JOYSTICK_NUMSLOTS) -#define PYGAMEAPI_DISPLAY_NUMSLOTS 2 - -typedef struct { -#if IS_SDLv1 - PyObject_HEAD SDL_VideoInfo info; -#else - PyObject_HEAD pg_VideoInfo info; -#endif -} pgVidInfoObject; - -#define pgVidInfo_AsVidInfo(x) (((pgVidInfoObject *)x)->info) -#ifndef PYGAMEAPI_DISPLAY_INTERNAL -#define pgVidInfo_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 0]) - -#define pgVidInfo_Type \ - (*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 0]) - -#if IS_SDLv1 -#define pgVidInfo_New \ - (*(PyObject * (*)(SDL_VideoInfo *)) \ - PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 1]) -#else -#define pgVidInfo_New \ - (*(PyObject * (*)(pg_VideoInfo *)) \ - PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 1]) -#endif - -#define import_pygame_display() IMPORT_PYGAME_MODULE(display, DISPLAY) -#endif - -/* SURFACE */ -#define PYGAMEAPI_SURFACE_FIRSTSLOT \ - (PYGAMEAPI_DISPLAY_FIRSTSLOT + PYGAMEAPI_DISPLAY_NUMSLOTS) -#define PYGAMEAPI_SURFACE_NUMSLOTS 3 -typedef struct { - PyObject_HEAD SDL_Surface *surf; -#if IS_SDLv2 - int owner; -#endif /* IS_SDLv2 */ - struct pgSubSurface_Data *subsurface; /*ptr to subsurface data (if a - * subsurface)*/ - PyObject *weakreflist; - PyObject *locklist; - PyObject *dependency; -} pgSurfaceObject; -#define pgSurface_AsSurface(x) (((pgSurfaceObject *)x)->surf) -#ifndef PYGAMEAPI_SURFACE_INTERNAL -#define pgSurface_Check(x) \ - (PyObject_IsInstance((x), \ - (PyObject *)PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 0])) -#define pgSurface_Type \ - (*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 0]) -#if IS_SDLv1 -#define pgSurface_New \ - (*(PyObject * (*)(SDL_Surface *)) \ - PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 1]) -#else /* IS_SDLv2 */ -#define pgSurface_New2 \ - (*(PyObject * (*)(SDL_Surface *, int)) \ - PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 1]) -#endif /* IS_SDLv2 */ -#define pgSurface_Blit \ - (*(int (*)(PyObject *, PyObject *, SDL_Rect *, SDL_Rect *, \ - int))PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 2]) - -#define import_pygame_surface() \ - do { \ - IMPORT_PYGAME_MODULE(surface, SURFACE); \ - if (PyErr_Occurred() != NULL) \ - break; \ - IMPORT_PYGAME_MODULE(surflock, SURFLOCK); \ - } while (0) - -#if IS_SDLv2 -#define pgSurface_New(surface) pgSurface_New2((surface), 1) -#define pgSurface_NewNoOwn(surface) pgSurface_New2((surface), 0) -#endif /* IS_SDLv2 */ - -#endif - -/* SURFLOCK */ /*auto import/init by surface*/ -#define PYGAMEAPI_SURFLOCK_FIRSTSLOT \ - (PYGAMEAPI_SURFACE_FIRSTSLOT + PYGAMEAPI_SURFACE_NUMSLOTS) -#define PYGAMEAPI_SURFLOCK_NUMSLOTS 8 -struct pgSubSurface_Data { - PyObject *owner; - int pixeloffset; - int offsetx, offsety; -}; - -typedef struct { - PyObject_HEAD PyObject *surface; - PyObject *lockobj; - PyObject *weakrefs; -} pgLifetimeLockObject; - -#ifndef PYGAMEAPI_SURFLOCK_INTERNAL -#define pgLifetimeLock_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 0]) -#define pgSurface_Prep(x) \ - if (((pgSurfaceObject *)x)->subsurface) \ - (*(*(void (*)( \ - PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 1]))(x) - -#define pgSurface_Unprep(x) \ - if (((pgSurfaceObject *)x)->subsurface) \ - (*(*(void (*)( \ - PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 2]))(x) - -#define pgSurface_Lock \ - (*(int (*)(PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 3]) -#define pgSurface_Unlock \ - (*(int (*)(PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 4]) -#define pgSurface_LockBy \ - (*(int (*)(PyObject *, \ - PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 5]) -#define pgSurface_UnlockBy \ - (*(int (*)(PyObject *, \ - PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 6]) -#define pgSurface_LockLifetime \ - (*(PyObject * (*)(PyObject *, PyObject *)) \ - PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 7]) -#endif - -/* EVENT */ -#define PYGAMEAPI_EVENT_FIRSTSLOT \ - (PYGAMEAPI_SURFLOCK_FIRSTSLOT + PYGAMEAPI_SURFLOCK_NUMSLOTS) -#if IS_SDLv1 -#define PYGAMEAPI_EVENT_NUMSLOTS 4 -#else /* IS_SDLv2 */ -#define PYGAMEAPI_EVENT_NUMSLOTS 6 -#endif /* IS_SDLv2 */ - -typedef struct { - PyObject_HEAD int type; - PyObject *dict; -} pgEventObject; - -#ifndef PYGAMEAPI_EVENT_INTERNAL -#define pgEvent_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 0]) -#define pgEvent_Type \ - (*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 0]) -#define pgEvent_New \ - (*(PyObject * (*)(SDL_Event *)) \ - PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 1]) -#define pgEvent_New2 \ - (*(PyObject * (*)(int, PyObject *)) \ - PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 2]) -#define pgEvent_FillUserEvent \ - (*(int (*)(pgEventObject *, \ - SDL_Event *))PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 3]) -#if IS_SDLv2 -#define pg_EnableKeyRepeat \ - (*(int (*)(int, int))PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 4]) -#define pg_GetKeyRepeat \ - (*(void (*)(int *, int *))PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 5]) -#endif /* IS_SDLv2 */ -#define import_pygame_event() IMPORT_PYGAME_MODULE(event, EVENT) -#endif - -/* RWOBJECT */ -/*the rwobject are only needed for C side work, not accessable from python*/ -#define PYGAMEAPI_RWOBJECT_FIRSTSLOT \ - (PYGAMEAPI_EVENT_FIRSTSLOT + PYGAMEAPI_EVENT_NUMSLOTS) -#define PYGAMEAPI_RWOBJECT_NUMSLOTS 6 -#ifndef PYGAMEAPI_RWOBJECT_INTERNAL -#define pgRWops_FromObject \ - (*(SDL_RWops * (*)(PyObject *)) \ - PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 0]) -#define pgRWops_IsFileObject \ - (*(int (*)(SDL_RWops *))PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 1]) -#define pg_EncodeFilePath \ - (*(PyObject * (*)(PyObject *, PyObject *)) \ - PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 2]) -#define pg_EncodeString \ - (*(PyObject * (*)(PyObject *, const char *, const char *, PyObject *)) \ - PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 3]) -#define pgRWops_FromFileObject \ - (*(SDL_RWops * (*)(PyObject *)) \ - PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 4]) -#define pgRWops_ReleaseObject \ - (*(int (*)(SDL_RWops *)) \ - PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 5]) -#define import_pygame_rwobject() IMPORT_PYGAME_MODULE(rwobject, RWOBJECT) - -#endif - -/* PixelArray */ -#define PYGAMEAPI_PIXELARRAY_FIRSTSLOT \ - (PYGAMEAPI_RWOBJECT_FIRSTSLOT + PYGAMEAPI_RWOBJECT_NUMSLOTS) -#define PYGAMEAPI_PIXELARRAY_NUMSLOTS 2 -#ifndef PYGAMEAPI_PIXELARRAY_INTERNAL -#define PyPixelArray_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_PIXELARRAY_FIRSTSLOT + 0]) -#define PyPixelArray_New \ - (*(PyObject * (*)) PyGAME_C_API[PYGAMEAPI_PIXELARRAY_FIRSTSLOT + 1]) -#define import_pygame_pixelarray() IMPORT_PYGAME_MODULE(pixelarray, PIXELARRAY) -#endif /* PYGAMEAPI_PIXELARRAY_INTERNAL */ - -/* Color */ -#define PYGAMEAPI_COLOR_FIRSTSLOT \ - (PYGAMEAPI_PIXELARRAY_FIRSTSLOT + PYGAMEAPI_PIXELARRAY_NUMSLOTS) -#define PYGAMEAPI_COLOR_NUMSLOTS 4 -#ifndef PYGAMEAPI_COLOR_INTERNAL -#define pgColor_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 0]) -#define pgColor_Type (*(PyObject *)PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT]) -#define pgColor_New \ - (*(PyObject * (*)(Uint8 *)) PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 1]) -#define pgColor_NewLength \ - (*(PyObject * (*)(Uint8 *, Uint8)) \ - PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 3]) - -#define pg_RGBAFromColorObj \ - (*(int (*)(PyObject *, \ - Uint8 *))PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 2]) -#define import_pygame_color() IMPORT_PYGAME_MODULE(color, COLOR) -#endif /* PYGAMEAPI_COLOR_INTERNAL */ - -/* Math */ -#define PYGAMEAPI_MATH_FIRSTSLOT \ - (PYGAMEAPI_COLOR_FIRSTSLOT + PYGAMEAPI_COLOR_NUMSLOTS) -#define PYGAMEAPI_MATH_NUMSLOTS 2 -#ifndef PYGAMEAPI_MATH_INTERNAL -#define pgVector2_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_MATH_FIRSTSLOT + 0]) -#define pgVector3_Check(x) \ - ((x)->ob_type == \ - (PyTypeObject *)PyGAME_C_API[PYGAMEAPI_MATH_FIRSTSLOT + 1]) -/* -#define pgVector2_New \ - (*(PyObject*(*)) PyGAME_C_API[PYGAMEAPI_MATH_FIRSTSLOT + 1]) -*/ -#define import_pygame_math() IMPORT_PYGAME_MODULE(math, MATH) -#endif /* PYGAMEAPI_MATH_INTERNAL */ - -#define PG_CAPSULE_NAME(m) (IMPPREFIX m "." PYGAMEAPI_LOCAL_ENTRY) - -#define _IMPORT_PYGAME_MODULE(module, MODULE, api_root) \ - { \ - PyObject *_module = PyImport_ImportModule(IMPPREFIX #module); \ - \ - if (_module != NULL) { \ - PyObject *_c_api = \ - PyObject_GetAttrString(_module, PYGAMEAPI_LOCAL_ENTRY); \ - \ - Py_DECREF(_module); \ - if (_c_api != NULL && PyCapsule_CheckExact(_c_api)) { \ - void **localptr = (void **)PyCapsule_GetPointer( \ - _c_api, PG_CAPSULE_NAME(#module)); \ - \ - if (localptr != NULL) { \ - memcpy(api_root + PYGAMEAPI_##MODULE##_FIRSTSLOT, \ - localptr, \ - sizeof(void **) * PYGAMEAPI_##MODULE##_NUMSLOTS); \ - } \ - } \ - Py_XDECREF(_c_api); \ - } \ - } - -#ifndef NO_PYGAME_C_API -#define IMPORT_PYGAME_MODULE(module, MODULE) \ - _IMPORT_PYGAME_MODULE(module, MODULE, PyGAME_C_API) -#define PYGAMEAPI_TOTALSLOTS \ - (PYGAMEAPI_MATH_FIRSTSLOT + PYGAMEAPI_MATH_NUMSLOTS) - -#ifdef PYGAME_H -void *PyGAME_C_API[PYGAMEAPI_TOTALSLOTS] = {NULL}; -#else -extern void *PyGAME_C_API[PYGAMEAPI_TOTALSLOTS]; -#endif -#endif - -#if PG_HAVE_CAPSULE -#define encapsulate_api(ptr, module) \ - PyCapsule_New(ptr, PG_CAPSULE_NAME(module), NULL) -#else -#define encapsulate_api(ptr, module) PyCObject_FromVoidPtr(ptr, NULL) -#endif - -#ifndef PG_INLINE -#if defined(__clang__) -#define PG_INLINE __inline__ __attribute__((__unused__)) -#elif defined(__GNUC__) -#define PG_INLINE __inline__ -#elif defined(_MSC_VER) -#define PG_INLINE __inline -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#define PG_INLINE inline -#else -#define PG_INLINE -#endif -#endif - -/*last platform compiler stuff*/ -#if defined(macintosh) && defined(__MWERKS__) || defined(__SYMBIAN32__) -#define PYGAME_EXPORT __declspec(export) -#else -#define PYGAME_EXPORT -#endif - - -#endif /* PYGAME_H */ diff --git a/venv/Include/site/python3.7/pygame/_surface.h b/venv/Include/site/python3.7/pygame/_surface.h deleted file mode 100644 index 016aac0156150854fe3f778cd4855e88d56d4ddd..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/_surface.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - Copyright (C) 2007 Marcus von Appen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -#ifndef _SURFACE_H -#define _SURFACE_H - -#include "_pygame.h" -#include "surface.h" - -#endif - diff --git a/venv/Include/site/python3.7/pygame/bitmask.h b/venv/Include/site/python3.7/pygame/bitmask.h deleted file mode 100644 index 12304979ea7490f64de96aa73b693c57beb41c44..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/bitmask.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - Bitmask 1.7 - A pixel-perfect collision detection library. - - Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount - function which is copyright (C) Donald W. Gillies, 1992. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef BITMASK_H -#define BITMASK_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -/* Define INLINE for different compilers. If your compiler does not - support inlining then there might be a performance hit in - bitmask_overlap_area(). -*/ -#ifndef INLINE -# ifdef __GNUC__ -# define INLINE inline -# else -# ifdef _MSC_VER -# define INLINE __inline -# else -# define INLINE -# endif -# endif -#endif - -#define BITMASK_W unsigned long int -#define BITMASK_W_LEN (sizeof(BITMASK_W)*CHAR_BIT) -#define BITMASK_W_MASK (BITMASK_W_LEN - 1) -#define BITMASK_N(n) ((BITMASK_W)1 << (n)) - -typedef struct bitmask -{ - int w,h; - BITMASK_W bits[1]; -} bitmask_t; - -/* Creates a bitmask of width w and height h, where - w and h must both be greater than or equal to 0. - The mask is automatically cleared when created. - */ -bitmask_t *bitmask_create(int w, int h); - -/* Frees all the memory allocated by bitmask_create for m. */ -void bitmask_free(bitmask_t *m); - -/* Clears all bits in the mask */ -void bitmask_clear(bitmask_t *m); - -/* Sets all bits in the mask */ -void bitmask_fill(bitmask_t *m); - -/* Flips all bits in the mask */ -void bitmask_invert(bitmask_t *m); - -/* Counts the bits in the mask */ -unsigned int bitmask_count(bitmask_t *m); - -/* Returns nonzero if the bit at (x,y) is set. Coordinates start at - (0,0) */ -static INLINE int bitmask_getbit(const bitmask_t *m, int x, int y) -{ - return (m->bits[x/BITMASK_W_LEN*m->h + y] & BITMASK_N(x & BITMASK_W_MASK)) != 0; -} - -/* Sets the bit at (x,y) */ -static INLINE void bitmask_setbit(bitmask_t *m, int x, int y) -{ - m->bits[x/BITMASK_W_LEN*m->h + y] |= BITMASK_N(x & BITMASK_W_MASK); -} - -/* Clears the bit at (x,y) */ -static INLINE void bitmask_clearbit(bitmask_t *m, int x, int y) -{ - m->bits[x/BITMASK_W_LEN*m->h + y] &= ~BITMASK_N(x & BITMASK_W_MASK); -} - -/* Returns nonzero if the masks overlap with the given offset. - The overlap tests uses the following offsets (which may be negative): - - +----+----------.. - |A | yoffset - | +-+----------.. - +--|B - |xoffset - | | - : : -*/ -int bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); - -/* Like bitmask_overlap(), but will also give a point of intersection. - x and y are given in the coordinates of mask a, and are untouched - if there is no overlap. */ -int bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, - int xoffset, int yoffset, int *x, int *y); - -/* Returns the number of overlapping 'pixels' */ -int bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); - -/* Fills a mask with the overlap of two other masks. A bitwise AND. */ -void bitmask_overlap_mask (const bitmask_t *a, const bitmask_t *b, bitmask_t *c, int xoffset, int yoffset); - -/* Draws mask b onto mask a (bitwise OR). Can be used to compose large - (game background?) mask from several submasks, which may speed up - the testing. */ - -void bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); - -void bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset); - -/* Return a new scaled bitmask, with dimensions w*h. The quality of the - scaling may not be perfect for all circumstances, but it should - be reasonable. If either w or h is 0 a clear 1x1 mask is returned. */ -bitmask_t *bitmask_scale(const bitmask_t *m, int w, int h); - -/* Convolve b into a, drawing the output into o, shifted by offset. If offset - * is 0, then the (x,y) bit will be set if and only if - * bitmask_overlap(a, b, x - b->w - 1, y - b->h - 1) returns true. - * - * Modifies bits o[xoffset ... xoffset + a->w + b->w - 1) - * [yoffset ... yoffset + a->h + b->h - 1). */ -void bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, int xoffset, int yoffset); - -#ifdef __cplusplus -} /* End of extern "C" { */ -#endif - -#endif diff --git a/venv/Include/site/python3.7/pygame/camera.h b/venv/Include/site/python3.7/pygame/camera.h deleted file mode 100644 index 46d2bebf1598d5b8f55a1c759507773f2f7ae7a5..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/camera.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - pygame - Python Game Library - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -#include "pygame.h" -#include "doc/camera_doc.h" - -#if defined(__unix__) - #include - #include - #include - #include - #include - - #include /* low-level i/o */ - #include - #include - #include - #include - #include - #include - #include - - /* on freebsd there is no asm/types */ - #ifdef linux - #include /* for videodev2.h */ - #endif - - #include -#elif defined(__APPLE__) - #include - /* We support OSX 10.6 and below. */ - #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060 - #define PYGAME_MAC_CAMERA_OLD 1 - #endif -#endif - -#if defined(PYGAME_MAC_CAMERA_OLD) - #include - #include - #include -#endif - -/* some constants used which are not defined on non-v4l machines. */ -#ifndef V4L2_PIX_FMT_RGB24 - #define V4L2_PIX_FMT_RGB24 'RGB3' -#endif -#ifndef V4L2_PIX_FMT_RGB444 - #define V4L2_PIX_FMT_RGB444 'R444' -#endif -#ifndef V4L2_PIX_FMT_YUYV - #define V4L2_PIX_FMT_YUYV 'YUYV' -#endif - -#define CLEAR(x) memset (&(x), 0, sizeof (x)) -#define SAT(c) if (c & (~255)) { if (c < 0) c = 0; else c = 255; } -#define SAT2(c) ((c) & (~255) ? ((c) < 0 ? 0 : 255) : (c)) -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 -#define RGB_OUT 1 -#define YUV_OUT 2 -#define HSV_OUT 4 -#define CAM_V4L 1 /* deprecated. the incomplete support in pygame was removed */ -#define CAM_V4L2 2 - -struct buffer { - void * start; - size_t length; -}; - -#if defined(__unix__) -typedef struct pgCameraObject { - PyObject_HEAD - char* device_name; - int camera_type; - unsigned long pixelformat; - unsigned int color_out; - struct buffer* buffers; - unsigned int n_buffers; - int width; - int height; - int size; - int hflip; - int vflip; - int brightness; - int fd; -} pgCameraObject; -#elif defined(PYGAME_MAC_CAMERA_OLD) -typedef struct pgCameraObject { - PyObject_HEAD - char* device_name; /* unieke name of the device */ - OSType pixelformat; - unsigned int color_out; - SeqGrabComponent component; /* A type used by the Sequence Grabber API */ - SGChannel channel; /* Channel of the Sequence Grabber */ - GWorldPtr gworld; /* Pointer to the struct that holds the data of the captured image */ - Rect boundsRect; /* bounds of the image frame */ - long size; /* size of the image in our buffer to draw */ - int hflip; - int vflip; - short depth; - struct buffer pixels; - //struct buffer tmp_pixels /* place where the flipped image in temporarly stored if hflip or vflip is true.*/ -} pgCameraObject; - -#else -/* generic definition. -*/ - -typedef struct pgCameraObject { - PyObject_HEAD - char* device_name; - int camera_type; - unsigned long pixelformat; - unsigned int color_out; - struct buffer* buffers; - unsigned int n_buffers; - int width; - int height; - int size; - int hflip; - int vflip; - int brightness; - int fd; -} pgCameraObject; -#endif - -/* internal functions for colorspace conversion */ -void colorspace (SDL_Surface *src, SDL_Surface *dst, int cspace); -void rgb24_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); -void rgb444_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); -void rgb_to_yuv (const void* src, void* dst, int length, - unsigned long source, SDL_PixelFormat* format); -void rgb_to_hsv (const void* src, void* dst, int length, - unsigned long source, SDL_PixelFormat* format); -void yuyv_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); -void yuyv_to_yuv (const void* src, void* dst, int length, SDL_PixelFormat* format); -void uyvy_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format); -void uyvy_to_yuv (const void* src, void* dst, int length, SDL_PixelFormat* format); -void sbggr8_to_rgb (const void* src, void* dst, int width, int height, - SDL_PixelFormat* format); -void yuv420_to_rgb (const void* src, void* dst, int width, int height, - SDL_PixelFormat* format); -void yuv420_to_yuv (const void* src, void* dst, int width, int height, - SDL_PixelFormat* format); - -#if defined(__unix__) -/* internal functions specific to v4l2 */ -char** v4l2_list_cameras (int* num_devices); -int v4l2_get_control (int fd, int id, int *value); -int v4l2_set_control (int fd, int id, int value); -PyObject* v4l2_read_raw (pgCameraObject* self); -int v4l2_xioctl (int fd, int request, void *arg); -int v4l2_process_image (pgCameraObject* self, const void *image, - unsigned int buffer_size, SDL_Surface* surf); -int v4l2_query_buffer (pgCameraObject* self); -int v4l2_read_frame (pgCameraObject* self, SDL_Surface* surf); -int v4l2_stop_capturing (pgCameraObject* self); -int v4l2_start_capturing (pgCameraObject* self); -int v4l2_uninit_device (pgCameraObject* self); -int v4l2_init_mmap (pgCameraObject* self); -int v4l2_init_device (pgCameraObject* self); -int v4l2_close_device (pgCameraObject* self); -int v4l2_open_device (pgCameraObject* self); - -#elif defined(PYGAME_MAC_CAMERA_OLD) -/* internal functions specific to mac */ -char** mac_list_cameras(int* num_devices); -int mac_open_device (pgCameraObject* self); -int mac_init_device(pgCameraObject* self); -int mac_close_device (pgCameraObject* self); -int mac_start_capturing(pgCameraObject* self); -int mac_stop_capturing (pgCameraObject* self); - -int mac_get_control(pgCameraObject* self, int id, int* value); -int mac_set_control(pgCameraObject* self, int id, int value); - -PyObject* mac_read_raw(pgCameraObject *self); -int mac_read_frame(pgCameraObject* self, SDL_Surface* surf); -int mac_camera_idle(pgCameraObject* self); -int mac_copy_gworld_to_surface(pgCameraObject* self, SDL_Surface* surf); - -void flip_image(const void* image, void* flipped_image, int width, int height, - short depth, int hflip, int vflip); - -#endif diff --git a/venv/Include/site/python3.7/pygame/fastevents.h b/venv/Include/site/python3.7/pygame/fastevents.h deleted file mode 100644 index 04098c3ab6da4bd597308ffa19dd3ae875058f6f..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/fastevents.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _FASTEVENTS_H_ -#define _FASTEVENTS_H_ -/* - NET2 is a threaded, event based, network IO library for SDL. - Copyright (C) 2002 Bob Pendleton - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 - of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA - - If you do not wish to comply with the terms of the LGPL please - contact the author as other terms are available for a fee. - - Bob Pendleton - Bob@Pendleton.com -*/ - -#include "SDL.h" - -#ifdef __cplusplus -extern "C" { -#endif - - int FE_Init(void); // Initialize FE - void FE_Quit(void); // shutdown FE - - void FE_PumpEvents(void); // replacement for SDL_PumpEvents - int FE_PollEvent(SDL_Event *event); // replacement for SDL_PollEvent - int FE_WaitEvent(SDL_Event *event); // replacement for SDL_WaitEvent - int FE_PushEvent(SDL_Event *event); // replacement for SDL_PushEvent - - char *FE_GetError(void); // get the last error -#ifdef __cplusplus -} -#endif - -#endif diff --git a/venv/Include/site/python3.7/pygame/font.h b/venv/Include/site/python3.7/pygame/font.h deleted file mode 100644 index b861a297d2e9b55c2133d1f79e3c93cb047cf248..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/font.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -#include -#if defined(HAVE_SNPRINTF) /* also defined in SDL_ttf (SDL.h) */ -#undef HAVE_SNPRINTF /* remove GCC macro redefine warning */ -#endif -#include - - -/* test font initialization */ -#define FONT_INIT_CHECK() \ - if(!(*(int*)PyFONT_C_API[2])) \ - return RAISE(pgExc_SDLError, "font system not initialized") - - - -#define PYGAMEAPI_FONT_FIRSTSLOT 0 -#define PYGAMEAPI_FONT_NUMSLOTS 3 -typedef struct { - PyObject_HEAD - TTF_Font* font; - PyObject* weakreflist; -} PyFontObject; -#define PyFont_AsFont(x) (((PyFontObject*)x)->font) - -#ifndef PYGAMEAPI_FONT_INTERNAL -#define PyFont_Check(x) ((x)->ob_type == (PyTypeObject*)PyFONT_C_API[0]) -#define PyFont_Type (*(PyTypeObject*)PyFONT_C_API[0]) -#define PyFont_New (*(PyObject*(*)(TTF_Font*))PyFONT_C_API[1]) -/*slot 2 taken by FONT_INIT_CHECK*/ - -#define import_pygame_font() \ - _IMPORT_PYGAME_MODULE(font, FONT, PyFONT_C_API) - -static void* PyFONT_C_API[PYGAMEAPI_FONT_NUMSLOTS] = {NULL}; -#endif - diff --git a/venv/Include/site/python3.7/pygame/freetype.h b/venv/Include/site/python3.7/pygame/freetype.h deleted file mode 100644 index fda722695212a32ddee763189f82086d3c57aaa3..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/freetype.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2009 Vicent Marti - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -#ifndef _PYGAME_FREETYPE_H_ -#define _PYGAME_FREETYPE_H_ - -#define PGFT_PYGAME1_COMPAT -#define HAVE_PYGAME_SDL_VIDEO -#define HAVE_PYGAME_SDL_RWOPS - -#include "pygame.h" -#include "pgcompat.h" - -#if PY3 -# define IS_PYTHON_3 -#endif - -#include -#include FT_FREETYPE_H -#include FT_CACHE_H -#include FT_XFREE86_H -#include FT_TRIGONOMETRY_H - -/********************************************************** - * Global module constants - **********************************************************/ - -/* Render styles */ -#define FT_STYLE_NORMAL 0x00 -#define FT_STYLE_STRONG 0x01 -#define FT_STYLE_OBLIQUE 0x02 -#define FT_STYLE_UNDERLINE 0x04 -#define FT_STYLE_WIDE 0x08 -#define FT_STYLE_DEFAULT 0xFF - -/* Bounding box modes */ -#define FT_BBOX_EXACT FT_GLYPH_BBOX_SUBPIXELS -#define FT_BBOX_EXACT_GRIDFIT FT_GLYPH_BBOX_GRIDFIT -#define FT_BBOX_PIXEL FT_GLYPH_BBOX_TRUNCATE -#define FT_BBOX_PIXEL_GRIDFIT FT_GLYPH_BBOX_PIXELS - -/* Rendering flags */ -#define FT_RFLAG_NONE (0) -#define FT_RFLAG_ANTIALIAS (1 << 0) -#define FT_RFLAG_AUTOHINT (1 << 1) -#define FT_RFLAG_VERTICAL (1 << 2) -#define FT_RFLAG_HINTED (1 << 3) -#define FT_RFLAG_KERNING (1 << 4) -#define FT_RFLAG_TRANSFORM (1 << 5) -#define FT_RFLAG_PAD (1 << 6) -#define FT_RFLAG_ORIGIN (1 << 7) -#define FT_RFLAG_UCS4 (1 << 8) -#define FT_RFLAG_USE_BITMAP_STRIKES (1 << 9) -#define FT_RFLAG_DEFAULTS (FT_RFLAG_HINTED | \ - FT_RFLAG_USE_BITMAP_STRIKES | \ - FT_RFLAG_ANTIALIAS) - - -#define FT_RENDER_NEWBYTEARRAY 0x0 -#define FT_RENDER_NEWSURFACE 0x1 -#define FT_RENDER_EXISTINGSURFACE 0x2 - -/********************************************************** - * Global module types - **********************************************************/ - -typedef struct _scale_s { - FT_UInt x, y; -} Scale_t; -typedef FT_Angle Angle_t; - -struct fontinternals_; -struct freetypeinstance_; - -typedef struct { - FT_Long font_index; - FT_Open_Args open_args; -} pgFontId; - -typedef struct { - PyObject_HEAD - pgFontId id; - PyObject *path; - int is_scalable; - - Scale_t face_size; - FT_Int16 style; - FT_Int16 render_flags; - double strength; - double underline_adjustment; - FT_UInt resolution; - Angle_t rotation; - FT_Matrix transform; - FT_Byte fgcolor[4]; - - struct freetypeinstance_ *freetype; /* Personal reference */ - struct fontinternals_ *_internals; -} pgFontObject; - -#define pgFont_IS_ALIVE(o) \ - (((pgFontObject *)(o))->_internals != 0) - -/********************************************************** - * Module declaration - **********************************************************/ -#define PYGAMEAPI_FREETYPE_FIRSTSLOT 0 -#define PYGAMEAPI_FREETYPE_NUMSLOTS 2 - -#ifndef PYGAME_FREETYPE_INTERNAL - -#define pgFont_Check(x) ((x)->ob_type == (PyTypeObject*)PgFREETYPE_C_API[0]) -#define pgFont_Type (*(PyTypeObject*)PgFREETYPE_C_API[1]) -#define pgFont_New (*(PyObject*(*)(const char*, long))PgFREETYPE_C_API[1]) - -#define import_pygame_freetype() \ - _IMPORT_PYGAME_MODULE(freetype, FREETYPE, PgFREETYPE_C_API) - -static void *PgFREETYPE_C_API[PYGAMEAPI_FREETYPE_NUMSLOTS] = {0}; -#endif /* PYGAME_FREETYPE_INTERNAL */ - -#endif /* _PYGAME_FREETYPE_H_ */ diff --git a/venv/Include/site/python3.7/pygame/mask.h b/venv/Include/site/python3.7/pygame/mask.h deleted file mode 100644 index b151dd4a0342b261f80398a9b141f3dc36cc8048..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/mask.h +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include "bitmask.h" - -#define PYGAMEAPI_MASK_FIRSTSLOT 0 -#define PYGAMEAPI_MASK_NUMSLOTS 1 -#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API" - -typedef struct { - PyObject_HEAD - bitmask_t *mask; -} pgMaskObject; - -#define pgMask_AsBitmap(x) (((pgMaskObject*)x)->mask) - -#ifndef PYGAMEAPI_MASK_INTERNAL - -#define pgMask_Type (*(PyTypeObject*)PyMASK_C_API[0]) -#define pgMask_Check(x) ((x)->ob_type == &pgMask_Type) - -#define import_pygame_mask() \ - _IMPORT_PYGAME_MODULE(mask, MASK, PyMASK_C_API) - -static void* PyMASK_C_API[PYGAMEAPI_MASK_NUMSLOTS] = {NULL}; -#endif /* #ifndef PYGAMEAPI_MASK_INTERNAL */ - diff --git a/venv/Include/site/python3.7/pygame/mixer.h b/venv/Include/site/python3.7/pygame/mixer.h deleted file mode 100644 index 36d57f3454c69d9e3d9d4f2fc6a8e771dff76adc..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/mixer.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -#include -#include -#include - - -/* test mixer initializations */ -#define MIXER_INIT_CHECK() \ - if(!SDL_WasInit(SDL_INIT_AUDIO)) \ - return RAISE(pgExc_SDLError, "mixer not initialized") - - -#define PYGAMEAPI_MIXER_FIRSTSLOT 0 -#define PYGAMEAPI_MIXER_NUMSLOTS 7 -typedef struct { - PyObject_HEAD - Mix_Chunk *chunk; - Uint8 *mem; - PyObject *weakreflist; -} pgSoundObject; -typedef struct { - PyObject_HEAD - int chan; -} pgChannelObject; -#define pgSound_AsChunk(x) (((pgSoundObject*)x)->chunk) -#define pgChannel_AsInt(x) (((pgChannelObject*)x)->chan) - -#ifndef PYGAMEAPI_MIXER_INTERNAL -#define pgSound_Check(x) ((x)->ob_type == (PyTypeObject*)pgMIXER_C_API[0]) -#define pgSound_Type (*(PyTypeObject*)pgMIXER_C_API[0]) -#define pgSound_New (*(PyObject*(*)(Mix_Chunk*))pgMIXER_C_API[1]) -#define pgSound_Play (*(PyObject*(*)(PyObject*, PyObject*))pgMIXER_C_API[2]) -#define pgChannel_Check(x) ((x)->ob_type == (PyTypeObject*)pgMIXER_C_API[3]) -#define pgChannel_Type (*(PyTypeObject*)pgMIXER_C_API[3]) -#define pgChannel_New (*(PyObject*(*)(int))pgMIXER_C_API[4]) -#define pgMixer_AutoInit (*(PyObject*(*)(PyObject*, PyObject*))pgMIXER_C_API[5]) -#define pgMixer_AutoQuit (*(void(*)(void))pgMIXER_C_API[6]) - -#define import_pygame_mixer() \ - _IMPORT_PYGAME_MODULE(mixer, MIXER, pgMIXER_C_API) - -static void* pgMIXER_C_API[PYGAMEAPI_MIXER_NUMSLOTS] = {NULL}; -#endif - diff --git a/venv/Include/site/python3.7/pygame/palette.h b/venv/Include/site/python3.7/pygame/palette.h deleted file mode 100644 index 1ae4cf6dcea06d8c8d56e41ea0cc70aa9e274709..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/palette.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -#ifndef PALETTE_H -#define PALETTE_H - -#include - -/* SDL 2 does not assign a default palette color scheme to a new 8 bit - * surface. Instead, the palette is set all white. This defines the SDL 1.2 - * default palette. - */ -static const SDL_Color default_palette_colors[] = { - {0, 0, 0, 255}, {0, 0, 85, 255}, {0, 0, 170, 255}, - {0, 0, 255, 255}, {0, 36, 0, 255}, {0, 36, 85, 255}, - {0, 36, 170, 255}, {0, 36, 255, 255}, {0, 73, 0, 255}, - {0, 73, 85, 255}, {0, 73, 170, 255}, {0, 73, 255, 255}, - {0, 109, 0, 255}, {0, 109, 85, 255}, {0, 109, 170, 255}, - {0, 109, 255, 255}, {0, 146, 0, 255}, {0, 146, 85, 255}, - {0, 146, 170, 255}, {0, 146, 255, 255}, {0, 182, 0, 255}, - {0, 182, 85, 255}, {0, 182, 170, 255}, {0, 182, 255, 255}, - {0, 219, 0, 255}, {0, 219, 85, 255}, {0, 219, 170, 255}, - {0, 219, 255, 255}, {0, 255, 0, 255}, {0, 255, 85, 255}, - {0, 255, 170, 255}, {0, 255, 255, 255}, {85, 0, 0, 255}, - {85, 0, 85, 255}, {85, 0, 170, 255}, {85, 0, 255, 255}, - {85, 36, 0, 255}, {85, 36, 85, 255}, {85, 36, 170, 255}, - {85, 36, 255, 255}, {85, 73, 0, 255}, {85, 73, 85, 255}, - {85, 73, 170, 255}, {85, 73, 255, 255}, {85, 109, 0, 255}, - {85, 109, 85, 255}, {85, 109, 170, 255}, {85, 109, 255, 255}, - {85, 146, 0, 255}, {85, 146, 85, 255}, {85, 146, 170, 255}, - {85, 146, 255, 255}, {85, 182, 0, 255}, {85, 182, 85, 255}, - {85, 182, 170, 255}, {85, 182, 255, 255}, {85, 219, 0, 255}, - {85, 219, 85, 255}, {85, 219, 170, 255}, {85, 219, 255, 255}, - {85, 255, 0, 255}, {85, 255, 85, 255}, {85, 255, 170, 255}, - {85, 255, 255, 255}, {170, 0, 0, 255}, {170, 0, 85, 255}, - {170, 0, 170, 255}, {170, 0, 255, 255}, {170, 36, 0, 255}, - {170, 36, 85, 255}, {170, 36, 170, 255}, {170, 36, 255, 255}, - {170, 73, 0, 255}, {170, 73, 85, 255}, {170, 73, 170, 255}, - {170, 73, 255, 255}, {170, 109, 0, 255}, {170, 109, 85, 255}, - {170, 109, 170, 255}, {170, 109, 255, 255}, {170, 146, 0, 255}, - {170, 146, 85, 255}, {170, 146, 170, 255}, {170, 146, 255, 255}, - {170, 182, 0, 255}, {170, 182, 85, 255}, {170, 182, 170, 255}, - {170, 182, 255, 255}, {170, 219, 0, 255}, {170, 219, 85, 255}, - {170, 219, 170, 255}, {170, 219, 255, 255}, {170, 255, 0, 255}, - {170, 255, 85, 255}, {170, 255, 170, 255}, {170, 255, 255, 255}, - {255, 0, 0, 255}, {255, 0, 85, 255}, {255, 0, 170, 255}, - {255, 0, 255, 255}, {255, 36, 0, 255}, {255, 36, 85, 255}, - {255, 36, 170, 255}, {255, 36, 255, 255}, {255, 73, 0, 255}, - {255, 73, 85, 255}, {255, 73, 170, 255}, {255, 73, 255, 255}, - {255, 109, 0, 255}, {255, 109, 85, 255}, {255, 109, 170, 255}, - {255, 109, 255, 255}, {255, 146, 0, 255}, {255, 146, 85, 255}, - {255, 146, 170, 255}, {255, 146, 255, 255}, {255, 182, 0, 255}, - {255, 182, 85, 255}, {255, 182, 170, 255}, {255, 182, 255, 255}, - {255, 219, 0, 255}, {255, 219, 85, 255}, {255, 219, 170, 255}, - {255, 219, 255, 255}, {255, 255, 0, 255}, {255, 255, 85, 255}, - {255, 255, 170, 255}, {255, 255, 255, 255}, {0, 0, 0, 255}, - {0, 0, 85, 255}, {0, 0, 170, 255}, {0, 0, 255, 255}, - {0, 36, 0, 255}, {0, 36, 85, 255}, {0, 36, 170, 255}, - {0, 36, 255, 255}, {0, 73, 0, 255}, {0, 73, 85, 255}, - {0, 73, 170, 255}, {0, 73, 255, 255}, {0, 109, 0, 255}, - {0, 109, 85, 255}, {0, 109, 170, 255}, {0, 109, 255, 255}, - {0, 146, 0, 255}, {0, 146, 85, 255}, {0, 146, 170, 255}, - {0, 146, 255, 255}, {0, 182, 0, 255}, {0, 182, 85, 255}, - {0, 182, 170, 255}, {0, 182, 255, 255}, {0, 219, 0, 255}, - {0, 219, 85, 255}, {0, 219, 170, 255}, {0, 219, 255, 255}, - {0, 255, 0, 255}, {0, 255, 85, 255}, {0, 255, 170, 255}, - {0, 255, 255, 255}, {85, 0, 0, 255}, {85, 0, 85, 255}, - {85, 0, 170, 255}, {85, 0, 255, 255}, {85, 36, 0, 255}, - {85, 36, 85, 255}, {85, 36, 170, 255}, {85, 36, 255, 255}, - {85, 73, 0, 255}, {85, 73, 85, 255}, {85, 73, 170, 255}, - {85, 73, 255, 255}, {85, 109, 0, 255}, {85, 109, 85, 255}, - {85, 109, 170, 255}, {85, 109, 255, 255}, {85, 146, 0, 255}, - {85, 146, 85, 255}, {85, 146, 170, 255}, {85, 146, 255, 255}, - {85, 182, 0, 255}, {85, 182, 85, 255}, {85, 182, 170, 255}, - {85, 182, 255, 255}, {85, 219, 0, 255}, {85, 219, 85, 255}, - {85, 219, 170, 255}, {85, 219, 255, 255}, {85, 255, 0, 255}, - {85, 255, 85, 255}, {85, 255, 170, 255}, {85, 255, 255, 255}, - {170, 0, 0, 255}, {170, 0, 85, 255}, {170, 0, 170, 255}, - {170, 0, 255, 255}, {170, 36, 0, 255}, {170, 36, 85, 255}, - {170, 36, 170, 255}, {170, 36, 255, 255}, {170, 73, 0, 255}, - {170, 73, 85, 255}, {170, 73, 170, 255}, {170, 73, 255, 255}, - {170, 109, 0, 255}, {170, 109, 85, 255}, {170, 109, 170, 255}, - {170, 109, 255, 255}, {170, 146, 0, 255}, {170, 146, 85, 255}, - {170, 146, 170, 255}, {170, 146, 255, 255}, {170, 182, 0, 255}, - {170, 182, 85, 255}, {170, 182, 170, 255}, {170, 182, 255, 255}, - {170, 219, 0, 255}, {170, 219, 85, 255}, {170, 219, 170, 255}, - {170, 219, 255, 255}, {170, 255, 0, 255}, {170, 255, 85, 255}, - {170, 255, 170, 255}, {170, 255, 255, 255}, {255, 0, 0, 255}, - {255, 0, 85, 255}, {255, 0, 170, 255}, {255, 0, 255, 255}, - {255, 36, 0, 255}, {255, 36, 85, 255}, {255, 36, 170, 255}, - {255, 36, 255, 255}, {255, 73, 0, 255}, {255, 73, 85, 255}, - {255, 73, 170, 255}, {255, 73, 255, 255}, {255, 109, 0, 255}, - {255, 109, 85, 255}, {255, 109, 170, 255}, {255, 109, 255, 255}, - {255, 146, 0, 255}, {255, 146, 85, 255}, {255, 146, 170, 255}, - {255, 146, 255, 255}, {255, 182, 0, 255}, {255, 182, 85, 255}, - {255, 182, 170, 255}, {255, 182, 255, 255}, {255, 219, 0, 255}, - {255, 219, 85, 255}, {255, 219, 170, 255}, {255, 219, 255, 255}, - {255, 255, 0, 255}, {255, 255, 85, 255}, {255, 255, 170, 255}, - {255, 255, 255, 255}}; - -static const int default_palette_size = - (int)(sizeof(default_palette_colors) / sizeof(SDL_Color)); - -#endif diff --git a/venv/Include/site/python3.7/pygame/pgarrinter.h b/venv/Include/site/python3.7/pygame/pgarrinter.h deleted file mode 100644 index 5ba096be22816f20a33b0bd37ec70d0cef9dc922..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/pgarrinter.h +++ /dev/null @@ -1,26 +0,0 @@ -/* array structure interface version 3 declarations */ - -#if !defined(PG_ARRAYINTER_HEADER) -#define PG_ARRAYINTER_HEADER - -static const int PAI_CONTIGUOUS = 0x01; -static const int PAI_FORTRAN = 0x02; -static const int PAI_ALIGNED = 0x100; -static const int PAI_NOTSWAPPED = 0x200; -static const int PAI_WRITEABLE = 0x400; -static const int PAI_ARR_HAS_DESCR = 0x800; - -typedef struct { - int two; /* contains the integer 2 -- simple sanity check */ - int nd; /* number of dimensions */ - char typekind; /* kind in array -- character code of typestr */ - int itemsize; /* size of each element */ - int flags; /* flags indicating how the data should be */ - /* interpreted */ - Py_intptr_t *shape; /* A length-nd array of shape information */ - Py_intptr_t *strides; /* A length-nd array of stride information */ - void *data; /* A pointer to the first element of the array */ - PyObject *descr; /* NULL or a data-description */ -} PyArrayInterface; - -#endif diff --git a/venv/Include/site/python3.7/pygame/pgbufferproxy.h b/venv/Include/site/python3.7/pygame/pgbufferproxy.h deleted file mode 100644 index 92dc2f0cb9da38d89b525a6d65a01804106a1eaa..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/pgbufferproxy.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - Copyright (C) 2007 Rene Dudfield, Richard Goedeken - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -/* Bufferproxy module C api. - Depends on pygame.h being included first. - */ -#if !defined(PG_BUFPROXY_HEADER) - -#define PYGAMEAPI_BUFPROXY_NUMSLOTS 4 -#define PYGAMEAPI_BUFPROXY_FIRSTSLOT 0 - -#if !(defined(PYGAMEAPI_BUFPROXY_INTERNAL) || defined(NO_PYGAME_C_API)) -static void *PgBUFPROXY_C_API[PYGAMEAPI_BUFPROXY_NUMSLOTS]; - -typedef PyObject *(*_pgbufproxy_new_t)(PyObject *, getbufferproc); -typedef PyObject *(*_pgbufproxy_get_obj_t)(PyObject *); -typedef int (*_pgbufproxy_trip_t)(PyObject *); - -#define pgBufproxy_Type (*(PyTypeObject*)PgBUFPROXY_C_API[0]) -#define pgBufproxy_New (*(_pgbufproxy_new_t)PgBUFPROXY_C_API[1]) -#define pgBufproxy_GetParent \ - (*(_pgbufproxy_get_obj_t)PgBUFPROXY_C_API[2]) -#define pgBufproxy_Trip (*(_pgbufproxy_trip_t)PgBUFPROXY_C_API[3]) -#define pgBufproxy_Check(x) ((x)->ob_type == (pgBufproxy_Type)) -#define import_pygame_bufferproxy() \ - _IMPORT_PYGAME_MODULE(bufferproxy, BUFPROXY, PgBUFPROXY_C_API) - -#endif /* #if !(defined(PYGAMEAPI_BUFPROXY_INTERNAL) || ... */ - -#define PG_BUFPROXY_HEADER - -#endif /* #if !defined(PG_BUFPROXY_HEADER) */ diff --git a/venv/Include/site/python3.7/pygame/pgcompat.h b/venv/Include/site/python3.7/pygame/pgcompat.h deleted file mode 100644 index 9eb1b886dbf1ded859a2fc991e9efc01fd5dd644..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/pgcompat.h +++ /dev/null @@ -1,195 +0,0 @@ -/* Python 2.x/3.x compitibility tools - */ - -#if !defined(PGCOMPAT_H) -#define PGCOMPAT_H - -#if PY_MAJOR_VERSION >= 3 - -#define PY3 1 - -/* Define some aliases for the removed PyInt_* functions */ -#define PyInt_Check(op) PyLong_Check(op) -#define PyInt_FromString PyLong_FromString -#define PyInt_FromUnicode PyLong_FromUnicode -#define PyInt_FromLong PyLong_FromLong -#define PyInt_FromSize_t PyLong_FromSize_t -#define PyInt_FromSsize_t PyLong_FromSsize_t -#define PyInt_AsLong PyLong_AsLong -#define PyInt_AsSsize_t PyLong_AsSsize_t -#define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask -#define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask -#define PyInt_AS_LONG PyLong_AS_LONG -#define PyNumber_Int PyNumber_Long - -/* Weakrefs flags changed in 3.x */ -#define Py_TPFLAGS_HAVE_WEAKREFS 0 - -/* Module init function returns new module instance. */ -#define MODINIT_RETURN(x) return x -#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC PyInit_##mod_name (void) -#define DECREF_MOD(mod) Py_DECREF (mod) - -/* Type header differs. */ -#define TYPE_HEAD(x,y) PyVarObject_HEAD_INIT(x,y) - -/* Text interface. Use unicode strings. */ -#define Text_Type PyUnicode_Type -#define Text_Check PyUnicode_Check - -#ifndef PYPY_VERSION -#define Text_FromLocale(s) PyUnicode_DecodeLocale((s), "strict") -#else /* PYPY_VERSION */ -/* workaround: missing function for pypy */ -#define Text_FromLocale PyUnicode_FromString -#endif /* PYPY_VERSION */ - -#define Text_FromUTF8 PyUnicode_FromString -#define Text_FromUTF8AndSize PyUnicode_FromStringAndSize -#define Text_FromFormat PyUnicode_FromFormat -#define Text_GetSize PyUnicode_GetSize -#define Text_GET_SIZE PyUnicode_GET_SIZE - -/* Binary interface. Use bytes. */ -#define Bytes_Type PyBytes_Type -#define Bytes_Check PyBytes_Check -#define Bytes_Size PyBytes_Size -#define Bytes_AsString PyBytes_AsString -#define Bytes_AsStringAndSize PyBytes_AsStringAndSize -#define Bytes_FromStringAndSize PyBytes_FromStringAndSize -#define Bytes_FromFormat PyBytes_FromFormat -#define Bytes_AS_STRING PyBytes_AS_STRING -#define Bytes_GET_SIZE PyBytes_GET_SIZE -#define Bytes_AsDecodeObject PyBytes_AsDecodedObject - -#define Object_Unicode PyObject_Str - -#define IsTextObj(x) (PyUnicode_Check(x) || PyBytes_Check(x)) - -/* Renamed builtins */ -#define BUILTINS_MODULE "builtins" -#define BUILTINS_UNICODE "str" -#define BUILTINS_UNICHR "chr" - -/* Defaults for unicode file path encoding */ -#define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding -#if defined(MS_WIN32) -#define UNICODE_DEF_FS_ERROR "replace" -#else -#define UNICODE_DEF_FS_ERROR "surrogateescape" -#endif - -#else /* #if PY_MAJOR_VERSION >= 3 */ - -#define PY3 0 - -/* Module init function returns nothing. */ -#define MODINIT_RETURN(x) return -#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC init##mod_name (void) -#define DECREF_MOD(mod) - -/* Type header differs. */ -#define TYPE_HEAD(x,y) \ - PyObject_HEAD_INIT(x) \ - 0, - -/* Text interface. Use ascii strings. */ -#define Text_Type PyString_Type -#define Text_Check PyString_Check -#define Text_FromLocale PyString_FromString -#define Text_FromUTF8 PyString_FromString -#define Text_FromUTF8AndSize PyString_FromStringAndSize -#define Text_FromFormat PyString_FromFormat -#define Text_GetSize PyString_GetSize -#define Text_GET_SIZE PyString_GET_SIZE - -/* Binary interface. Use ascii strings. */ -#define Bytes_Type PyString_Type -#define Bytes_Check PyString_Check -#define Bytes_Size PyString_Size -#define Bytes_AsString PyString_AsString -#define Bytes_AsStringAndSize PyString_AsStringAndSize -#define Bytes_FromStringAndSize PyString_FromStringAndSize -#define Bytes_FromFormat PyString_FromFormat -#define Bytes_AS_STRING PyString_AS_STRING -#define Bytes_GET_SIZE PyString_GET_SIZE -#define Bytes_AsDecodedObject PyString_AsDecodedObject - -#define Object_Unicode PyObject_Unicode - -/* Renamed builtins */ -#define BUILTINS_MODULE "__builtin__" -#define BUILTINS_UNICODE "unicode" -#define BUILTINS_UNICHR "unichr" - -/* Defaults for unicode file path encoding */ -#define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding -#define UNICODE_DEF_FS_ERROR "strict" - -#endif /* #if PY_MAJOR_VERSION >= 3 */ - -#define PY2 (!PY3) - -#define MODINIT_ERROR MODINIT_RETURN (NULL) - -/* Module state. These macros are used to define per-module macros. - * v - global state variable (Python 2.x) - * s - global state structure (Python 3.x) - */ -#define PY2_GETSTATE(v) (&(v)) -#define PY3_GETSTATE(s, m) ((struct s *) PyModule_GetState (m)) - -/* Pep 3123: Making PyObject_HEAD conform to standard C */ -#if !defined(Py_TYPE) -#define Py_TYPE(o) (((PyObject *)(o))->ob_type) -#define Py_REFCNT(o) (((PyObject *)(o))->ob_refcnt) -#define Py_SIZE(o) (((PyVarObject *)(o))->ob_size) -#endif - -/* Encode a unicode file path */ -#define Unicode_AsEncodedPath(u) \ - PyUnicode_AsEncodedString ((u), UNICODE_DEF_FS_CODEC, UNICODE_DEF_FS_ERROR) - -#define RELATIVE_MODULE(m) ("." m) - -#define HAVE_OLD_BUFPROTO PY2 - -#if !defined(PG_ENABLE_OLDBUF) /* allow for command line override */ -#if HAVE_OLD_BUFPROTO -#define PG_ENABLE_OLDBUF 1 -#else -#define PG_ENABLE_OLDBUF 0 -#endif -#endif - -#ifndef Py_TPFLAGS_HAVE_NEWBUFFER -#define Py_TPFLAGS_HAVE_NEWBUFFER 0 -#endif - -#ifndef Py_TPFLAGS_HAVE_CLASS -#define Py_TPFLAGS_HAVE_CLASS 0 -#endif - -#ifndef Py_TPFLAGS_CHECKTYPES -#define Py_TPFLAGS_CHECKTYPES 0 -#endif - -#if PY_VERSION_HEX >= 0x03020000 -#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \ - PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength) -#else -#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \ - PySlice_GetIndicesEx((PySliceObject *)(slice), length, \ - start, stop, step, slicelength) -#endif - -/* Support new buffer protocol? */ -#if !defined(PG_ENABLE_NEWBUF) /* allow for command line override */ -#if !defined(PYPY_VERSION) -#define PG_ENABLE_NEWBUF 1 -#else -#define PG_ENABLE_NEWBUF 0 -#endif -#endif - -#endif /* #if !defined(PGCOMPAT_H) */ diff --git a/venv/Include/site/python3.7/pygame/pgopengl.h b/venv/Include/site/python3.7/pygame/pgopengl.h deleted file mode 100644 index 3c80dca5499f380eadb06c660832ce8dce61668b..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/pgopengl.h +++ /dev/null @@ -1,16 +0,0 @@ -#if !defined(PGOPENGL_H) -#define PGOPENGL_H - -/** This header includes definitions of Opengl functions as pointer types for - ** use with the SDL function SDL_GL_GetProcAddress. - **/ - -#if defined(_WIN32) -#define GL_APIENTRY __stdcall -#else -#define GL_APIENTRY -#endif - -typedef void (GL_APIENTRY *GL_glReadPixels_Func)(int, int, int, int, unsigned int, unsigned int, void*); - -#endif diff --git a/venv/Include/site/python3.7/pygame/pygame.h b/venv/Include/site/python3.7/pygame/pygame.h deleted file mode 100644 index bcbf1d9998de7304f73682b8e2c9243272f95223..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/pygame.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -/* To allow the Pygame C api to be globally shared by all code within an - * extension module built from multiple C files, only include the pygame.h - * header within the top level C file, the one which calls the - * 'import_pygame_*' macros. All other C source files of the module should - * include _pygame.h instead. - */ -#ifndef PYGAME_H -#define PYGAME_H - -#include "_pygame.h" - -#endif diff --git a/venv/Include/site/python3.7/pygame/scrap.h b/venv/Include/site/python3.7/pygame/scrap.h deleted file mode 100644 index b1b3856523fc77578be8acec61e261143509fb20..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/scrap.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen - - Originally put in the public domain by Sam Lantinga. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* This is unconditionally defined in Python.h */ -#if defined(_POSIX_C_SOURCE) -#undef _POSIX_C_SOURCE -#endif - -#include - -/* Handle clipboard text and data in arbitrary formats */ - -/** - * Predefined supported pygame scrap types. - */ -#define PYGAME_SCRAP_TEXT "text/plain" -#define PYGAME_SCRAP_BMP "image/bmp" -#define PYGAME_SCRAP_PPM "image/ppm" -#define PYGAME_SCRAP_PBM "image/pbm" - -/** - * The supported scrap clipboard types. - * - * This is only relevant in a X11 environment, which supports mouse - * selections as well. For Win32 and MacOS environments the default - * clipboard is used, no matter what value is passed. - */ -typedef enum -{ - SCRAP_CLIPBOARD, - SCRAP_SELECTION /* only supported in X11 environments. */ -} ScrapClipType; - -/** - * Macro for initialization checks. - */ -#define PYGAME_SCRAP_INIT_CHECK() \ - if(!pygame_scrap_initialized()) \ - return (PyErr_SetString (pgExc_SDLError, \ - "scrap system not initialized."), NULL) - -/** - * \brief Checks, whether the pygame scrap module was initialized. - * - * \return 1 if the modules was initialized, 0 otherwise. - */ -extern int -pygame_scrap_initialized (void); - -/** - * \brief Initializes the pygame scrap module internals. Call this before any - * other method. - * - * \return 1 on successful initialization, 0 otherwise. - */ -extern int -pygame_scrap_init (void); - -/** - * \brief Checks, whether the pygame window lost the clipboard focus or not. - * - * \return 1 if the window lost the focus, 0 otherwise. - */ -extern int -pygame_scrap_lost (void); - -/** - * \brief Places content of a specific type into the clipboard. - * - * \note For X11 the following notes are important: The following types - * are reserved for internal usage and thus will throw an error on - * setting them: "TIMESTAMP", "TARGETS", "SDL_SELECTION". - * Setting PYGAME_SCRAP_TEXT ("text/plain") will also automatically - * set the X11 types "STRING" (XA_STRING), "TEXT" and "UTF8_STRING". - * - * For Win32 the following notes are important: Setting - * PYGAME_SCRAP_TEXT ("text/plain") will also automatically set - * the Win32 type "TEXT" (CF_TEXT). - * - * For QNX the following notes are important: Setting - * PYGAME_SCRAP_TEXT ("text/plain") will also automatically set - * the QNX type "TEXT" (Ph_CL_TEXT). - * - * \param type The type of the content. - * \param srclen The length of the content. - * \param src The NULL terminated content. - * \return 1, if the content could be successfully pasted into the clipboard, - * 0 otherwise. - */ -extern int -pygame_scrap_put (char *type, int srclen, char *src); - -/** - * \brief Gets the current content from the clipboard. - * - * \note The received content does not need to be the content previously - * placed in the clipboard using pygame_put_scrap(). See the - * pygame_put_scrap() notes for more details. - * - * \param type The type of the content to receive. - * \param count The size of the returned content. - * \return The content or NULL in case of an error or if no content of the - * specified type was available. - */ -extern char* -pygame_scrap_get (char *type, unsigned long *count); - -/** - * \brief Gets the currently available content types from the clipboard. - * - * \return The different available content types or NULL in case of an - * error or if no content type is available. - */ -extern char** -pygame_scrap_get_types (void); - -/** - * \brief Checks whether content for the specified scrap type is currently - * available in the clipboard. - * - * \param type The type to check for. - * \return 1, if there is content and 0 otherwise. - */ -extern int -pygame_scrap_contains (char *type); diff --git a/venv/Include/site/python3.7/pygame/surface.h b/venv/Include/site/python3.7/pygame/surface.h deleted file mode 100644 index cc5f07146efea07d388ca6aed95419ec58e362c2..0000000000000000000000000000000000000000 --- a/venv/Include/site/python3.7/pygame/surface.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - pygame - Python Game Library - Copyright (C) 2000-2001 Pete Shinners - Copyright (C) 2007 Marcus von Appen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Pete Shinners - pete@shinners.org -*/ - -#ifndef SURFACE_H -#define SURFACE_H - -/* This is defined in SDL.h */ -#if defined(_POSIX_C_SOURCE) -#undef _POSIX_C_SOURCE -#endif - -#include -#include "pygame.h" - -/* Blend modes */ -#define PYGAME_BLEND_ADD 0x1 -#define PYGAME_BLEND_SUB 0x2 -#define PYGAME_BLEND_MULT 0x3 -#define PYGAME_BLEND_MIN 0x4 -#define PYGAME_BLEND_MAX 0x5 - -#define PYGAME_BLEND_RGB_ADD 0x1 -#define PYGAME_BLEND_RGB_SUB 0x2 -#define PYGAME_BLEND_RGB_MULT 0x3 -#define PYGAME_BLEND_RGB_MIN 0x4 -#define PYGAME_BLEND_RGB_MAX 0x5 - -#define PYGAME_BLEND_RGBA_ADD 0x6 -#define PYGAME_BLEND_RGBA_SUB 0x7 -#define PYGAME_BLEND_RGBA_MULT 0x8 -#define PYGAME_BLEND_RGBA_MIN 0x9 -#define PYGAME_BLEND_RGBA_MAX 0x10 -#define PYGAME_BLEND_PREMULTIPLIED 0x11 - - - - - -#if SDL_BYTEORDER == SDL_LIL_ENDIAN -#define GET_PIXEL_24(b) (b[0] + (b[1] << 8) + (b[2] << 16)) -#else -#define GET_PIXEL_24(b) (b[2] + (b[1] << 8) + (b[0] << 16)) -#endif - -#define GET_PIXEL(pxl, bpp, source) \ - switch (bpp) \ - { \ - case 2: \ - pxl = *((Uint16 *) (source)); \ - break; \ - case 4: \ - pxl = *((Uint32 *) (source)); \ - break; \ - default: \ - { \ - Uint8 *b = (Uint8 *) source; \ - pxl = GET_PIXEL_24(b); \ - } \ - break; \ - } - -#if IS_SDLv1 -#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \ - _sR = ((px & fmt->Rmask) >> fmt->Rshift); \ - _sR = (_sR << fmt->Rloss) + (_sR >> (8 - (fmt->Rloss << 1))); \ - _sG = ((px & fmt->Gmask) >> fmt->Gshift); \ - _sG = (_sG << fmt->Gloss) + (_sG >> (8 - (fmt->Gloss << 1))); \ - _sB = ((px & fmt->Bmask) >> fmt->Bshift); \ - _sB = (_sB << fmt->Bloss) + (_sB >> (8 - (fmt->Bloss << 1))); \ - if (ppa) \ - { \ - _sA = ((px & fmt->Amask) >> fmt->Ashift); \ - _sA = (_sA << fmt->Aloss) + (_sA >> (8 - (fmt->Aloss << 1))); \ - } \ - else \ - { \ - _sA = 255; \ - } - -#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \ - sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \ - sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \ - sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \ - sa = 255; - -/* For 1 byte palette pixels */ -#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \ - *(px) = (Uint8) SDL_MapRGB(fmt, _dR, _dG, _dB) -#else /* IS_SDLv2 */ -#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \ - SDL_GetRGBA(px, fmt, &(_sR), &(_sG), &(_sB), &(_sA)); \ - if (!ppa) { \ - _sA = 255; \ - } - -#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \ - sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \ - sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \ - sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \ - sa = 255; - -/* For 1 byte palette pixels */ -#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \ - *(px) = (Uint8) SDL_MapRGBA(fmt, _dR, _dG, _dB, _dA) -#endif /* IS_SDLv2 */ - - - - - - - - -#if SDL_BYTEORDER == SDL_LIL_ENDIAN -#define SET_OFFSETS_24(or, og, ob, fmt) \ - { \ - or = (fmt->Rshift == 0 ? 0 : \ - fmt->Rshift == 8 ? 1 : \ - 2 ); \ - og = (fmt->Gshift == 0 ? 0 : \ - fmt->Gshift == 8 ? 1 : \ - 2 ); \ - ob = (fmt->Bshift == 0 ? 0 : \ - fmt->Bshift == 8 ? 1 : \ - 2 ); \ - } - -#define SET_OFFSETS_32(or, og, ob, fmt) \ - { \ - or = (fmt->Rshift == 0 ? 0 : \ - fmt->Rshift == 8 ? 1 : \ - fmt->Rshift == 16 ? 2 : \ - 3 ); \ - og = (fmt->Gshift == 0 ? 0 : \ - fmt->Gshift == 8 ? 1 : \ - fmt->Gshift == 16 ? 2 : \ - 3 ); \ - ob = (fmt->Bshift == 0 ? 0 : \ - fmt->Bshift == 8 ? 1 : \ - fmt->Bshift == 16 ? 2 : \ - 3 ); \ - } -#else -#define SET_OFFSETS_24(or, og, ob, fmt) \ - { \ - or = (fmt->Rshift == 0 ? 2 : \ - fmt->Rshift == 8 ? 1 : \ - 0 ); \ - og = (fmt->Gshift == 0 ? 2 : \ - fmt->Gshift == 8 ? 1 : \ - 0 ); \ - ob = (fmt->Bshift == 0 ? 2 : \ - fmt->Bshift == 8 ? 1 : \ - 0 ); \ - } - -#define SET_OFFSETS_32(or, og, ob, fmt) \ - { \ - or = (fmt->Rshift == 0 ? 3 : \ - fmt->Rshift == 8 ? 2 : \ - fmt->Rshift == 16 ? 1 : \ - 0 ); \ - og = (fmt->Gshift == 0 ? 3 : \ - fmt->Gshift == 8 ? 2 : \ - fmt->Gshift == 16 ? 1 : \ - 0 ); \ - ob = (fmt->Bshift == 0 ? 3 : \ - fmt->Bshift == 8 ? 2 : \ - fmt->Bshift == 16 ? 1 : \ - 0 ); \ - } -#endif - - -#define CREATE_PIXEL(buf, r, g, b, a, bp, ft) \ - switch (bp) \ - { \ - case 2: \ - *((Uint16 *) (buf)) = \ - ((r >> ft->Rloss) << ft->Rshift) | \ - ((g >> ft->Gloss) << ft->Gshift) | \ - ((b >> ft->Bloss) << ft->Bshift) | \ - ((a >> ft->Aloss) << ft->Ashift); \ - break; \ - case 4: \ - *((Uint32 *) (buf)) = \ - ((r >> ft->Rloss) << ft->Rshift) | \ - ((g >> ft->Gloss) << ft->Gshift) | \ - ((b >> ft->Bloss) << ft->Bshift) | \ - ((a >> ft->Aloss) << ft->Ashift); \ - break; \ - } - -/* Pretty good idea from Tom Duff :-). */ -#define LOOP_UNROLLED4(code, n, width) \ - n = (width + 3) / 4; \ - switch (width & 3) \ - { \ - case 0: do { code; \ - case 3: code; \ - case 2: code; \ - case 1: code; \ - } while (--n > 0); \ - } - -/* Used in the srcbpp == dstbpp == 1 blend functions */ -#define REPEAT_3(code) \ - code; \ - code; \ - code; - -#define REPEAT_4(code) \ - code; \ - code; \ - code; \ - code; - - -#define BLEND_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ - tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255); \ - tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255); \ - tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255); - -#define BLEND_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ - tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0); \ - tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0); \ - tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0); - -#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ - dR = (dR && sR) ? (dR * sR) >> 8 : 0; \ - dG = (dG && sG) ? (dG * sG) >> 8 : 0; \ - dB = (dB && sB) ? (dB * sB) >> 8 : 0; - -#define BLEND_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \ - if(sR < dR) { dR = sR; } \ - if(sG < dG) { dG = sG; } \ - if(sB < dB) { dB = sB; } - -#define BLEND_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \ - if(sR > dR) { dR = sR; } \ - if(sG > dG) { dG = sG; } \ - if(sB > dB) { dB = sB; } - - - - - - -#define BLEND_RGBA_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ - tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255); \ - tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255); \ - tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255); \ - tmp = dA + sA; dA = (tmp <= 255 ? tmp : 255); - -#define BLEND_RGBA_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ - tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0); \ - tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0); \ - tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0); \ - tmp = dA - sA; dA = (tmp >= 0 ? tmp : 0); - -#define BLEND_RGBA_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ - dR = (dR && sR) ? (dR * sR) >> 8 : 0; \ - dG = (dG && sG) ? (dG * sG) >> 8 : 0; \ - dB = (dB && sB) ? (dB * sB) >> 8 : 0; \ - dA = (dA && sA) ? (dA * sA) >> 8 : 0; - -#define BLEND_RGBA_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \ - if(sR < dR) { dR = sR; } \ - if(sG < dG) { dG = sG; } \ - if(sB < dB) { dB = sB; } \ - if(sA < dA) { dA = sA; } - -#define BLEND_RGBA_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \ - if(sR > dR) { dR = sR; } \ - if(sG > dG) { dG = sG; } \ - if(sB > dB) { dB = sB; } \ - if(sA > dA) { dA = sA; } - - - - - - - - - - - -#if 1 -/* Choose an alpha blend equation. If the sign is preserved on a right shift - * then use a specialized, faster, equation. Otherwise a more general form, - * where all additions are done before the shift, is needed. -*/ -#if (-1 >> 1) < 0 -#define ALPHA_BLEND_COMP(sC, dC, sA) ((((sC - dC) * sA + sC) >> 8) + dC) -#else -#define ALPHA_BLEND_COMP(sC, dC, sA) (((dC << 8) + (sC - dC) * sA + sC) >> 8) -#endif - -#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \ - do { \ - if (dA) \ - { \ - dR = ALPHA_BLEND_COMP(sR, dR, sA); \ - dG = ALPHA_BLEND_COMP(sG, dG, sA); \ - dB = ALPHA_BLEND_COMP(sB, dB, sA); \ - dA = sA + dA - ((sA * dA) / 255); \ - } \ - else \ - { \ - dR = sR; \ - dG = sG; \ - dB = sB; \ - dA = sA; \ - } \ - } while(0) - -#define ALPHA_BLEND_PREMULTIPLIED_COMP(sC, dC, sA) (sC + dC - ((dC * sA) >> 8)) - -#define ALPHA_BLEND_PREMULTIPLIED(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \ - do { \ - tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sR, dR, sA); dR = (tmp > 255 ? 255 : tmp); \ - tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sG, dG, sA); dG = (tmp > 255 ? 255 : tmp); \ - tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sB, dB, sA); dB = (tmp > 255 ? 255 : tmp); \ - dA = sA + dA - ((sA * dA) / 255); \ - } while(0) -#elif 0 - -#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \ - do { \ - if(sA){ \ - if(dA && sA < 255){ \ - int dContrib = dA*(255 - sA)/255; \ - dA = sA+dA - ((sA*dA)/255); \ - dR = (dR*dContrib + sR*sA)/dA; \ - dG = (dG*dContrib + sG*sA)/dA; \ - dB = (dB*dContrib + sB*sA)/dA; \ - }else{ \ - dR = sR; \ - dG = sG; \ - dB = sB; \ - dA = sA; \ - } \ - } \ - } while(0) -#endif - -int -surface_fill_blend (SDL_Surface *surface, SDL_Rect *rect, Uint32 color, - int blendargs); - -void -surface_respect_clip_rect (SDL_Surface *surface, SDL_Rect *rect); - -int -pygame_AlphaBlit (SDL_Surface * src, SDL_Rect * srcrect, - SDL_Surface * dst, SDL_Rect * dstrect, int the_args); - -int -pygame_Blit (SDL_Surface * src, SDL_Rect * srcrect, - SDL_Surface * dst, SDL_Rect * dstrect, int the_args); - -#endif /* SURFACE_H */ diff --git a/venv/Lib/site-packages/easy_install.py b/venv/Lib/site-packages/easy_install.py deleted file mode 100644 index d87e984034b6e6e9eb456ebcb2b3f420c07a48bc..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/easy_install.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Run the EasyInstall command""" - -if __name__ == '__main__': - from setuptools.command.easy_install import main - main() diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/INSTALLER b/venv/Lib/site-packages/pip-20.1.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e38a32041e49332e5e81c2d363dc418d68..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/LICENSE.txt b/venv/Lib/site-packages/pip-20.1.1.dist-info/LICENSE.txt deleted file mode 100644 index 737fec5c5352af3d9a6a47a0670da4bdb52c5725..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2008-2019 The pip developers (see AUTHORS.txt file) - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/METADATA b/venv/Lib/site-packages/pip-20.1.1.dist-info/METADATA deleted file mode 100644 index 1413a044d98913977de7531f38f289696dbfb8c0..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/METADATA +++ /dev/null @@ -1,87 +0,0 @@ -Metadata-Version: 2.1 -Name: pip -Version: 20.1.1 -Summary: The PyPA recommended tool for installing Python packages. -Home-page: https://pip.pypa.io/ -Author: The pip developers -Author-email: pypa-dev@groups.google.com -License: MIT -Project-URL: Documentation, https://pip.pypa.io -Project-URL: Source, https://github.com/pypa/pip -Keywords: distutils easy_install egg setuptools wheel virtualenv -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Software Development :: Build Tools -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.* - -pip - The Python Package Installer -================================== - -.. image:: https://img.shields.io/pypi/v/pip.svg - :target: https://pypi.org/project/pip/ - -.. image:: https://readthedocs.org/projects/pip/badge/?version=latest - :target: https://pip.pypa.io/en/latest - -pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes. - -Please take a look at our documentation for how to install and use pip: - -* `Installation`_ -* `Usage`_ - -We release updates regularly, with a new version every 3 months. Find more details in our documentation: - -* `Release notes`_ -* `Release process`_ - -In 2020, we're working on improvements to the heart of pip. Please `learn more and take our survey`_ to help us do it right. - -If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms: - -* `Issue tracking`_ -* `Discourse channel`_ -* `User IRC`_ - -If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms: - -* `GitHub page`_ -* `Development documentation`_ -* `Development mailing list`_ -* `Development IRC`_ - -Code of Conduct ---------------- - -Everyone interacting in the pip project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. - -.. _package installer: https://packaging.python.org/guides/tool-recommendations/ -.. _Python Package Index: https://pypi.org -.. _Installation: https://pip.pypa.io/en/stable/installing.html -.. _Usage: https://pip.pypa.io/en/stable/ -.. _Release notes: https://pip.pypa.io/en/stable/news.html -.. _Release process: https://pip.pypa.io/en/latest/development/release-process/ -.. _GitHub page: https://github.com/pypa/pip -.. _Development documentation: https://pip.pypa.io/en/latest/development -.. _learn more and take our survey: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html -.. _Issue tracking: https://github.com/pypa/pip/issues -.. _Discourse channel: https://discuss.python.org/c/packaging -.. _Development mailing list: https://groups.google.com/forum/#!forum/pypa-dev -.. _User IRC: https://webchat.freenode.net/?channels=%23pypa -.. _Development IRC: https://webchat.freenode.net/?channels=%23pypa-dev -.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ - - diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/RECORD b/venv/Lib/site-packages/pip-20.1.1.dist-info/RECORD deleted file mode 100644 index 0726471d883da94c99ddf7e56ae55171347c0356..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/RECORD +++ /dev/null @@ -1,743 +0,0 @@ -../../Scripts/pip.exe,sha256=lECGixVFPqx6_l9xsvfq-toOZfMAUZt_emyxGdDsUlc,102766 -../../Scripts/pip3.7.exe,sha256=lECGixVFPqx6_l9xsvfq-toOZfMAUZt_emyxGdDsUlc,102766 -../../Scripts/pip3.exe,sha256=lECGixVFPqx6_l9xsvfq-toOZfMAUZt_emyxGdDsUlc,102766 -pip-20.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip-20.1.1.dist-info/LICENSE.txt,sha256=W6Ifuwlk-TatfRU2LR7W1JMcyMj5_y1NkRkOEJvnRDE,1090 -pip-20.1.1.dist-info/METADATA,sha256=dwRFheMvgIBpyZllM4tVlf5TfjoXc1ZxlsJf0ze61_M,3634 -pip-20.1.1.dist-info/RECORD,, -pip-20.1.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -pip-20.1.1.dist-info/entry_points.txt,sha256=HtfDOwpUlr9s73jqLQ6wF9V0_0qvUXJwCBz7Vwx0Ue0,125 -pip-20.1.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip/__init__.py,sha256=9lnkMA2mCKfgnTkqep7tMbosgEJ4rENcyu2tcqDwUNw,455 -pip/__main__.py,sha256=bqCAM1cj1HwHCDx3WJa-LJxOBXimGxE8OjBqAvnhVg0,911 -pip/__pycache__/__init__.cpython-37.pyc,, -pip/__pycache__/__main__.cpython-37.pyc,, -pip/_internal/__init__.py,sha256=2si23JBW1erg19xIJ8CD6tfGknz0ijtXmzuXjGfGMGE,495 -pip/_internal/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/__pycache__/build_env.cpython-37.pyc,, -pip/_internal/__pycache__/cache.cpython-37.pyc,, -pip/_internal/__pycache__/configuration.cpython-37.pyc,, -pip/_internal/__pycache__/exceptions.cpython-37.pyc,, -pip/_internal/__pycache__/locations.cpython-37.pyc,, -pip/_internal/__pycache__/main.cpython-37.pyc,, -pip/_internal/__pycache__/pyproject.cpython-37.pyc,, -pip/_internal/__pycache__/self_outdated_check.cpython-37.pyc,, -pip/_internal/__pycache__/wheel_builder.cpython-37.pyc,, -pip/_internal/build_env.py,sha256=2P0xaKpDhEfrA5P7cXnbx9QpL52Hc1Uturp8EIcjGRg,7506 -pip/_internal/cache.py,sha256=aXPdcihRKQVH26jl1cxSKTmTnV0_hNMs7cGADMUFi1Y,12334 -pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 -pip/_internal/cli/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/cli/__pycache__/autocompletion.cpython-37.pyc,, -pip/_internal/cli/__pycache__/base_command.cpython-37.pyc,, -pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pyc,, -pip/_internal/cli/__pycache__/command_context.cpython-37.pyc,, -pip/_internal/cli/__pycache__/main.cpython-37.pyc,, -pip/_internal/cli/__pycache__/main_parser.cpython-37.pyc,, -pip/_internal/cli/__pycache__/parser.cpython-37.pyc,, -pip/_internal/cli/__pycache__/progress_bars.cpython-37.pyc,, -pip/_internal/cli/__pycache__/req_command.cpython-37.pyc,, -pip/_internal/cli/__pycache__/spinners.cpython-37.pyc,, -pip/_internal/cli/__pycache__/status_codes.cpython-37.pyc,, -pip/_internal/cli/autocompletion.py,sha256=ekGNtcDI0p7rFVc-7s4T9Tbss4Jgb7vsB649XJIblRg,6547 -pip/_internal/cli/base_command.py,sha256=O5fT5HHfc_UYNvhqK0rjfh_13K3fIVQzcKUF4xKbFts,8024 -pip/_internal/cli/cmdoptions.py,sha256=SjHNqaQ49FO0VKshjoazPVFb7iDx71FKW_N4KgcS3qQ,28403 -pip/_internal/cli/command_context.py,sha256=ygMVoTy2jpNilKT-6416gFSQpaBtrKRBbVbi2fy__EU,975 -pip/_internal/cli/main.py,sha256=Hxc9dZyW3xiDsYZX-_J2cGXT5DWNLNn_Y7o9oUme-Ec,2616 -pip/_internal/cli/main_parser.py,sha256=voAtjo4WVPIYeu7Fqabva9SXaB3BjG0gH93GBfe6jHQ,2843 -pip/_internal/cli/parser.py,sha256=4FfwW8xB84CrkLs35ud90ZkhCcWyVkx17XD6j3XCW7c,9480 -pip/_internal/cli/progress_bars.py,sha256=WtKOHkePvHwnlhDUotAmKpjBH6hBdVTOnxSiiuCC2l8,9031 -pip/_internal/cli/req_command.py,sha256=NajtG3IfB3YkiM7LANLttyJTfPtgB-3CTErY0YR0k50,15309 -pip/_internal/cli/spinners.py,sha256=PS9s53LB5aDPelIn8FhKerK3bOdgeefFH5wSWJ2PCzI,5509 -pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 -pip/_internal/commands/__init__.py,sha256=yoLAnmEXjoQgYfDuwsuWG3RzzD19oeHobGEhmpIYsB4,4100 -pip/_internal/commands/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/commands/__pycache__/cache.cpython-37.pyc,, -pip/_internal/commands/__pycache__/check.cpython-37.pyc,, -pip/_internal/commands/__pycache__/completion.cpython-37.pyc,, -pip/_internal/commands/__pycache__/configuration.cpython-37.pyc,, -pip/_internal/commands/__pycache__/debug.cpython-37.pyc,, -pip/_internal/commands/__pycache__/download.cpython-37.pyc,, -pip/_internal/commands/__pycache__/freeze.cpython-37.pyc,, -pip/_internal/commands/__pycache__/hash.cpython-37.pyc,, -pip/_internal/commands/__pycache__/help.cpython-37.pyc,, -pip/_internal/commands/__pycache__/install.cpython-37.pyc,, -pip/_internal/commands/__pycache__/list.cpython-37.pyc,, -pip/_internal/commands/__pycache__/search.cpython-37.pyc,, -pip/_internal/commands/__pycache__/show.cpython-37.pyc,, -pip/_internal/commands/__pycache__/uninstall.cpython-37.pyc,, -pip/_internal/commands/__pycache__/wheel.cpython-37.pyc,, -pip/_internal/commands/cache.py,sha256=LZCLVEYCr5Ugh81Zt07Hz5v6SIt0QQzr2-npj3M44aE,5676 -pip/_internal/commands/check.py,sha256=fqRrz2uKPC8Qsx2rgLygAD2Rbr-qxp1Q55zUoyZzB9Q,1677 -pip/_internal/commands/completion.py,sha256=BoEW3RZQZhsZe5to1aOe245dcBLkf-PTCJL7_u9A-Es,2957 -pip/_internal/commands/configuration.py,sha256=y74Vl2p41dBOE2NwUzW4YqnbGbl9r0lsCyBlHguDAWA,7206 -pip/_internal/commands/debug.py,sha256=3YkY_M-h1tfpEzat4agulzAk17MU93Qt7ehy_Gi2l6Q,7275 -pip/_internal/commands/download.py,sha256=thDfHi0qY6DQ_1GkYPTutwta3tA0RaHJhKycepC4FgA,4740 -pip/_internal/commands/freeze.py,sha256=LveCd11SlrZ7s3RovnWpaK0tXB2Jci4DPcP6A9cj0e4,3342 -pip/_internal/commands/hash.py,sha256=KckEd5FeomfsRgZmRzhJRPYSsz-HXbFZGNdrzp12ftQ,1742 -pip/_internal/commands/help.py,sha256=s8bDMJbRVxs9ehLKuD4mXTsv1bTRapy1jDwaTCE90qw,1193 -pip/_internal/commands/install.py,sha256=1oXXadnvHM-55XWIdeqdaR75BjLPGC2-wXEkkhMyMtQ,25430 -pip/_internal/commands/list.py,sha256=GCrhhu07cw3CyZGFk8rGM2ejk3JmeF9XLbC7raiq_bw,10141 -pip/_internal/commands/search.py,sha256=KjAz-s9mwkiLfDd-cpQO3pL6KFoOyl1RKlvxnJj3zz8,5191 -pip/_internal/commands/show.py,sha256=RqSX_KvzcZWz1gxIOZEnnk4-VeSkNvr0yWz5jF6JrcY,6791 -pip/_internal/commands/uninstall.py,sha256=D2Otze7J-RJvjfozRq2Yon9NKJrg4cbBGFuXyEwBMR0,3202 -pip/_internal/commands/wheel.py,sha256=oxyo51V1m_Hu4U-HCS53vBx5-82Q6GOhn1doOgAr3KE,6431 -pip/_internal/configuration.py,sha256=k3Y3HMMMm_fzNqX75QoNuHvjX8tplmNBuIJJpDHmf9M,14349 -pip/_internal/distributions/__init__.py,sha256=ECBUW5Gtu9TjJwyFLvim-i6kUMYVuikNh9I5asL6tbA,959 -pip/_internal/distributions/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/distributions/__pycache__/base.cpython-37.pyc,, -pip/_internal/distributions/__pycache__/installed.cpython-37.pyc,, -pip/_internal/distributions/__pycache__/sdist.cpython-37.pyc,, -pip/_internal/distributions/__pycache__/wheel.cpython-37.pyc,, -pip/_internal/distributions/base.py,sha256=ruprpM_L2T2HNi3KLUHlbHimZ1sWVw-3Q0Lb8O7TDAI,1425 -pip/_internal/distributions/installed.py,sha256=YqlkBKr6TVP1MAYS6SG8ojud21wVOYLMZ8jMLJe9MSU,760 -pip/_internal/distributions/sdist.py,sha256=D4XTMlCwgPlK69l62GLYkNSVTVe99fR5iAcVt2EbGok,4086 -pip/_internal/distributions/wheel.py,sha256=95uD-TfaYoq3KiKBdzk9YMN4RRqJ28LNoSTS2K46gek,1294 -pip/_internal/exceptions.py,sha256=B3tSkzheqSfGoGt5OcAOhLhfnWWMzfJ60URvZWwkwHw,10308 -pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30 -pip/_internal/index/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/index/__pycache__/collector.cpython-37.pyc,, -pip/_internal/index/__pycache__/package_finder.cpython-37.pyc,, -pip/_internal/index/collector.py,sha256=tFpQdkBlbNzdwlep7a5_o9unymgWuEmo2WtARsagiao,21547 -pip/_internal/index/package_finder.py,sha256=2Uq4RPSRboyRPj1Zp3-SB8ZFNLAEMrZv6G2yH-wVjIA,37676 -pip/_internal/locations.py,sha256=VifFEqhc7FWFV8QGoEM3CpECRY8Doq7kTytytxsEgx0,6734 -pip/_internal/main.py,sha256=IVBnUQ-FG7DK6617uEXRB5_QJqspAsBFmTmTesYkbdQ,437 -pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 -pip/_internal/models/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/models/__pycache__/candidate.cpython-37.pyc,, -pip/_internal/models/__pycache__/direct_url.cpython-37.pyc,, -pip/_internal/models/__pycache__/format_control.cpython-37.pyc,, -pip/_internal/models/__pycache__/index.cpython-37.pyc,, -pip/_internal/models/__pycache__/link.cpython-37.pyc,, -pip/_internal/models/__pycache__/scheme.cpython-37.pyc,, -pip/_internal/models/__pycache__/search_scope.cpython-37.pyc,, -pip/_internal/models/__pycache__/selection_prefs.cpython-37.pyc,, -pip/_internal/models/__pycache__/target_python.cpython-37.pyc,, -pip/_internal/models/__pycache__/wheel.cpython-37.pyc,, -pip/_internal/models/candidate.py,sha256=Y58Bcm6oXUj0iS-yhmerlGo5CQJI2p0Ww9h6hR9zQDw,1150 -pip/_internal/models/direct_url.py,sha256=MnBLPci1hE9Ndh6d3m0LAqB7hX3ci80CCJTE5eerFaQ,6900 -pip/_internal/models/format_control.py,sha256=ICzVjjGwfZYdX-eLLKHjMHLutEJlAGpfj09OG_eMqac,2673 -pip/_internal/models/index.py,sha256=K59A8-hVhBM20Xkahr4dTwP7OjkJyEqXH11UwHFVgqM,1060 -pip/_internal/models/link.py,sha256=KobEaGViwOzyPBD7kgzpGqyrQfh5zjlonOStCGAhl2U,7302 -pip/_internal/models/scheme.py,sha256=vvhBrrno7eVDXcdKHiZWwxhPHf4VG5uSCEkC0QDR2RU,679 -pip/_internal/models/search_scope.py,sha256=AYbFyfEen5cx0kRZTMgUWUxzcMr5nDk32MO4S67Ror4,4712 -pip/_internal/models/selection_prefs.py,sha256=rPeif2KKjhTPXeMoQYffjqh10oWpXhdkxRDaPT1HO8k,1908 -pip/_internal/models/target_python.py,sha256=bbOSwPmojPMtCW6i2XMNjVJzt_2GQYfx3FcGQY8pL44,3842 -pip/_internal/models/wheel.py,sha256=FTfzVb4WIbfIehxhdlAVvCil_MQ0-W44oyN56cE6NHc,2772 -pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50 -pip/_internal/network/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/network/__pycache__/auth.cpython-37.pyc,, -pip/_internal/network/__pycache__/cache.cpython-37.pyc,, -pip/_internal/network/__pycache__/download.cpython-37.pyc,, -pip/_internal/network/__pycache__/session.cpython-37.pyc,, -pip/_internal/network/__pycache__/utils.cpython-37.pyc,, -pip/_internal/network/__pycache__/xmlrpc.cpython-37.pyc,, -pip/_internal/network/auth.py,sha256=HJg5peC3gL44H7pmZhCPnu2MrwpAalOSF7d1jmNDqt8,11125 -pip/_internal/network/cache.py,sha256=51CExcRkXWrgMZ7WsrZ6cmijKfViD5tVgKbBvJHO1IE,2394 -pip/_internal/network/download.py,sha256=MIisedL1oFOSrYAN119HDlIuFfw6eL6CNY7oJhHIzUc,6269 -pip/_internal/network/session.py,sha256=Zs0uiyPxTpfpgSv-ZI9hK9TjasmTplBuBivOTcUiJME,15208 -pip/_internal/network/utils.py,sha256=iiixo1OeaQ3niUWiBjg59PN6f1w7vvTww1vFriTD_IU,1959 -pip/_internal/network/xmlrpc.py,sha256=AL115M3vFJ8xiHVJneb8Hi0ZFeRvdPhblC89w25OG5s,1597 -pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_internal/operations/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/operations/__pycache__/check.cpython-37.pyc,, -pip/_internal/operations/__pycache__/freeze.cpython-37.pyc,, -pip/_internal/operations/__pycache__/prepare.cpython-37.pyc,, -pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_internal/operations/build/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/operations/build/__pycache__/metadata.cpython-37.pyc,, -pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-37.pyc,, -pip/_internal/operations/build/__pycache__/wheel.cpython-37.pyc,, -pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-37.pyc,, -pip/_internal/operations/build/metadata.py,sha256=yHMi5gHYXcXyHcvUPWHdO-UyOo3McFWljn_nHfM1O9c,1307 -pip/_internal/operations/build/metadata_legacy.py,sha256=VgzBTk8naIO8-8N_ifEYF7ZAxWUDhphWVIaVlZ2FqYM,2011 -pip/_internal/operations/build/wheel.py,sha256=ntltdNP6D2Tpr4V0agssu6rE0F9LaBpJkYT6zSdhEbw,1469 -pip/_internal/operations/build/wheel_legacy.py,sha256=N1aqNZyGURBX0Bj6wPmB0t4866oMbxoHUpC9pz6FyT0,3356 -pip/_internal/operations/check.py,sha256=a6uHG0daoWpmSPCdL7iYJaGQYZ-CRvPvTnCv2PnIIs0,5353 -pip/_internal/operations/freeze.py,sha256=mGT2OFjMOb0FlVjgedAzJ9GbNOgNwYiL0130xx60pHA,10587 -pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51 -pip/_internal/operations/install/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/operations/install/__pycache__/editable_legacy.cpython-37.pyc,, -pip/_internal/operations/install/__pycache__/legacy.cpython-37.pyc,, -pip/_internal/operations/install/__pycache__/wheel.cpython-37.pyc,, -pip/_internal/operations/install/editable_legacy.py,sha256=rJ_xs2qtDUjpY2-n6eYlVyZiNoKbOtZXZrYrcnIELt4,1488 -pip/_internal/operations/install/legacy.py,sha256=YkKdL_tyNwDP2huOGxmopySh5Pz2v_wRVeSTEa6ZUco,4686 -pip/_internal/operations/install/wheel.py,sha256=8IO3GYTtrJ42cqipLOh0rxex4j-PfU8m71HVB0tOQd0,23885 -pip/_internal/operations/prepare.py,sha256=RDwtSetVTfv-nv1-_apYBA3Dez5ngBmOYzcuZy2Q3vk,20030 -pip/_internal/pyproject.py,sha256=VJKsrXORGiGoDPVKCQhuu4tWlQSTOhoiRlVLRNu4rx4,7400 -pip/_internal/req/__init__.py,sha256=UVaYPlHZVGRBQQPjvGC_6jJDQtewXm0ws-8Lxhg_TiY,2671 -pip/_internal/req/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/req/__pycache__/constructors.cpython-37.pyc,, -pip/_internal/req/__pycache__/req_file.cpython-37.pyc,, -pip/_internal/req/__pycache__/req_install.cpython-37.pyc,, -pip/_internal/req/__pycache__/req_set.cpython-37.pyc,, -pip/_internal/req/__pycache__/req_tracker.cpython-37.pyc,, -pip/_internal/req/__pycache__/req_uninstall.cpython-37.pyc,, -pip/_internal/req/constructors.py,sha256=i_dU2sYtSk5GMsad68gBx26tfneRmhPF2sYGe4uPnu8,15441 -pip/_internal/req/req_file.py,sha256=5QlZr36kkw1Jsbr8vFO-fGUEAef9h-AoRRqjx8EYSuQ,19075 -pip/_internal/req/req_install.py,sha256=9yn_fBFTyzHPMjYG5WXBB2WiGQvk3BT6gYl9Khw8ZoE,31713 -pip/_internal/req/req_set.py,sha256=EBHZ9zWSR8arxjcadyU2OotZIECemM8oOFQ0nK-Bb7E,7792 -pip/_internal/req/req_tracker.py,sha256=cAKhSw-QbhGxqPF1Wc0zD6jo932jpdYF3ROfRSH8hes,4744 -pip/_internal/req/req_uninstall.py,sha256=NdErRQBpNScsdwJAo3O_zo2KPPfQyVMJ_Q2mxPWYyOA,23734 -pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_internal/resolution/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/resolution/__pycache__/base.cpython-37.pyc,, -pip/_internal/resolution/base.py,sha256=xi72YmIS-lEjyK13PN_3qkGGthA4yGoK0C6qWynyHrE,682 -pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_internal/resolution/legacy/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/resolution/legacy/__pycache__/resolver.cpython-37.pyc,, -pip/_internal/resolution/legacy/resolver.py,sha256=56GuGHWcseV24cvTCOuRHMAF_Er1UeDxn5m18XMkHBs,17587 -pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/__pycache__/base.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-37.pyc,, -pip/_internal/resolution/resolvelib/base.py,sha256=l2Z3-1Qg243lWzwFbaN17qixA4U8LYr-qMhZTdaHROc,1502 -pip/_internal/resolution/resolvelib/candidates.py,sha256=wzi9t3aX1Twi3xTNEkpG6eWOd0dEg5uKul-QkK3arvw,15173 -pip/_internal/resolution/resolvelib/factory.py,sha256=mDs3p8D9N9zfYvn_iIx0saDLHF1SF7KHBQlA1gWSWVQ,7574 -pip/_internal/resolution/resolvelib/provider.py,sha256=0fKuPuEoD5T7w-YwKgQZc1AmgSnAkrxGnLBOf-_6Kiw,1703 -pip/_internal/resolution/resolvelib/requirements.py,sha256=bu9Y4YINHjvBm-NBKvnxw9IYHW4t6rRlm4-QKVqLDsM,3872 -pip/_internal/resolution/resolvelib/resolver.py,sha256=3LXhhCz6CtIpih8tK2nHeRvVEjVJmXrqxNCM1FQM1U0,6673 -pip/_internal/self_outdated_check.py,sha256=3KO1pTJUuYaiV9X0t87I9PimkGL82HbhLWbocqKZpBU,8009 -pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_internal/utils/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/utils/__pycache__/appdirs.cpython-37.pyc,, -pip/_internal/utils/__pycache__/compat.cpython-37.pyc,, -pip/_internal/utils/__pycache__/compatibility_tags.cpython-37.pyc,, -pip/_internal/utils/__pycache__/deprecation.cpython-37.pyc,, -pip/_internal/utils/__pycache__/direct_url_helpers.cpython-37.pyc,, -pip/_internal/utils/__pycache__/distutils_args.cpython-37.pyc,, -pip/_internal/utils/__pycache__/encoding.cpython-37.pyc,, -pip/_internal/utils/__pycache__/entrypoints.cpython-37.pyc,, -pip/_internal/utils/__pycache__/filesystem.cpython-37.pyc,, -pip/_internal/utils/__pycache__/filetypes.cpython-37.pyc,, -pip/_internal/utils/__pycache__/glibc.cpython-37.pyc,, -pip/_internal/utils/__pycache__/hashes.cpython-37.pyc,, -pip/_internal/utils/__pycache__/inject_securetransport.cpython-37.pyc,, -pip/_internal/utils/__pycache__/logging.cpython-37.pyc,, -pip/_internal/utils/__pycache__/misc.cpython-37.pyc,, -pip/_internal/utils/__pycache__/models.cpython-37.pyc,, -pip/_internal/utils/__pycache__/packaging.cpython-37.pyc,, -pip/_internal/utils/__pycache__/pkg_resources.cpython-37.pyc,, -pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pyc,, -pip/_internal/utils/__pycache__/subprocess.cpython-37.pyc,, -pip/_internal/utils/__pycache__/temp_dir.cpython-37.pyc,, -pip/_internal/utils/__pycache__/typing.cpython-37.pyc,, -pip/_internal/utils/__pycache__/unpacking.cpython-37.pyc,, -pip/_internal/utils/__pycache__/urls.cpython-37.pyc,, -pip/_internal/utils/__pycache__/virtualenv.cpython-37.pyc,, -pip/_internal/utils/__pycache__/wheel.cpython-37.pyc,, -pip/_internal/utils/appdirs.py,sha256=RZzUG-Bkh2b-miX0DSZ3v703_-bgK-v0PfWCCjwVE9g,1349 -pip/_internal/utils/compat.py,sha256=ZRJsXMjq373p0US54CUkKRkpLH-ioOM3H3yAhmbUPcs,8898 -pip/_internal/utils/compatibility_tags.py,sha256=b2NWEbxfsrB2pBLwJkNVSYUrIAsumQ2IWDoNabbwLPs,5492 -pip/_internal/utils/deprecation.py,sha256=pBnNogoA4UGTxa_JDnPXBRRYpKMbExAhXpBwAwklOBs,3318 -pip/_internal/utils/direct_url_helpers.py,sha256=bZCBNwPQVyZpYGjX_VcomvVvRHvKw-9JzEV-Ft09LQc,4359 -pip/_internal/utils/distutils_args.py,sha256=a56mblNxk9BGifbpEETG61mmBrqhjtjRkJ4HYn-oOEE,1350 -pip/_internal/utils/encoding.py,sha256=hxZz0t3Whw3d4MHQEiofxalTlfKwxFdLc8fpeGfhKo8,1320 -pip/_internal/utils/entrypoints.py,sha256=vHcNpnksCv6mllihU6hfifdsKPEjwcaJ1aLIXEaynaU,1152 -pip/_internal/utils/filesystem.py,sha256=fqpFwT280152rlX1RjJqjoLp_MXVA8HzKTtDmsl15Ps,6813 -pip/_internal/utils/filetypes.py,sha256=R2FwzoeX7b-rZALOXx5cuO8VPPMhUQ4ne7wm3n3IcWA,571 -pip/_internal/utils/glibc.py,sha256=LOeNGgawCKS-4ke9fii78fwXD73dtNav3uxz1Bf-Ab8,3297 -pip/_internal/utils/hashes.py,sha256=LQVOt2LTWAdBJH6WPim1YGdF0J-0AfBBLghIDlY1-80,3986 -pip/_internal/utils/inject_securetransport.py,sha256=M17ZlFVY66ApgeASVjKKLKNz0LAfk-SyU0HZ4ZB6MmI,810 -pip/_internal/utils/logging.py,sha256=YIfuDUEkmdn9cIRQ_Ec8rgXs1m5nOwDECtZqM4CBH5U,13093 -pip/_internal/utils/misc.py,sha256=cK17YkNfEccS9AuH6Xc9kYQxE0DPNgb-ULh8QgKPr8c,26195 -pip/_internal/utils/models.py,sha256=IA0hw_T4awQzui0kqfIEASm5yLtgZAB08ag59Nip5G8,1148 -pip/_internal/utils/packaging.py,sha256=VtiwcAAL7LBi7tGL2je7LeW4bE11KMHGCsJ1NZY5XtM,3035 -pip/_internal/utils/pkg_resources.py,sha256=ZX-k7V5q_aNWyDse92nN7orN1aCpRLsaxzpkBZ1XKzU,1254 -pip/_internal/utils/setuptools_build.py,sha256=E1KswI7wfNnCDE5R6G8c9ZbByENpu7NqocjY26PCQDw,5058 -pip/_internal/utils/subprocess.py,sha256=vI2QWpNDqM-dkn-z8i1Yrfxnn5sYniPeWn6FhTxX4dY,9902 -pip/_internal/utils/temp_dir.py,sha256=H8yUBrRWqTM83cuUu7jVvw_xKL9eZtg_IIbXQtjMLlA,8185 -pip/_internal/utils/typing.py,sha256=xkYwOeHlf4zsHXBDC4310HtEqwhQcYXFPq2h35Tcrl0,1401 -pip/_internal/utils/unpacking.py,sha256=M944JTSiapBOSKLWu7lbawpVHSE7flfzZTEr3TAG7v8,9438 -pip/_internal/utils/urls.py,sha256=q2rw1kMiiig_XZcoyJSsWMJQqYw-2wUmrMoST4mCW_I,1527 -pip/_internal/utils/virtualenv.py,sha256=iVJ8ZlbNtGon6I4uZFsY2SidrUf1vt3YHrgS5CuU98w,3553 -pip/_internal/utils/wheel.py,sha256=ofsZEN35YhSxRYC4gfzpTtqa_UQ8GF1tl4jtyUdd0gU,7306 -pip/_internal/vcs/__init__.py,sha256=viJxJRqRE_mVScum85bgQIXAd6o0ozFt18VpC-qIJrM,617 -pip/_internal/vcs/__pycache__/__init__.cpython-37.pyc,, -pip/_internal/vcs/__pycache__/bazaar.cpython-37.pyc,, -pip/_internal/vcs/__pycache__/git.cpython-37.pyc,, -pip/_internal/vcs/__pycache__/mercurial.cpython-37.pyc,, -pip/_internal/vcs/__pycache__/subversion.cpython-37.pyc,, -pip/_internal/vcs/__pycache__/versioncontrol.cpython-37.pyc,, -pip/_internal/vcs/bazaar.py,sha256=84q1-kj1_nJ9AMzMu8RmMp-riRZu81M7K9kowcYgi3U,3957 -pip/_internal/vcs/git.py,sha256=wlvvVT-hPRwCvkihEoOCZmkCzMzosmV43_DTPvEVA_M,14165 -pip/_internal/vcs/mercurial.py,sha256=wVdmoFH-RYoaxjtuAqw40b0daMPX-Fr_26W1ME_9HZU,5347 -pip/_internal/vcs/subversion.py,sha256=6shByxeASetbM7WCj6WNoPcuLfBK65DoOEqbkSiWPAI,12331 -pip/_internal/vcs/versioncontrol.py,sha256=RtSrHr96CynqXYBQIC61cVY_9C0e7hk8dXUV-BpHmpI,23591 -pip/_internal/wheel_builder.py,sha256=p9ZFawfCR1GXchTRY6oq7Qx5enLLjs2SouafNFNsAAE,9590 -pip/_vendor/__init__.py,sha256=Tfcbsek_rpFZWMnYp6vzGpWHsmiwBGYOmInUX1NGJp4,4788 -pip/_vendor/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/__pycache__/appdirs.cpython-37.pyc,, -pip/_vendor/__pycache__/contextlib2.cpython-37.pyc,, -pip/_vendor/__pycache__/distro.cpython-37.pyc,, -pip/_vendor/__pycache__/ipaddress.cpython-37.pyc,, -pip/_vendor/__pycache__/pyparsing.cpython-37.pyc,, -pip/_vendor/__pycache__/retrying.cpython-37.pyc,, -pip/_vendor/__pycache__/six.cpython-37.pyc,, -pip/_vendor/__pycache__/toml.cpython-37.pyc,, -pip/_vendor/appdirs.py,sha256=pYg72GhKgkVzkPxZNFUSIzMF3tAPWBgIPoQE8jgVftg,25888 -pip/_vendor/cachecontrol/__init__.py,sha256=pJtAaUxOsMPnytI1A3juAJkXYDr8krdSnsg4Yg3OBEg,302 -pip/_vendor/cachecontrol/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/adapter.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/cache.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/compat.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/controller.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/serialize.cpython-37.pyc,, -pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-37.pyc,, -pip/_vendor/cachecontrol/_cmd.py,sha256=URGE0KrA87QekCG3SGPatlSPT571dZTDjNa-ZXX3pDc,1295 -pip/_vendor/cachecontrol/adapter.py,sha256=sSwaSYd93IIfCFU4tOMgSo6b2LCt_gBSaQUj8ktJFOA,4882 -pip/_vendor/cachecontrol/cache.py,sha256=1fc4wJP8HYt1ycnJXeEw5pCpeBL2Cqxx6g9Fb0AYDWQ,805 -pip/_vendor/cachecontrol/caches/__init__.py,sha256=-gHNKYvaeD0kOk5M74eOrsSgIKUtC6i6GfbmugGweEo,86 -pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-37.pyc,, -pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-37.pyc,, -pip/_vendor/cachecontrol/caches/file_cache.py,sha256=nYVKsJtXh6gJXvdn1iWyrhxvkwpQrK-eKoMRzuiwkKk,4153 -pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=HxelMpNCo-dYr2fiJDwM3hhhRmxUYtB5tXm1GpAAT4Y,856 -pip/_vendor/cachecontrol/compat.py,sha256=kHNvMRdt6s_Xwqq_9qJmr9ou3wYMOMUMxPPcwNxT8Mc,695 -pip/_vendor/cachecontrol/controller.py,sha256=CWEX3pedIM9s60suf4zZPtm_JvVgnvogMGK_OiBG5F8,14149 -pip/_vendor/cachecontrol/filewrapper.py,sha256=vACKO8Llzu_ZWyjV1Fxn1MA4TGU60N5N3GSrAFdAY2Q,2533 -pip/_vendor/cachecontrol/heuristics.py,sha256=BFGHJ3yQcxvZizfo90LLZ04T_Z5XSCXvFotrp7Us0sc,4070 -pip/_vendor/cachecontrol/serialize.py,sha256=vIa4jvq4x_KSOLdEIedoknX2aXYHQujLDFV4-F21Dno,7091 -pip/_vendor/cachecontrol/wrapper.py,sha256=5LX0uJwkNQUtYSEw3aGmGu9WY8wGipd81mJ8lG0d0M4,690 -pip/_vendor/certifi/__init__.py,sha256=AOqspvggP_F62Q_4UmJAhx5rZkoRHoRYBE3SCnSJzAk,64 -pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255 -pip/_vendor/certifi/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/certifi/__pycache__/__main__.cpython-37.pyc,, -pip/_vendor/certifi/__pycache__/core.cpython-37.pyc,, -pip/_vendor/certifi/cacert.pem,sha256=hwBo73gFnF0BKiP3FQKfG32xkGDhxl4SwCQiH2rDKr0,284099 -pip/_vendor/certifi/core.py,sha256=c8hoNcYWz6rQo2VAM5d0mcEWiewCvQuOUbCwJz49vhQ,792 -pip/_vendor/chardet/__init__.py,sha256=YsP5wQlsHJ2auF1RZJfypiSrCA7_bQiRm3ES_NI76-Y,1559 -pip/_vendor/chardet/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/big5freq.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/big5prober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/chardistribution.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/charsetprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/compat.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/cp949prober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/enums.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/escprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/escsm.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/eucjpprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/euckrfreq.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/euckrprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/euctwfreq.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/euctwprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/gb2312freq.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/gb2312prober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/hebrewprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/jisfreq.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/jpcntx.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langthaimodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/latin1prober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/mbcssm.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/sjisprober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/universaldetector.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/utf8prober.cpython-37.pyc,, -pip/_vendor/chardet/__pycache__/version.cpython-37.pyc,, -pip/_vendor/chardet/big5freq.py,sha256=D_zK5GyzoVsRes0HkLJziltFQX0bKCLOrFe9_xDvO_8,31254 -pip/_vendor/chardet/big5prober.py,sha256=kBxHbdetBpPe7xrlb-e990iot64g_eGSLd32lB7_h3M,1757 -pip/_vendor/chardet/chardistribution.py,sha256=3woWS62KrGooKyqz4zQSnjFbJpa6V7g02daAibTwcl8,9411 -pip/_vendor/chardet/charsetgroupprober.py,sha256=6bDu8YIiRuScX4ca9Igb0U69TA2PGXXDej6Cc4_9kO4,3787 -pip/_vendor/chardet/charsetprober.py,sha256=KSmwJErjypyj0bRZmC5F5eM7c8YQgLYIjZXintZNstg,5110 -pip/_vendor/chardet/cli/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 -pip/_vendor/chardet/cli/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-37.pyc,, -pip/_vendor/chardet/cli/chardetect.py,sha256=DI8dlV3FBD0c0XA_y3sQ78z754DUv1J8n34RtDjOXNw,2774 -pip/_vendor/chardet/codingstatemachine.py,sha256=VYp_6cyyki5sHgXDSZnXW4q1oelHc3cu9AyQTX7uug8,3590 -pip/_vendor/chardet/compat.py,sha256=PKTzHkSbtbHDqS9PyujMbX74q1a8mMpeQTDVsQhZMRw,1134 -pip/_vendor/chardet/cp949prober.py,sha256=TZ434QX8zzBsnUvL_8wm4AQVTZ2ZkqEEQL_lNw9f9ow,1855 -pip/_vendor/chardet/enums.py,sha256=Aimwdb9as1dJKZaFNUH2OhWIVBVd6ZkJJ_WK5sNY8cU,1661 -pip/_vendor/chardet/escprober.py,sha256=kkyqVg1Yw3DIOAMJ2bdlyQgUFQhuHAW8dUGskToNWSc,3950 -pip/_vendor/chardet/escsm.py,sha256=RuXlgNvTIDarndvllNCk5WZBIpdCxQ0kcd9EAuxUh84,10510 -pip/_vendor/chardet/eucjpprober.py,sha256=iD8Jdp0ISRjgjiVN7f0e8xGeQJ5GM2oeZ1dA8nbSeUw,3749 -pip/_vendor/chardet/euckrfreq.py,sha256=-7GdmvgWez4-eO4SuXpa7tBiDi5vRXQ8WvdFAzVaSfo,13546 -pip/_vendor/chardet/euckrprober.py,sha256=MqFMTQXxW4HbzIpZ9lKDHB3GN8SP4yiHenTmf8g_PxY,1748 -pip/_vendor/chardet/euctwfreq.py,sha256=No1WyduFOgB5VITUA7PLyC5oJRNzRyMbBxaKI1l16MA,31621 -pip/_vendor/chardet/euctwprober.py,sha256=13p6EP4yRaxqnP4iHtxHOJ6R2zxHq1_m8hTRjzVZ95c,1747 -pip/_vendor/chardet/gb2312freq.py,sha256=JX8lsweKLmnCwmk8UHEQsLgkr_rP_kEbvivC4qPOrlc,20715 -pip/_vendor/chardet/gb2312prober.py,sha256=gGvIWi9WhDjE-xQXHvNIyrnLvEbMAYgyUSZ65HUfylw,1754 -pip/_vendor/chardet/hebrewprober.py,sha256=c3SZ-K7hvyzGY6JRAZxJgwJ_sUS9k0WYkvMY00YBYFo,13838 -pip/_vendor/chardet/jisfreq.py,sha256=vpmJv2Bu0J8gnMVRPHMFefTRvo_ha1mryLig8CBwgOg,25777 -pip/_vendor/chardet/jpcntx.py,sha256=PYlNqRUQT8LM3cT5FmHGP0iiscFlTWED92MALvBungo,19643 -pip/_vendor/chardet/langbulgarianmodel.py,sha256=1HqQS9Pbtnj1xQgxitJMvw8X6kKr5OockNCZWfEQrPE,12839 -pip/_vendor/chardet/langcyrillicmodel.py,sha256=LODajvsetH87yYDDQKA2CULXUH87tI223dhfjh9Zx9c,17948 -pip/_vendor/chardet/langgreekmodel.py,sha256=8YAW7bU8YwSJap0kIJSbPMw1BEqzGjWzqcqf0WgUKAA,12688 -pip/_vendor/chardet/langhebrewmodel.py,sha256=JSnqmE5E62tDLTPTvLpQsg5gOMO4PbdWRvV7Avkc0HA,11345 -pip/_vendor/chardet/langhungarianmodel.py,sha256=RhapYSG5l0ZaO-VV4Fan5sW0WRGQqhwBM61yx3yxyOA,12592 -pip/_vendor/chardet/langthaimodel.py,sha256=8l0173Gu_W6G8mxmQOTEF4ls2YdE7FxWf3QkSxEGXJQ,11290 -pip/_vendor/chardet/langturkishmodel.py,sha256=W22eRNJsqI6uWAfwXSKVWWnCerYqrI8dZQTm_M0lRFk,11102 -pip/_vendor/chardet/latin1prober.py,sha256=S2IoORhFk39FEFOlSFWtgVybRiP6h7BlLldHVclNkU8,5370 -pip/_vendor/chardet/mbcharsetprober.py,sha256=AR95eFH9vuqSfvLQZN-L5ijea25NOBCoXqw8s5O9xLQ,3413 -pip/_vendor/chardet/mbcsgroupprober.py,sha256=h6TRnnYq2OxG1WdD5JOyxcdVpn7dG0q-vB8nWr5mbh4,2012 -pip/_vendor/chardet/mbcssm.py,sha256=SY32wVIF3HzcjY3BaEspy9metbNSKxIIB0RKPn7tjpI,25481 -pip/_vendor/chardet/sbcharsetprober.py,sha256=LDSpCldDCFlYwUkGkwD2oFxLlPWIWXT09akH_2PiY74,5657 -pip/_vendor/chardet/sbcsgroupprober.py,sha256=1IprcCB_k1qfmnxGC6MBbxELlKqD3scW6S8YIwdeyXA,3546 -pip/_vendor/chardet/sjisprober.py,sha256=IIt-lZj0WJqK4rmUZzKZP4GJlE8KUEtFYVuY96ek5MQ,3774 -pip/_vendor/chardet/universaldetector.py,sha256=qL0174lSZE442eB21nnktT9_VcAye07laFWUeUrjttY,12485 -pip/_vendor/chardet/utf8prober.py,sha256=IdD8v3zWOsB8OLiyPi-y_fqwipRFxV9Nc1eKBLSuIEw,2766 -pip/_vendor/chardet/version.py,sha256=sp3B08mrDXB-pf3K9fqJ_zeDHOCLC8RrngQyDFap_7g,242 -pip/_vendor/colorama/__init__.py,sha256=DqjXH9URVP3IJwmMt7peYw50ns1RNAymIB9-XdPEFV8,239 -pip/_vendor/colorama/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/colorama/__pycache__/ansi.cpython-37.pyc,, -pip/_vendor/colorama/__pycache__/ansitowin32.cpython-37.pyc,, -pip/_vendor/colorama/__pycache__/initialise.cpython-37.pyc,, -pip/_vendor/colorama/__pycache__/win32.cpython-37.pyc,, -pip/_vendor/colorama/__pycache__/winterm.cpython-37.pyc,, -pip/_vendor/colorama/ansi.py,sha256=Fi0un-QLqRm-v7o_nKiOqyC8PapBJK7DLV_q9LKtTO0,2524 -pip/_vendor/colorama/ansitowin32.py,sha256=u8QaqdqS_xYSfNkPM1eRJLHz6JMWPodaJaP0mxgHCDc,10462 -pip/_vendor/colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915 -pip/_vendor/colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404 -pip/_vendor/colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438 -pip/_vendor/contextlib2.py,sha256=5HjGflUzwWAUfcILhSmC2GqvoYdZZzFzVfIDztHigUs,16915 -pip/_vendor/distlib/__init__.py,sha256=gzl1hjUXmDGrqRyU7ZLjBwJGAcMimQbrZ22XPVaKaRE,581 -pip/_vendor/distlib/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/compat.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/database.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/index.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/locators.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/manifest.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/markers.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/metadata.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/resources.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/scripts.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/util.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/version.cpython-37.pyc,, -pip/_vendor/distlib/__pycache__/wheel.cpython-37.pyc,, -pip/_vendor/distlib/_backport/__init__.py,sha256=bqS_dTOH6uW9iGgd0uzfpPjo6vZ4xpPZ7kyfZJ2vNaw,274 -pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/distlib/_backport/__pycache__/misc.cpython-37.pyc,, -pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-37.pyc,, -pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-37.pyc,, -pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-37.pyc,, -pip/_vendor/distlib/_backport/misc.py,sha256=KWecINdbFNOxSOP1fGF680CJnaC6S4fBRgEtaYTw0ig,971 -pip/_vendor/distlib/_backport/shutil.py,sha256=VW1t3uYqUjWZH7jV-6QiimLhnldoV5uIpH4EuiT1jfw,25647 -pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617 -pip/_vendor/distlib/_backport/sysconfig.py,sha256=BQHFlb6pubCl_dvT1NjtzIthylofjKisox239stDg0U,26854 -pip/_vendor/distlib/_backport/tarfile.py,sha256=Ihp7rXRcjbIKw8COm9wSePV9ARGXbSF9gGXAMn2Q-KU,92628 -pip/_vendor/distlib/compat.py,sha256=xdNZmqFN5HwF30HjRn5M415pcC2kgXRBXn767xS8v-M,41404 -pip/_vendor/distlib/database.py,sha256=fhNzEDtb4HXrpxKyQvhVzDXcOiJlzrOM--UYnvCeZrI,51045 -pip/_vendor/distlib/index.py,sha256=SXKzpQCERctxYDMp_OLee2f0J0e19ZhGdCIoMlUfUQM,21066 -pip/_vendor/distlib/locators.py,sha256=c9E4cDEacJ_uKbuE5BqAVocoWp6rsuBGTkiNDQq3zV4,52100 -pip/_vendor/distlib/manifest.py,sha256=nQEhYmgoreaBZzyFzwYsXxJARu3fo4EkunU163U16iE,14811 -pip/_vendor/distlib/markers.py,sha256=6Ac3cCfFBERexiESWIOXmg-apIP8l2esafNSX3KMy-8,4387 -pip/_vendor/distlib/metadata.py,sha256=OhbCKmf5lswE8unWBopI1hj7tRpHp4ZbFvU4d6aAEMM,40234 -pip/_vendor/distlib/resources.py,sha256=2FGv0ZHF14KXjLIlL0R991lyQQGcewOS4mJ-5n-JVnc,10766 -pip/_vendor/distlib/scripts.py,sha256=OAkEwxRvIzX-VSfhEttQEKJFVLA47gbW0OgQXJRs7OQ,16998 -pip/_vendor/distlib/t32.exe,sha256=NS3xBCVAld35JVFNmb-1QRyVtThukMrwZVeXn4LhaEQ,96768 -pip/_vendor/distlib/t64.exe,sha256=oAqHes78rUWVM0OtVqIhUvequl_PKhAhXYQWnUf7zR0,105984 -pip/_vendor/distlib/util.py,sha256=f2jZCPrcLCt6LcnC0gUy-Fur60tXD8reA7k4rDpHMDw,59845 -pip/_vendor/distlib/version.py,sha256=_n7F6juvQGAcn769E_SHa7fOcf5ERlEVymJ_EjPRwGw,23391 -pip/_vendor/distlib/w32.exe,sha256=lJtnZdeUxTZWya_EW5DZos_K5rswRECGspIl8ZJCIXs,90112 -pip/_vendor/distlib/w64.exe,sha256=0aRzoN2BO9NWW4ENy4_4vHkHR4qZTFZNVSAJJYlODTI,99840 -pip/_vendor/distlib/wheel.py,sha256=bRtR5bNR_u_DwkwktN1bgZuwLVOJT1p_vNIUPyN8kJc,40452 -pip/_vendor/distro.py,sha256=xxMIh2a3KmippeWEHzynTdHT3_jZM0o-pos0dAWJROM,43628 -pip/_vendor/html5lib/__init__.py,sha256=Ztrn7UvF-wIFAgRBBa0ML-Gu5AffH3BPX_INJx4SaBI,1162 -pip/_vendor/html5lib/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/_inputstream.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/_utils.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/constants.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/html5parser.cpython-37.pyc,, -pip/_vendor/html5lib/__pycache__/serializer.cpython-37.pyc,, -pip/_vendor/html5lib/_ihatexml.py,sha256=3LBtJMlzgwM8vpQiU1TvGmEEmNH72sV0yD8yS53y07A,16705 -pip/_vendor/html5lib/_inputstream.py,sha256=bPUWcAfJScK4xkjQQaG_HsI2BvEVbFvI0AsodDYPQj0,32552 -pip/_vendor/html5lib/_tokenizer.py,sha256=YAaOEBD6qc5ISq9Xt9Nif1OFgcybTTfMdwqBkZhpAq4,76580 -pip/_vendor/html5lib/_trie/__init__.py,sha256=8VR1bcgD2OpeS2XExpu5yBhP_Q1K-lwKbBKICBPf1kU,289 -pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-37.pyc,, -pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-37.pyc,, -pip/_vendor/html5lib/_trie/__pycache__/py.cpython-37.pyc,, -pip/_vendor/html5lib/_trie/_base.py,sha256=CaybYyMro8uERQYjby2tTeSUatnWDfWroUN9N7ety5w,1013 -pip/_vendor/html5lib/_trie/datrie.py,sha256=EQpqSfkZRuTbE-DuhW7xMdVDxdZNZ0CfmnYfHA_3zxM,1178 -pip/_vendor/html5lib/_trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775 -pip/_vendor/html5lib/_utils.py,sha256=ismpASeqa2jqEPQjHUj8vReAf7yIoKnvLN5fuOw6nv0,4015 -pip/_vendor/html5lib/constants.py,sha256=4lmZWLtEPRLnl8NzftOoYTJdo6jpeMtP6dqQC0g_bWQ,83518 -pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/base.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/lint.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-37.pyc,, -pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-37.pyc,, -pip/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=lViZc2JMCclXi_5gduvmdzrRxtO5Xo9ONnbHBVCsykU,919 -pip/_vendor/html5lib/filters/base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286 -pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=egDXUEHXmAG9504xz0K6ALDgYkvUrC2q15YUVeNlVQg,2945 -pip/_vendor/html5lib/filters/lint.py,sha256=jk6q56xY0ojiYfvpdP-OZSm9eTqcAdRqhCoPItemPYA,3643 -pip/_vendor/html5lib/filters/optionaltags.py,sha256=8lWT75J0aBOHmPgfmqTHSfPpPMp01T84NKu0CRedxcE,10588 -pip/_vendor/html5lib/filters/sanitizer.py,sha256=4ON02KNjuqda1lCw5_JCUZxb0BzWR5M7ON84dtJ7dm0,26248 -pip/_vendor/html5lib/filters/whitespace.py,sha256=8eWqZxd4UC4zlFGW6iyY6f-2uuT8pOCSALc3IZt7_t4,1214 -pip/_vendor/html5lib/html5parser.py,sha256=g5g2ezkusHxhi7b23vK_-d6K6BfIJRbqIQmvQ9z4EgI,118963 -pip/_vendor/html5lib/serializer.py,sha256=yfcfBHse2wDs6ojxn-kieJjLT5s1ipilQJ0gL3-rJis,15758 -pip/_vendor/html5lib/treeadapters/__init__.py,sha256=A0rY5gXIe4bJOiSGRO_j_tFhngRBO8QZPzPtPw5dFzo,679 -pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-37.pyc,, -pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-37.pyc,, -pip/_vendor/html5lib/treeadapters/genshi.py,sha256=CH27pAsDKmu4ZGkAUrwty7u0KauGLCZRLPMzaO3M5vo,1715 -pip/_vendor/html5lib/treeadapters/sax.py,sha256=BKS8woQTnKiqeffHsxChUqL4q2ZR_wb5fc9MJ3zQC8s,1776 -pip/_vendor/html5lib/treebuilders/__init__.py,sha256=AysSJyvPfikCMMsTVvaxwkgDieELD5dfR8FJIAuq7hY,3592 -pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-37.pyc,, -pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-37.pyc,, -pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-37.pyc,, -pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-37.pyc,, -pip/_vendor/html5lib/treebuilders/base.py,sha256=wQGp5yy22TNG8tJ6aREe4UUeTR7A99dEz0BXVaedWb4,14579 -pip/_vendor/html5lib/treebuilders/dom.py,sha256=22whb0C71zXIsai5mamg6qzBEiigcBIvaDy4Asw3at0,8925 -pip/_vendor/html5lib/treebuilders/etree.py,sha256=aqIBOGj_dFYqBURIcTegGNBhAIJOw5iFDHb4jrkYH-8,12764 -pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=9V0dXxbJYYq-Skgb5-_OL2NkVYpjioEb4CHajo0e9yI,14122 -pip/_vendor/html5lib/treewalkers/__init__.py,sha256=yhXxHpjlSqfQyUag3v8-vWjMPriFBU8YRAPNpDgBTn8,5714 -pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-37.pyc,, -pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-37.pyc,, -pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-37.pyc,, -pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-37.pyc,, -pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-37.pyc,, -pip/_vendor/html5lib/treewalkers/base.py,sha256=ouiOsuSzvI0KgzdWP8PlxIaSNs9falhbiinAEc_UIJY,7476 -pip/_vendor/html5lib/treewalkers/dom.py,sha256=EHyFR8D8lYNnyDU9lx_IKigVJRyecUGua0mOi7HBukc,1413 -pip/_vendor/html5lib/treewalkers/etree.py,sha256=sz1o6mmE93NQ53qJFDO7HKyDtuwgK-Ay3qSFZPC6u00,4550 -pip/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=sY6wfRshWTllu6n48TPWpKsQRPp-0CQrT0hj_AdzHSU,6309 -pip/_vendor/html5lib/treewalkers/genshi.py,sha256=4D2PECZ5n3ZN3qu3jMl9yY7B81jnQApBQSVlfaIuYbA,2309 -pip/_vendor/idna/__init__.py,sha256=9Nt7xpyet3DmOrPUGooDdAwmHZZu1qUAy2EaJ93kGiQ,58 -pip/_vendor/idna/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/codec.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/compat.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/core.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/idnadata.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/intranges.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/package_data.cpython-37.pyc,, -pip/_vendor/idna/__pycache__/uts46data.cpython-37.pyc,, -pip/_vendor/idna/codec.py,sha256=lvYb7yu7PhAqFaAIAdWcwgaWI2UmgseUua-1c0AsG0A,3299 -pip/_vendor/idna/compat.py,sha256=R-h29D-6mrnJzbXxymrWUW7iZUvy-26TQwZ0ij57i4U,232 -pip/_vendor/idna/core.py,sha256=Hy1RkJrrIQWW8kicqZ8vQT_GreYlAhkfopjESSzL3wk,11844 -pip/_vendor/idna/idnadata.py,sha256=p1_KeD9BqT-sDGqMcGxhBWAOrYNrPxj5YvHya0ImFbU,41201 -pip/_vendor/idna/intranges.py,sha256=TY1lpxZIQWEP6tNqjZkFA5hgoMWOj1OBmnUG8ihT87E,1749 -pip/_vendor/idna/package_data.py,sha256=IjspS_rQQ_0HCGc0CaNhn3NXl3ohvRg7-_P0gAaSc-o,21 -pip/_vendor/idna/uts46data.py,sha256=w9d1B5OESLSgr2tMx0svwoPBi0Qj0_7HRyL1Vq5axwg,201192 -pip/_vendor/ipaddress.py,sha256=-0RmurI31XgAaN20WCi0zrcuoat90nNA70_6yGlx2PU,79875 -pip/_vendor/msgpack/__init__.py,sha256=2gJwcsTIaAtCM0GMi2rU-_Y6kILeeQuqRkrQ22jSANc,1118 -pip/_vendor/msgpack/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/msgpack/__pycache__/_version.cpython-37.pyc,, -pip/_vendor/msgpack/__pycache__/exceptions.cpython-37.pyc,, -pip/_vendor/msgpack/__pycache__/ext.cpython-37.pyc,, -pip/_vendor/msgpack/__pycache__/fallback.cpython-37.pyc,, -pip/_vendor/msgpack/_version.py,sha256=hu7lzmZ_ClOaOOmRsWb4xomhzQ4UIsLsvv8KY6UysHE,20 -pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081 -pip/_vendor/msgpack/ext.py,sha256=nV19BzE9Be8SJHrxxYJHFbvEHJaXcP3avRkHVp5wovM,6034 -pip/_vendor/msgpack/fallback.py,sha256=Z8V3iYUUPqKVy4WWTk64Vq3G0PylQIOmlWvgnMhmkdU,37133 -pip/_vendor/packaging/__about__.py,sha256=y-K51xPSysxvOfTjAb074yqOZfDeX5qSID0ZEbEb9cE,744 -pip/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 -pip/_vendor/packaging/__pycache__/__about__.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/_compat.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/_structures.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/_typing.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/markers.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/requirements.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/tags.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/utils.cpython-37.pyc,, -pip/_vendor/packaging/__pycache__/version.cpython-37.pyc,, -pip/_vendor/packaging/_compat.py,sha256=Z-PwchK0cREbaRGF5MZP8LEv8JkC-qydn2FRrtjeixk,1138 -pip/_vendor/packaging/_structures.py,sha256=ozkCX8Q8f2qE1Eic3YiQ4buDVfgz2iYevY9e7R2y3iY,2022 -pip/_vendor/packaging/_typing.py,sha256=-cq_iNeveAWCVoseVvqmknWLbvZ_i9g7BeZBo0ShtHg,1449 -pip/_vendor/packaging/markers.py,sha256=yap5bk3c8QyPuGtiVbQSYhN70bxWj1nLDv2ZuaCLq7g,9501 -pip/_vendor/packaging/requirements.py,sha256=G43p2ylM_REg87RLG9JybjbdwfaPyzaKYRtllRfNdrM,4913 -pip/_vendor/packaging/specifiers.py,sha256=Nz8bnFp53cQInmRGZy50QXlIi2tkDXMfRuGyGps2IRE,31314 -pip/_vendor/packaging/tags.py,sha256=SCrw-jC3h0ymam6QXDX5ZqgvRcMNq_cQD55gFnT56Xg,23704 -pip/_vendor/packaging/utils.py,sha256=v5Wk8B7gUL13Rzed6NNhCZlutPQT7jNV-7hr-WOtacU,1700 -pip/_vendor/packaging/version.py,sha256=qRdNN0_XuPFOJ3fut8ehzxJrNYtBzqF8ZtagEvgNUUM,15480 -pip/_vendor/pep517/__init__.py,sha256=r5uA106NGJa3slspaD2m32aFpFUiZX-mZ9vIlzAEOp4,84 -pip/_vendor/pep517/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/_in_process.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/build.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/check.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/colorlog.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/compat.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/dirtools.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/envbuild.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/meta.cpython-37.pyc,, -pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pyc,, -pip/_vendor/pep517/_in_process.py,sha256=XrKOTURJdia5R7i3i_OQmS89LASFXE3HQXfX63qZBIE,8438 -pip/_vendor/pep517/build.py,sha256=DN4ouyj_bd00knOKqv0KHRtN0-JezJoNNZQmcDi4juk,3335 -pip/_vendor/pep517/check.py,sha256=YoaNE3poJGpz96biVCYwtcDshwEGE2HRU5KKya9yfpY,5961 -pip/_vendor/pep517/colorlog.py,sha256=Tk9AuYm_cLF3BKTBoSTJt9bRryn0aFojIQOwbfVUTxQ,4098 -pip/_vendor/pep517/compat.py,sha256=M-5s4VNp8rjyT76ZZ_ibnPD44DYVzSQlyCEHayjtDPw,780 -pip/_vendor/pep517/dirtools.py,sha256=2mkAkAL0mRz_elYFjRKuekTJVipH1zTn4tbf1EDev84,1129 -pip/_vendor/pep517/envbuild.py,sha256=szKUFlO50X1ahQfXwz4hD9V2VE_bz9MLVPIeidsFo4w,6041 -pip/_vendor/pep517/meta.py,sha256=8mnM5lDnT4zXQpBTliJbRGfesH7iioHwozbDxALPS9Y,2463 -pip/_vendor/pep517/wrappers.py,sha256=yFU4Lp7TIYbmuVOTY-pXnlyGZ3F_grIi-JlLkpGN8Gk,10783 -pip/_vendor/pkg_resources/__init__.py,sha256=XpGBfvS9fafA6bm5rx7vnxdxs7yqyoc_NnpzKApkJ64,108277 -pip/_vendor/pkg_resources/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-37.pyc,, -pip/_vendor/pkg_resources/py31compat.py,sha256=CRk8fkiPRDLsbi5pZcKsHI__Pbmh_94L8mr9Qy9Ab2U,562 -pip/_vendor/progress/__init__.py,sha256=fcbQQXo5np2CoQyhSH5XprkicwLZNLePR3uIahznSO0,4857 -pip/_vendor/progress/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/progress/__pycache__/bar.cpython-37.pyc,, -pip/_vendor/progress/__pycache__/counter.cpython-37.pyc,, -pip/_vendor/progress/__pycache__/spinner.cpython-37.pyc,, -pip/_vendor/progress/bar.py,sha256=QuDuVNcmXgpxtNtxO0Fq72xKigxABaVmxYGBw4J3Z_E,2854 -pip/_vendor/progress/counter.py,sha256=MznyBrvPWrOlGe4MZAlGUb9q3aODe6_aNYeAE_VNoYA,1372 -pip/_vendor/progress/spinner.py,sha256=k8JbDW94T0-WXuXfxZIFhdoNPYp3jfnpXqBnfRv5fGs,1380 -pip/_vendor/pyparsing.py,sha256=J1b4z3S_KwyJW7hKGnoN-hXW9pgMIzIP6QThyY5yJq4,273394 -pip/_vendor/requests/__init__.py,sha256=DoS7sn6SOs-Vmi3IHdJFfM1RmlONZHmEKO3DGvyWsnY,4080 -pip/_vendor/requests/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/__version__.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/_internal_utils.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/adapters.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/api.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/auth.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/certs.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/compat.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/cookies.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/exceptions.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/help.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/hooks.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/models.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/packages.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/sessions.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/status_codes.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/structures.cpython-37.pyc,, -pip/_vendor/requests/__pycache__/utils.cpython-37.pyc,, -pip/_vendor/requests/__version__.py,sha256=dpcXABdGo9y3UFFKFU_Wu_YHSa7TBXxCghOju7S8IYs,441 -pip/_vendor/requests/_internal_utils.py,sha256=Zx3PnEUccyfsB-ie11nZVAW8qClJy0gx1qNME7rgT18,1096 -pip/_vendor/requests/adapters.py,sha256=e-bmKEApNVqFdylxuMJJfiaHdlmS_zhWhIMEzlHvGuc,21548 -pip/_vendor/requests/api.py,sha256=PlHM-HT3PQ5lyufoeGmV-nJxRi7UnUyGVh7OV7B9XV4,6496 -pip/_vendor/requests/auth.py,sha256=OMoJIVKyRLy9THr91y8rxysZuclwPB-K1Xg1zBomUhQ,10207 -pip/_vendor/requests/certs.py,sha256=nXRVq9DtGmv_1AYbwjTu9UrgAcdJv05ZvkNeaoLOZxY,465 -pip/_vendor/requests/compat.py,sha256=LQWuCR4qXk6w7-qQopXyz0WNHUdAD40k0mKnaAEf1-g,2045 -pip/_vendor/requests/cookies.py,sha256=Y-bKX6TvW3FnYlE6Au0SXtVVWcaNdFvuAwQxw-G0iTI,18430 -pip/_vendor/requests/exceptions.py,sha256=-mLam3TAx80V09EaH3H-ZxR61eAVuLRZ8zgBBSLjK44,3197 -pip/_vendor/requests/help.py,sha256=SJPVcoXeo7KfK4AxJN5eFVQCjr0im87tU2n7ubLsksU,3578 -pip/_vendor/requests/hooks.py,sha256=QReGyy0bRcr5rkwCuObNakbYsc7EkiKeBwG4qHekr2Q,757 -pip/_vendor/requests/models.py,sha256=P7sUBpjV7iIzjpf9rYVzHaOXlxlzJk5ikFP_Nsg6zAU,34278 -pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695 -pip/_vendor/requests/sessions.py,sha256=Ju8VnlWZPU_Xr-cMjKXNbs_l2cyanr3Dm9c7fLxprQI,29265 -pip/_vendor/requests/status_codes.py,sha256=gT79Pbs_cQjBgp-fvrUgg1dn2DQO32bDj4TInjnMPSc,4188 -pip/_vendor/requests/structures.py,sha256=msAtr9mq1JxHd-JRyiILfdFlpbJwvvFuP3rfUQT_QxE,3005 -pip/_vendor/requests/utils.py,sha256=VBs99cvV8Z29WGXeWZqHzZ80_nu1AwwjYzJfe0wQIvs,30176 -pip/_vendor/resolvelib/__init__.py,sha256=aBndiGQ3I68Ezdv0fMPQ9ek6ScvwpuQRimxn6wp7pJ4,537 -pip/_vendor/resolvelib/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/resolvelib/__pycache__/providers.cpython-37.pyc,, -pip/_vendor/resolvelib/__pycache__/reporters.cpython-37.pyc,, -pip/_vendor/resolvelib/__pycache__/resolvers.cpython-37.pyc,, -pip/_vendor/resolvelib/__pycache__/structs.cpython-37.pyc,, -pip/_vendor/resolvelib/providers.py,sha256=1JCBbBV0E_Q1o0XOAeAdr2YaKU_zHxO7i_BBLqCLhUc,4913 -pip/_vendor/resolvelib/reporters.py,sha256=5gXUn3hRjA4UomD3HHZGreBp23aSjMHgBrC3OgYSpTY,1094 -pip/_vendor/resolvelib/resolvers.py,sha256=nTTfBBIMsoxGhqUAyh8GbmR-si_TijpY67wD7aUS264,14481 -pip/_vendor/resolvelib/structs.py,sha256=yrdhd-n7DercimPGclXe20rgqhlxw8PnxC0wmcXO19Y,2016 -pip/_vendor/retrying.py,sha256=k3fflf5_Mm0XcIJYhB7Tj34bqCCPhUDkYbx1NvW2FPE,9972 -pip/_vendor/six.py,sha256=Q6WvEXZ1DGEASAo3CGNCJkKv2tPy8xkSmK-VHE9PYIA,34074 -pip/_vendor/toml.py,sha256=D8pTQbgSGge2nDdpsAk8_C7UGJeva0qFq2tGGNaE70Q,35675 -pip/_vendor/toml/__init__.py,sha256=CwtvzaEThoIbqNwJJB8BhHoA5clb2_x8V42UF6JAysA,527 -pip/_vendor/toml/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/toml/__pycache__/decoder.cpython-37.pyc,, -pip/_vendor/toml/__pycache__/encoder.cpython-37.pyc,, -pip/_vendor/toml/__pycache__/ordered.cpython-37.pyc,, -pip/_vendor/toml/__pycache__/tz.cpython-37.pyc,, -pip/_vendor/toml/decoder.py,sha256=8uIcWLLyMFwBGT3fiph98KDx8uF_V2Pf84Qi43pqNf0,35067 -pip/_vendor/toml/encoder.py,sha256=Nw1wrNgcUgod1XbOXwxFSDGqO5blgOi07Lp_6kStTgc,8128 -pip/_vendor/toml/ordered.py,sha256=UWt5Eka90IWVBYdvLgY5PXnkBcVYpHjnw9T67rM85T8,378 -pip/_vendor/toml/tz.py,sha256=DrAgI3wZxZiGcLuV_l8ueA_nPrYoxQ3hZA9tJSjWRsQ,618 -pip/_vendor/urllib3/__init__.py,sha256=oVOeFuNyEg33ShIefD4qSeGvbb-aJ3otjcdX2iswkLM,2683 -pip/_vendor/urllib3/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/_collections.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/connection.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/connectionpool.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/exceptions.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/fields.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/filepost.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/poolmanager.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/request.cpython-37.pyc,, -pip/_vendor/urllib3/__pycache__/response.cpython-37.pyc,, -pip/_vendor/urllib3/_collections.py,sha256=GouVsNzwg6jADZTmimMI6oqmwKSswnMo9dh5tGNVWO4,10792 -pip/_vendor/urllib3/connection.py,sha256=wHpV1S60bvXWnqEDtdzmHiY9NwIiKeFwQI5VvX7AtqM,14026 -pip/_vendor/urllib3/connectionpool.py,sha256=jg_yTHo2B3Zyo4urYVecyg-34v3hxUrH9B401-c4A9w,36513 -pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/_appengine_environ.py,sha256=bDbyOEhW2CKLJcQqAKAyrEHN-aklsyHFKq6vF8ZFsmk,957 -pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-37.pyc,, -pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=mullWYFaghBdRWla6HYU-TBgFRTPLBEfxj3jplbeJmQ,16886 -pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=V7GnujxnWZh2N2sMsV5N4d9Imymokkm3zBwgt77_bSE,11956 -pip/_vendor/urllib3/contrib/appengine.py,sha256=gfdK4T7CRin7v9HRhHDbDh-Hbk66hHDWeoz7nV3PJo8,11034 -pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=a402AwGN_Ll3N-4ur_AS6UrU-ycUtlnYqoBF76lORg8,4160 -pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=w35mWy_1POZUsbOhurVb_zhf0C1Jkd79AFlucLs6KuQ,16440 -pip/_vendor/urllib3/contrib/securetransport.py,sha256=iKzVUAxKnChsADR5YMwc05oEixXDzAk0xPU0g-rc2z8,32275 -pip/_vendor/urllib3/contrib/socks.py,sha256=nzDMgDIFJWVubKHqvIn2-SKCO91hhJInP92WgHChGzA,7036 -pip/_vendor/urllib3/exceptions.py,sha256=P3e-p9_LScyIxX7FoR3wU0A6hZmDqFAVCz2wgI3D0lM,6607 -pip/_vendor/urllib3/fields.py,sha256=kroD76QK-GdHHW7f_AUN4XxDC3OQPI2FFrS9eSL4BCs,8553 -pip/_vendor/urllib3/filepost.py,sha256=vj0qbrpT1AFzvvW4SuC8M5kJiw7wftHcSr-7b8UpPpw,2440 -pip/_vendor/urllib3/packages/__init__.py,sha256=h4BLhD4tLaBx1adaDtKXfupsgqY0wWLXb_f1_yVlV6A,108 -pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/packages/__pycache__/six.cpython-37.pyc,, -pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-37.pyc,, -pip/_vendor/urllib3/packages/backports/makefile.py,sha256=005wrvH-_pWSnTFqQ2sdzzh4zVCtQUUQ4mR2Yyxwc0A,1418 -pip/_vendor/urllib3/packages/six.py,sha256=adx4z-eM_D0Vvu0IIqVzFACQ_ux9l64y7DkSEfbxCDs,32536 -pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py,sha256=ywgKMtfHi1-DrXlzPfVAhzsLzzqcK7GT6eLgdode1Fg,688 -pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-37.pyc,, -pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=rvQDQviqQLtPJB6MfEgABnBFj3nXft7ZJ3Dx-BC0AQY,5696 -pip/_vendor/urllib3/poolmanager.py,sha256=JYUyBUN3IiEknUdjZ7VJrpCQr6SP7vi0WwSndrn8XpE,17053 -pip/_vendor/urllib3/request.py,sha256=hhoHvEEatyd9Tn5EbGjQ0emn-ENMCyY591yNWTneINA,6018 -pip/_vendor/urllib3/response.py,sha256=5OVDLR6ss88mak30ZhDaSqHxXbbAQ7CIUrktI7B5aPA,27833 -pip/_vendor/urllib3/util/__init__.py,sha256=bWNaav_OT-1L7-sxm59cGb59rDORlbhb_4noduM5m0U,1038 -pip/_vendor/urllib3/util/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/connection.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/queue.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/request.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/response.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/retry.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/timeout.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/url.cpython-37.pyc,, -pip/_vendor/urllib3/util/__pycache__/wait.cpython-37.pyc,, -pip/_vendor/urllib3/util/connection.py,sha256=NsxUAKQ98GKywta--zg57CdVpeTCI6N-GElCq78Dl8U,4637 -pip/_vendor/urllib3/util/queue.py,sha256=myTX3JDHntglKQNBf3b6dasHH-uF-W59vzGSQiFdAfI,497 -pip/_vendor/urllib3/util/request.py,sha256=C-6-AWffxZG03AdRGoY59uqsn4CVItKU6gjxz7Hc3Mc,3815 -pip/_vendor/urllib3/util/response.py,sha256=_WbTQr8xRQuJuY2rTIZxVdJD6mnEOtQupjaK_bF_Vj8,2573 -pip/_vendor/urllib3/util/retry.py,sha256=Ui74h44gLIIWkAxT9SK3A2mEvu55-odWgJMw3LiUNGk,15450 -pip/_vendor/urllib3/util/ssl_.py,sha256=W52OyrqLO1IQ5kUgzM8fLAO-EgJxuftd5nhcTOD54ew,14167 -pip/_vendor/urllib3/util/timeout.py,sha256=bCtaS_xVKaTDJ5VMlroXBfCnPUDNVGZqik7-z83issg,9871 -pip/_vendor/urllib3/util/url.py,sha256=6cDyd46labP8STbF0vieWygu2xVhKkgcF1h3tc6HYno,13979 -pip/_vendor/urllib3/util/wait.py,sha256=k46KzqIYu3Vnzla5YW3EvtInNlU_QycFqQAghIOxoAg,5406 -pip/_vendor/vendor.txt,sha256=ZewT4rljE_Ct9eJsAk0HhDlKSHaguTqeBSGlF1zIgss,440 -pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579 -pip/_vendor/webencodings/__pycache__/__init__.cpython-37.pyc,, -pip/_vendor/webencodings/__pycache__/labels.cpython-37.pyc,, -pip/_vendor/webencodings/__pycache__/mklabels.cpython-37.pyc,, -pip/_vendor/webencodings/__pycache__/tests.cpython-37.pyc,, -pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-37.pyc,, -pip/_vendor/webencodings/labels.py,sha256=4AO_KxTddqGtrL9ns7kAPjb0CcN6xsCIxbK37HY9r3E,8979 -pip/_vendor/webencodings/mklabels.py,sha256=GYIeywnpaLnP0GSic8LFWgd0UVvO_l1Nc6YoF-87R_4,1305 -pip/_vendor/webencodings/tests.py,sha256=OtGLyjhNY1fvkW1GvLJ_FV9ZoqC9Anyjr7q3kxTbzNs,6563 -pip/_vendor/webencodings/x_user_defined.py,sha256=yOqWSdmpytGfUgh_Z6JYgDNhoc-BAHyyeeT15Fr42tM,4307 diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/WHEEL b/venv/Lib/site-packages/pip-20.1.1.dist-info/WHEEL deleted file mode 100644 index ef99c6cf3283b50a273ac4c6d009a0aa85597070..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/entry_points.txt b/venv/Lib/site-packages/pip-20.1.1.dist-info/entry_points.txt deleted file mode 100644 index d48bd8a85e683c7a9607f3f418f50d11445bdf40..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/entry_points.txt +++ /dev/null @@ -1,5 +0,0 @@ -[console_scripts] -pip = pip._internal.cli.main:main -pip3 = pip._internal.cli.main:main -pip3.8 = pip._internal.cli.main:main - diff --git a/venv/Lib/site-packages/pip-20.1.1.dist-info/top_level.txt b/venv/Lib/site-packages/pip-20.1.1.dist-info/top_level.txt deleted file mode 100644 index a1b589e38a32041e49332e5e81c2d363dc418d68..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip-20.1.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/pip/__init__.py b/venv/Lib/site-packages/pip/__init__.py index 3dcf3a9deea405e67bbdb6ce6f4a593c668fc250..acead997652694cce86fec4ed4d4fb10f66dfb42 100644 --- a/venv/Lib/site-packages/pip/__init__.py +++ b/venv/Lib/site-packages/pip/__init__.py @@ -1,14 +1,9 @@ -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from typing import List, Optional -if MYPY_CHECK_RUNNING: - from typing import List, Optional +__version__ = "21.3.1" -__version__ = "20.1.1" - - -def main(args=None): - # type: (Optional[List[str]]) -> int +def main(args: Optional[List[str]] = None) -> int: """This is an internal API only meant for use by pip's own console scripts. For additional details, see https://github.com/pypa/pip/issues/7498. diff --git a/venv/Lib/site-packages/pip/__main__.py b/venv/Lib/site-packages/pip/__main__.py index 7c2505fa5bd3e8ad7da3a5c500dcf76cf1eb3810..fe34a7b7772cef55f5b5cb3455a2850489620ca7 100644 --- a/venv/Lib/site-packages/pip/__main__.py +++ b/venv/Lib/site-packages/pip/__main__.py @@ -1,18 +1,17 @@ -from __future__ import absolute_import - import os import sys +import warnings # Remove '' and current working directory from the first entry # of sys.path, if present to avoid using current directory # in pip commands check, freeze, install, list and show, # when invoked as python -m pip -if sys.path[0] in ('', os.getcwd()): +if sys.path[0] in ("", os.getcwd()): sys.path.pop(0) # If we are running from a wheel, add the wheel to sys.path # This allows the usage python pip-*.whl/pip install pip-*.whl -if __package__ == '': +if __package__ == "": # __file__ is pip-*.whl/pip/__main__.py # first dirname call strips of '/__main__.py', second strips off '/pip' # Resulting path is the name of the wheel itself @@ -20,7 +19,13 @@ if __package__ == '': path = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, path) -from pip._internal.cli.main import main as _main # isort:skip # noqa +if __name__ == "__main__": + # Work around the error reported in #9540, pending a proper fix. + # Note: It is essential the warning filter is set *before* importing + # pip, as the deprecation happens at import time, not runtime. + warnings.filterwarnings( + "ignore", category=DeprecationWarning, module=".*packaging\\.version" + ) + from pip._internal.cli.main import main as _main -if __name__ == '__main__': sys.exit(_main()) diff --git a/venv/Lib/site-packages/pip/_internal/__init__.py b/venv/Lib/site-packages/pip/_internal/__init__.py index 264c2cab88dd0b414be6d33b43ae1cfbb19489ac..6afb5c627ce3db6e61cbf46276f7ddd42552eb28 100644 --- a/venv/Lib/site-packages/pip/_internal/__init__.py +++ b/venv/Lib/site-packages/pip/_internal/__init__.py @@ -1,12 +1,14 @@ +from typing import List, Optional + import pip._internal.utils.inject_securetransport # noqa -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils import _log -if MYPY_CHECK_RUNNING: - from typing import Optional, List +# init_logging() must be called before any call to logging.getLogger() +# which happens at import of most modules. +_log.init_logging() -def main(args=None): - # type: (Optional[List[str]]) -> int +def main(args: (Optional[List[str]]) = None) -> int: """This is preserved for old console scripts that may still be referencing it. diff --git a/venv/Lib/site-packages/pip/_internal/build_env.py b/venv/Lib/site-packages/pip/_internal/build_env.py index b8f005f5ca9453eb5a533973ca8e5de7f60ac61e..8faf1cf7e8415561c30e67647f2f0d46d81eeada 100644 --- a/venv/Lib/site-packages/pip/_internal/build_env.py +++ b/venv/Lib/site-packages/pip/_internal/build_env.py @@ -1,70 +1,85 @@ """Build Environment used for isolation during sdist building """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - +import contextlib import logging import os +import pathlib import sys import textwrap +import zipfile from collections import OrderedDict -from distutils.sysconfig import get_python_lib from sysconfig import get_paths +from types import TracebackType +from typing import TYPE_CHECKING, Iterable, Iterator, List, Optional, Set, Tuple, Type -from pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet +from pip._vendor.certifi import where +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.version import Version from pip import __file__ as pip_location from pip._internal.cli.spinners import open_spinner +from pip._internal.locations import get_platlib, get_prefixed_libs, get_purelib +from pip._internal.metadata import get_environment from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Tuple, Set, Iterable, Optional, List +if TYPE_CHECKING: from pip._internal.index.package_finder import PackageFinder logger = logging.getLogger(__name__) class _Prefix: - - def __init__(self, path): - # type: (str) -> None + def __init__(self, path: str) -> None: self.path = path self.setup = False self.bin_dir = get_paths( - 'nt' if os.name == 'nt' else 'posix_prefix', - vars={'base': path, 'platbase': path} - )['scripts'] - # Note: prefer distutils' sysconfig to get the - # library paths so PyPy is correctly supported. - purelib = get_python_lib(plat_specific=False, prefix=path) - platlib = get_python_lib(plat_specific=True, prefix=path) - if purelib == platlib: - self.lib_dirs = [purelib] - else: - self.lib_dirs = [purelib, platlib] + "nt" if os.name == "nt" else "posix_prefix", + vars={"base": path, "platbase": path}, + )["scripts"] + self.lib_dirs = get_prefixed_libs(path) -class BuildEnvironment(object): - """Creates and manages an isolated environment to install build deps +@contextlib.contextmanager +def _create_standalone_pip() -> Iterator[str]: + """Create a "standalone pip" zip file. + + The zip file's content is identical to the currently-running pip. + It will be used to install requirements into the build environment. """ + source = pathlib.Path(pip_location).resolve().parent - def __init__(self): - # type: () -> None - temp_dir = TempDirectory( - kind=tempdir_kinds.BUILD_ENV, globally_managed=True - ) + # Return the current instance if `source` is not a directory. We can't build + # a zip from this, and it likely means the instance is already standalone. + if not source.is_dir(): + yield str(source) + return + + with TempDirectory(kind="standalone-pip") as tmp_dir: + pip_zip = os.path.join(tmp_dir.path, "__env_pip__.zip") + kwargs = {} + if sys.version_info >= (3, 8): + kwargs["strict_timestamps"] = False + with zipfile.ZipFile(pip_zip, "w", **kwargs) as zf: + for child in source.rglob("*"): + zf.write(child, child.relative_to(source.parent).as_posix()) + yield os.path.join(pip_zip, "pip") - self._prefixes = OrderedDict(( + +class BuildEnvironment: + """Creates and manages an isolated environment to install build deps""" + + def __init__(self) -> None: + temp_dir = TempDirectory(kind=tempdir_kinds.BUILD_ENV, globally_managed=True) + + self._prefixes = OrderedDict( (name, _Prefix(os.path.join(temp_dir.path, name))) - for name in ('normal', 'overlay') - )) + for name in ("normal", "overlay") + ) - self._bin_dirs = [] # type: List[str] - self._lib_dirs = [] # type: List[str] + self._bin_dirs: List[str] = [] + self._lib_dirs: List[str] = [] for prefix in reversed(list(self._prefixes.values())): self._bin_dirs.append(prefix.bin_dir) self._lib_dirs.extend(prefix.lib_dirs) @@ -73,17 +88,15 @@ class BuildEnvironment(object): # - ensure .pth files are honored # - prevent access to system site packages system_sites = { - os.path.normcase(site) for site in ( - get_python_lib(plat_specific=False), - get_python_lib(plat_specific=True), - ) + os.path.normcase(site) for site in (get_purelib(), get_platlib()) } - self._site_dir = os.path.join(temp_dir.path, 'site') + self._site_dir = os.path.join(temp_dir.path, "site") if not os.path.exists(self._site_dir): os.mkdir(self._site_dir) - with open(os.path.join(self._site_dir, 'sitecustomize.py'), 'w') as fp: - fp.write(textwrap.dedent( - ''' + with open(os.path.join(self._site_dir, "sitecustomize.py"), "w") as fp: + fp.write( + textwrap.dedent( + """ import os, site, sys # First, drop system-sites related paths. @@ -106,114 +119,175 @@ class BuildEnvironment(object): for path in {lib_dirs!r}: assert not path in sys.path site.addsitedir(path) - ''' - ).format(system_sites=system_sites, lib_dirs=self._lib_dirs)) + """ + ).format(system_sites=system_sites, lib_dirs=self._lib_dirs) + ) - def __enter__(self): + def __enter__(self) -> None: self._save_env = { name: os.environ.get(name, None) - for name in ('PATH', 'PYTHONNOUSERSITE', 'PYTHONPATH') + for name in ("PATH", "PYTHONNOUSERSITE", "PYTHONPATH") } path = self._bin_dirs[:] - old_path = self._save_env['PATH'] + old_path = self._save_env["PATH"] if old_path: path.extend(old_path.split(os.pathsep)) pythonpath = [self._site_dir] - os.environ.update({ - 'PATH': os.pathsep.join(path), - 'PYTHONNOUSERSITE': '1', - 'PYTHONPATH': os.pathsep.join(pythonpath), - }) + os.environ.update( + { + "PATH": os.pathsep.join(path), + "PYTHONNOUSERSITE": "1", + "PYTHONPATH": os.pathsep.join(pythonpath), + } + ) - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: for varname, old_value in self._save_env.items(): if old_value is None: os.environ.pop(varname, None) else: os.environ[varname] = old_value - def check_requirements(self, reqs): - # type: (Iterable[str]) -> Tuple[Set[Tuple[str, str]], Set[str]] + def check_requirements( + self, reqs: Iterable[str] + ) -> Tuple[Set[Tuple[str, str]], Set[str]]: """Return 2 sets: - - conflicting requirements: set of (installed, wanted) reqs tuples - - missing requirements: set of reqs + - conflicting requirements: set of (installed, wanted) reqs tuples + - missing requirements: set of reqs """ missing = set() conflicting = set() if reqs: - ws = WorkingSet(self._lib_dirs) - for req in reqs: - try: - if ws.find(Requirement.parse(req)) is None: - missing.add(req) - except VersionConflict as e: - conflicting.add((str(e.args[0].as_requirement()), - str(e.args[1]))) + env = get_environment(self._lib_dirs) + for req_str in reqs: + req = Requirement(req_str) + dist = env.get_distribution(req.name) + if not dist: + missing.add(req_str) + continue + if isinstance(dist.version, Version): + installed_req_str = f"{req.name}=={dist.version}" + else: + installed_req_str = f"{req.name}==={dist.version}" + if dist.version not in req.specifier: + conflicting.add((installed_req_str, req_str)) + # FIXME: Consider direct URL? return conflicting, missing def install_requirements( self, - finder, # type: PackageFinder - requirements, # type: Iterable[str] - prefix_as_string, # type: str - message # type: Optional[str] - ): - # type: (...) -> None + finder: "PackageFinder", + requirements: Iterable[str], + prefix_as_string: str, + message: str, + ) -> None: prefix = self._prefixes[prefix_as_string] assert not prefix.setup prefix.setup = True if not requirements: return - args = [ - sys.executable, os.path.dirname(pip_location), 'install', - '--ignore-installed', '--no-user', '--prefix', prefix.path, - '--no-warn-script-location', - ] # type: List[str] + with contextlib.ExitStack() as ctx: + # TODO: Remove this block when dropping 3.6 support. Python 3.6 + # lacks importlib.resources and pep517 has issues loading files in + # a zip, so we fallback to the "old" method by adding the current + # pip directory to the child process's sys.path. + if sys.version_info < (3, 7): + pip_runnable = os.path.dirname(pip_location) + else: + pip_runnable = ctx.enter_context(_create_standalone_pip()) + self._install_requirements( + pip_runnable, + finder, + requirements, + prefix, + message, + ) + + @staticmethod + def _install_requirements( + pip_runnable: str, + finder: "PackageFinder", + requirements: Iterable[str], + prefix: _Prefix, + message: str, + ) -> None: + args: List[str] = [ + sys.executable, + pip_runnable, + "install", + "--ignore-installed", + "--no-user", + "--prefix", + prefix.path, + "--no-warn-script-location", + ] if logger.getEffectiveLevel() <= logging.DEBUG: - args.append('-v') - for format_control in ('no_binary', 'only_binary'): + args.append("-v") + for format_control in ("no_binary", "only_binary"): formats = getattr(finder.format_control, format_control) - args.extend(('--' + format_control.replace('_', '-'), - ','.join(sorted(formats or {':none:'})))) + args.extend( + ( + "--" + format_control.replace("_", "-"), + ",".join(sorted(formats or {":none:"})), + ) + ) index_urls = finder.index_urls if index_urls: - args.extend(['-i', index_urls[0]]) + args.extend(["-i", index_urls[0]]) for extra_index in index_urls[1:]: - args.extend(['--extra-index-url', extra_index]) + args.extend(["--extra-index-url", extra_index]) else: - args.append('--no-index') + args.append("--no-index") for link in finder.find_links: - args.extend(['--find-links', link]) + args.extend(["--find-links", link]) for host in finder.trusted_hosts: - args.extend(['--trusted-host', host]) + args.extend(["--trusted-host", host]) if finder.allow_all_prereleases: - args.append('--pre') - args.append('--') + args.append("--pre") + if finder.prefer_binary: + args.append("--prefer-binary") + args.append("--") args.extend(requirements) + extra_environ = {"_PIP_STANDALONE_CERT": where()} with open_spinner(message) as spinner: - call_subprocess(args, spinner=spinner) + call_subprocess(args, spinner=spinner, extra_environ=extra_environ) class NoOpBuildEnvironment(BuildEnvironment): - """A no-op drop-in replacement for BuildEnvironment - """ + """A no-op drop-in replacement for BuildEnvironment""" - def __init__(self): + def __init__(self) -> None: pass - def __enter__(self): + def __enter__(self) -> None: pass - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: pass - def cleanup(self): + def cleanup(self) -> None: pass - def install_requirements(self, finder, requirements, prefix, message): + def install_requirements( + self, + finder: "PackageFinder", + requirements: Iterable[str], + prefix_as_string: str, + message: str, + ) -> None: raise NotImplementedError() diff --git a/venv/Lib/site-packages/pip/_internal/cache.py b/venv/Lib/site-packages/pip/_internal/cache.py index b534f0cfec30e45318d0d60af1b2ee45a6690935..1d6df2201183a5786afbbcc96486e565ef90e5e0 100644 --- a/venv/Lib/site-packages/pip/_internal/cache.py +++ b/venv/Lib/site-packages/pip/_internal/cache.py @@ -1,55 +1,46 @@ """Cache Management """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import hashlib import json import logging import os +from typing import Any, Dict, List, Optional, Set -from pip._vendor.packaging.tags import interpreter_name, interpreter_version +from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version from pip._vendor.packaging.utils import canonicalize_name from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.models.format_control import FormatControl from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url -if MYPY_CHECK_RUNNING: - from typing import Optional, Set, List, Any, Dict - - from pip._vendor.packaging.tags import Tag - - from pip._internal.models.format_control import FormatControl - logger = logging.getLogger(__name__) -def _hash_dict(d): - # type: (Dict[str, str]) -> str +def _hash_dict(d: Dict[str, str]) -> str: """Return a stable sha224 of a dictionary.""" s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True) return hashlib.sha224(s.encode("ascii")).hexdigest() -class Cache(object): +class Cache: """An abstract class - provides cache directories for data from links - :param cache_dir: The root of the cache. - :param format_control: An object of FormatControl class to limit - binaries being read from the cache. - :param allowed_formats: which formats of files the cache should store. - ('binary' and 'source' are the only allowed values) + :param cache_dir: The root of the cache. + :param format_control: An object of FormatControl class to limit + binaries being read from the cache. + :param allowed_formats: which formats of files the cache should store. + ('binary' and 'source' are the only allowed values) """ - def __init__(self, cache_dir, format_control, allowed_formats): - # type: (str, FormatControl, Set[str]) -> None - super(Cache, self).__init__() + def __init__( + self, cache_dir: str, format_control: FormatControl, allowed_formats: Set[str] + ) -> None: + super().__init__() assert not cache_dir or os.path.isabs(cache_dir) self.cache_dir = cache_dir or None self.format_control = format_control @@ -58,38 +49,8 @@ class Cache(object): _valid_formats = {"source", "binary"} assert self.allowed_formats.union(_valid_formats) == _valid_formats - def _get_cache_path_parts_legacy(self, link): - # type: (Link) -> List[str] - """Get parts of part that must be os.path.joined with cache_dir - - Legacy cache key (pip < 20) for compatibility with older caches. - """ - - # We want to generate an url to use as our cache key, we don't want to - # just re-use the URL because it might have other items in the fragment - # and we don't care about those. - key_parts = [link.url_without_fragment] - if link.hash_name is not None and link.hash is not None: - key_parts.append("=".join([link.hash_name, link.hash])) - key_url = "#".join(key_parts) - - # Encode our key url with sha224, we'll use this because it has similar - # security properties to sha256, but with a shorter total output (and - # thus less secure). However the differences don't make a lot of - # difference for our use case here. - hashed = hashlib.sha224(key_url.encode()).hexdigest() - - # We want to nest the directories some to prevent having a ton of top - # level directories where we might run out of sub directories on some - # FS. - parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] - - return parts - - def _get_cache_path_parts(self, link): - # type: (Link) -> List[str] - """Get parts of part that must be os.path.joined with cache_dir - """ + def _get_cache_path_parts(self, link: Link) -> List[str]: + """Get parts of part that must be os.path.joined with cache_dir""" # We want to generate an url to use as our cache key, we don't want to # just re-use the URL because it might have other items in the fragment @@ -121,19 +82,12 @@ class Cache(object): return parts - def _get_candidates(self, link, canonical_package_name): - # type: (Link, Optional[str]) -> List[Any] - can_not_cache = ( - not self.cache_dir or - not canonical_package_name or - not link - ) + def _get_candidates(self, link: Link, canonical_package_name: str) -> List[Any]: + can_not_cache = not self.cache_dir or not canonical_package_name or not link if can_not_cache: return [] - formats = self.format_control.get_allowed_formats( - canonical_package_name - ) + formats = self.format_control.get_allowed_formats(canonical_package_name) if not self.allowed_formats.intersection(formats): return [] @@ -142,30 +96,18 @@ class Cache(object): if os.path.isdir(path): for candidate in os.listdir(path): candidates.append((candidate, path)) - # TODO remove legacy path lookup in pip>=21 - legacy_path = self.get_path_for_link_legacy(link) - if os.path.isdir(legacy_path): - for candidate in os.listdir(legacy_path): - candidates.append((candidate, legacy_path)) return candidates - def get_path_for_link_legacy(self, link): - # type: (Link) -> str - raise NotImplementedError() - - def get_path_for_link(self, link): - # type: (Link) -> str - """Return a directory to store cached items in for link. - """ + def get_path_for_link(self, link: Link) -> str: + """Return a directory to store cached items in for link.""" raise NotImplementedError() def get( self, - link, # type: Link - package_name, # type: Optional[str] - supported_tags, # type: List[Tag] - ): - # type: (...) -> Link + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Link: """Returns a link to a cached item if it exists, otherwise returns the passed link. """ @@ -173,22 +115,12 @@ class Cache(object): class SimpleWheelCache(Cache): - """A cache of wheels for future installs. - """ - - def __init__(self, cache_dir, format_control): - # type: (str, FormatControl) -> None - super(SimpleWheelCache, self).__init__( - cache_dir, format_control, {"binary"} - ) + """A cache of wheels for future installs.""" - def get_path_for_link_legacy(self, link): - # type: (Link) -> str - parts = self._get_cache_path_parts_legacy(link) - return os.path.join(self.cache_dir, "wheels", *parts) + def __init__(self, cache_dir: str, format_control: FormatControl) -> None: + super().__init__(cache_dir, format_control, {"binary"}) - def get_path_for_link(self, link): - # type: (Link) -> str + def get_path_for_link(self, link: Link) -> str: """Return a directory to store cached wheels for link Because there are M wheels for any one sdist, we provide a directory @@ -204,36 +136,34 @@ class SimpleWheelCache(Cache): :param link: The link of the sdist for which this will cache wheels. """ parts = self._get_cache_path_parts(link) - + assert self.cache_dir # Store wheels within the root cache_dir return os.path.join(self.cache_dir, "wheels", *parts) def get( self, - link, # type: Link - package_name, # type: Optional[str] - supported_tags, # type: List[Tag] - ): - # type: (...) -> Link + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Link: candidates = [] if not package_name: return link canonical_package_name = canonicalize_name(package_name) - for wheel_name, wheel_dir in self._get_candidates( - link, canonical_package_name - ): + for wheel_name, wheel_dir in self._get_candidates(link, canonical_package_name): try: wheel = Wheel(wheel_name) except InvalidWheelFilename: continue if canonicalize_name(wheel.name) != canonical_package_name: logger.debug( - "Ignoring cached wheel {} for {} as it " - "does not match the expected distribution name {}.".format( - wheel_name, link, package_name - ) + "Ignoring cached wheel %s for %s as it " + "does not match the expected distribution name %s.", + wheel_name, + link, + package_name, ) continue if not wheel.supported(supported_tags): @@ -255,26 +185,22 @@ class SimpleWheelCache(Cache): class EphemWheelCache(SimpleWheelCache): - """A SimpleWheelCache that creates it's own temporary cache directory - """ + """A SimpleWheelCache that creates it's own temporary cache directory""" - def __init__(self, format_control): - # type: (FormatControl) -> None + def __init__(self, format_control: FormatControl) -> None: self._temp_dir = TempDirectory( kind=tempdir_kinds.EPHEM_WHEEL_CACHE, globally_managed=True, ) - super(EphemWheelCache, self).__init__( - self._temp_dir.path, format_control - ) + super().__init__(self._temp_dir.path, format_control) -class CacheEntry(object): +class CacheEntry: def __init__( self, - link, # type: Link - persistent, # type: bool + link: Link, + persistent: bool, ): self.link = link self.persistent = persistent @@ -287,33 +213,23 @@ class WheelCache(Cache): when a certain link is not found in the simple wheel cache first. """ - def __init__(self, cache_dir, format_control): - # type: (str, FormatControl) -> None - super(WheelCache, self).__init__( - cache_dir, format_control, {'binary'} - ) + def __init__(self, cache_dir: str, format_control: FormatControl) -> None: + super().__init__(cache_dir, format_control, {"binary"}) self._wheel_cache = SimpleWheelCache(cache_dir, format_control) self._ephem_cache = EphemWheelCache(format_control) - def get_path_for_link_legacy(self, link): - # type: (Link) -> str - return self._wheel_cache.get_path_for_link_legacy(link) - - def get_path_for_link(self, link): - # type: (Link) -> str + def get_path_for_link(self, link: Link) -> str: return self._wheel_cache.get_path_for_link(link) - def get_ephem_path_for_link(self, link): - # type: (Link) -> str + def get_ephem_path_for_link(self, link: Link) -> str: return self._ephem_cache.get_path_for_link(link) def get( self, - link, # type: Link - package_name, # type: Optional[str] - supported_tags, # type: List[Tag] - ): - # type: (...) -> Link + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Link: cache_entry = self.get_cache_entry(link, package_name, supported_tags) if cache_entry is None: return link @@ -321,11 +237,10 @@ class WheelCache(Cache): def get_cache_entry( self, - link, # type: Link - package_name, # type: Optional[str] - supported_tags, # type: List[Tag] - ): - # type: (...) -> Optional[CacheEntry] + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Optional[CacheEntry]: """Returns a CacheEntry with a link to a cached item if it exists or None. The cache entry indicates if the item was found in the persistent or ephemeral cache. diff --git a/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py b/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py index 329de602513d7bb868799a49d36d3f081a79e441..3cad1486071a69c9bfe48d2834c62045ac8c1865 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py +++ b/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py @@ -5,36 +5,31 @@ import optparse import os import sys from itertools import chain +from typing import Any, Iterable, List, Optional from pip._internal.cli.main_parser import create_main_parser from pip._internal.commands import commands_dict, create_command -from pip._internal.utils.misc import get_installed_distributions -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.metadata import get_default_environment -if MYPY_CHECK_RUNNING: - from typing import Any, Iterable, List, Optional - -def autocomplete(): - # type: () -> None - """Entry Point for completion of main and subcommand options. - """ +def autocomplete() -> None: + """Entry Point for completion of main and subcommand options.""" # Don't complete if user hasn't sourced bash_completion file. - if 'PIP_AUTO_COMPLETE' not in os.environ: + if "PIP_AUTO_COMPLETE" not in os.environ: return - cwords = os.environ['COMP_WORDS'].split()[1:] - cword = int(os.environ['COMP_CWORD']) + cwords = os.environ["COMP_WORDS"].split()[1:] + cword = int(os.environ["COMP_CWORD"]) try: current = cwords[cword - 1] except IndexError: - current = '' + current = "" parser = create_main_parser() subcommands = list(commands_dict) options = [] # subcommand - subcommand_name = None # type: Optional[str] + subcommand_name: Optional[str] = None for word in cwords: if word in subcommands: subcommand_name = word @@ -42,19 +37,22 @@ def autocomplete(): # subcommand options if subcommand_name is not None: # special case: 'help' subcommand has no options - if subcommand_name == 'help': + if subcommand_name == "help": sys.exit(1) # special case: list locally installed dists for show and uninstall - should_list_installed = ( - subcommand_name in ['show', 'uninstall'] and - not current.startswith('-') - ) + should_list_installed = not current.startswith("-") and subcommand_name in [ + "show", + "uninstall", + ] if should_list_installed: - installed = [] + env = get_default_environment() lc = current.lower() - for dist in get_installed_distributions(local_only=True): - if dist.key.startswith(lc) and dist.key not in cwords[1:]: - installed.append(dist.key) + installed = [ + dist.canonical_name + for dist in env.iter_installed_distributions(local_only=True) + if dist.canonical_name.startswith(lc) + and dist.canonical_name not in cwords[1:] + ] # if there are no dists installed, fall back to option completion if installed: for dist in installed: @@ -69,13 +67,15 @@ def autocomplete(): options.append((opt_str, opt.nargs)) # filter out previously specified options from available options - prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]] options = [(x, v) for (x, v) in options if x not in prev_opts] # filter options by current input options = [(k, v) for k, v in options if k.startswith(current)] # get completion type given cwords and available subcommand options completion_type = get_path_completion_type( - cwords, cword, subcommand.parser.option_list_all, + cwords, + cword, + subcommand.parser.option_list_all, ) # get completion files and directories if ``completion_type`` is # ````, ```` or ```` @@ -86,7 +86,7 @@ def autocomplete(): opt_label = option[0] # append '=' to options which require args if option[1] and option[0][:2] == "--": - opt_label += '=' + opt_label += "=" print(opt_label) else: # show main parser options only when necessary @@ -94,24 +94,23 @@ def autocomplete(): opts = [i.option_list for i in parser.option_groups] opts.append(parser.option_list) flattened_opts = chain.from_iterable(opts) - if current.startswith('-'): + if current.startswith("-"): for opt in flattened_opts: if opt.help != optparse.SUPPRESS_HELP: subcommands += opt._long_opts + opt._short_opts else: # get completion type given cwords and all available options - completion_type = get_path_completion_type(cwords, cword, - flattened_opts) + completion_type = get_path_completion_type(cwords, cword, flattened_opts) if completion_type: - subcommands = list(auto_complete_paths(current, - completion_type)) + subcommands = list(auto_complete_paths(current, completion_type)) - print(' '.join([x for x in subcommands if x.startswith(current)])) + print(" ".join([x for x in subcommands if x.startswith(current)])) sys.exit(1) -def get_path_completion_type(cwords, cword, opts): - # type: (List[str], int, Iterable[Any]) -> Optional[str] +def get_path_completion_type( + cwords: List[str], cword: int, opts: Iterable[Any] +) -> Optional[str]: """Get the type of path completion (``file``, ``dir``, ``path`` or None) :param cwords: same as the environmental variable ``COMP_WORDS`` @@ -119,22 +118,21 @@ def get_path_completion_type(cwords, cword, opts): :param opts: The available options to check :return: path completion type (``file``, ``dir``, ``path`` or None) """ - if cword < 2 or not cwords[cword - 2].startswith('-'): + if cword < 2 or not cwords[cword - 2].startswith("-"): return None for opt in opts: if opt.help == optparse.SUPPRESS_HELP: continue - for o in str(opt).split('/'): - if cwords[cword - 2].split('=')[0] == o: + for o in str(opt).split("/"): + if cwords[cword - 2].split("=")[0] == o: if not opt.metavar or any( - x in ('path', 'file', 'dir') - for x in opt.metavar.split('/')): + x in ("path", "file", "dir") for x in opt.metavar.split("/") + ): return opt.metavar return None -def auto_complete_paths(current, completion_type): - # type: (str, str) -> Iterable[str] +def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]: """If ``completion_type`` is ``file`` or ``path``, list all regular files and directories starting with ``current``; otherwise only list directories starting with ``current``. @@ -150,15 +148,16 @@ def auto_complete_paths(current, completion_type): return filename = os.path.normcase(filename) # list all files that start with ``filename`` - file_list = (x for x in os.listdir(current_path) - if os.path.normcase(x).startswith(filename)) + file_list = ( + x for x in os.listdir(current_path) if os.path.normcase(x).startswith(filename) + ) for f in file_list: opt = os.path.join(current_path, f) comp_file = os.path.normcase(os.path.join(directory, f)) # complete regular files when there is not ```` after option # complete directories when there is ````, ```` or # ````after option - if completion_type != 'dir' and os.path.isfile(opt): + if completion_type != "dir" and os.path.isfile(opt): yield comp_file elif os.path.isdir(opt): - yield os.path.join(comp_file, '') + yield os.path.join(comp_file, "") diff --git a/venv/Lib/site-packages/pip/_internal/cli/base_command.py b/venv/Lib/site-packages/pip/_internal/cli/base_command.py index 1fa5ba0bd4a9dc93a38256c72932316962d2bb08..0afe7e75d46e432cf1b4f90c9c1a9c3f5af28914 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/base_command.py +++ b/venv/Lib/site-packages/pip/_internal/cli/base_command.py @@ -1,25 +1,21 @@ """Base Command class, and related routines""" -from __future__ import absolute_import, print_function - +import functools import logging import logging.config import optparse import os -import platform import sys import traceback +from optparse import Values +from typing import Any, Callable, List, Optional, Tuple from pip._internal.cli import cmdoptions from pip._internal.cli.command_context import CommandContextMixIn -from pip._internal.cli.parser import ( - ConfigOptionParser, - UpdatingDefaultsHelpFormatter, -) +from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter from pip._internal.cli.status_codes import ( ERROR, PREVIOUS_BUILD_DIR_ERROR, - SUCCESS, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, ) @@ -27,58 +23,45 @@ from pip._internal.exceptions import ( BadCommand, CommandError, InstallationError, + NetworkConnectionError, PreviousBuildDirError, UninstallationError, ) -from pip._internal.utils.deprecation import deprecated from pip._internal.utils.filesystem import check_path_owner from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging from pip._internal.utils.misc import get_prog, normalize_path -from pip._internal.utils.temp_dir import ( - global_tempdir_manager, - tempdir_registry, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry +from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry from pip._internal.utils.virtualenv import running_under_virtualenv -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Tuple, Any - from optparse import Values - - from pip._internal.utils.temp_dir import ( - TempDirectoryTypeRegistry as TempDirRegistry - ) - -__all__ = ['Command'] +__all__ = ["Command"] logger = logging.getLogger(__name__) class Command(CommandContextMixIn): - usage = None # type: str - ignore_require_venv = False # type: bool - - def __init__(self, name, summary, isolated=False): - # type: (str, str, bool) -> None - super(Command, self).__init__() - parser_kw = { - 'usage': self.usage, - 'prog': '{} {}'.format(get_prog(), name), - 'formatter': UpdatingDefaultsHelpFormatter(), - 'add_help_option': False, - 'name': name, - 'description': self.__doc__, - 'isolated': isolated, - } + usage: str = "" + ignore_require_venv: bool = False + + def __init__(self, name: str, summary: str, isolated: bool = False) -> None: + super().__init__() self.name = name self.summary = summary - self.parser = ConfigOptionParser(**parser_kw) + self.parser = ConfigOptionParser( + usage=self.usage, + prog=f"{get_prog()} {name}", + formatter=UpdatingDefaultsHelpFormatter(), + add_help_option=False, + name=name, + description=self.__doc__, + isolated=isolated, + ) - self.tempdir_registry = None # type: Optional[TempDirRegistry] + self.tempdir_registry: Optional[TempDirRegistry] = None # Commands should add options to this option group - optgroup_name = '{} Options'.format(self.name.capitalize()) + optgroup_name = f"{self.name.capitalize()} Options" self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) # Add the general options @@ -88,35 +71,35 @@ class Command(CommandContextMixIn): ) self.parser.add_option_group(gen_opts) - def handle_pip_version_check(self, options): - # type: (Values) -> None + self.add_options() + + def add_options(self) -> None: + pass + + def handle_pip_version_check(self, options: Values) -> None: """ This is a no-op so that commands by default do not do the pip version check. """ # Make sure we do the pip version check if the index_group options # are present. - assert not hasattr(options, 'no_index') + assert not hasattr(options, "no_index") - def run(self, options, args): - # type: (Values, List[Any]) -> Any + def run(self, options: Values, args: List[str]) -> int: raise NotImplementedError - def parse_args(self, args): - # type: (List[str]) -> Tuple[Any, Any] + def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]: # factored out for testability return self.parser.parse_args(args) - def main(self, args): - # type: (List[str]) -> int + def main(self, args: List[str]) -> int: try: with self.main_context(): return self._main(args) finally: logging.shutdown() - def _main(self, args): - # type: (List[str]) -> int + def _main(self, args: List[str]) -> int: # We must initialize this before the tempdir manager, otherwise the # configuration would not be accessible by the time we clean up the # tempdir manager. @@ -136,39 +119,20 @@ class Command(CommandContextMixIn): user_log_file=options.log, ) - if ( - sys.version_info[:2] == (2, 7) and - not options.no_python_version_warning - ): - message = ( - "pip 21.0 will drop support for Python 2.7 in January 2021. " - "More details about Python 2 support in pip, can be found at " - "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa - ) - if platform.python_implementation() == "CPython": - message = ( - "Python 2.7 reached the end of its life on January " - "1st, 2020. Please upgrade your Python as Python 2.7 " - "is no longer maintained. " - ) + message - deprecated(message, replacement=None, gone_in=None) - # TODO: Try to get these passing down from the command? # without resorting to os.environ to hold these. # This also affects isolated builds and it should. if options.no_input: - os.environ['PIP_NO_INPUT'] = '1' + os.environ["PIP_NO_INPUT"] = "1" if options.exists_action: - os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action) if options.require_venv and not self.ignore_require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): - logger.critical( - 'Could not find an activated virtualenv (required).' - ) + logger.critical("Could not find an activated virtualenv (required).") sys.exit(VIRTUALENV_NOT_FOUND) if options.cache_dir: @@ -178,51 +142,73 @@ class Command(CommandContextMixIn): "The directory '%s' or its parent directory is not owned " "or is not writable by the current user. The cache " "has been disabled. Check the permissions and owner of " - "that directory. If executing pip with sudo, you may want " - "sudo's -H flag.", + "that directory. If executing pip with sudo, you should " + "use sudo's -H flag.", options.cache_dir, ) options.cache_dir = None + if "2020-resolver" in options.features_enabled: + logger.warning( + "--use-feature=2020-resolver no longer has any effect, " + "since it is now the default dependency resolver in pip. " + "This will become an error in pip 21.0." + ) + + def intercepts_unhandled_exc( + run_func: Callable[..., int] + ) -> Callable[..., int]: + @functools.wraps(run_func) + def exc_logging_wrapper(*args: Any) -> int: + try: + status = run_func(*args) + assert isinstance(status, int) + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug("Exception information:", exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except ( + InstallationError, + UninstallationError, + BadCommand, + NetworkConnectionError, + ) as exc: + logger.critical(str(exc)) + logger.debug("Exception information:", exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical("%s", exc) + logger.debug("Exception information:", exc_info=True) + + return ERROR + except BrokenStdoutLoggingError: + # Bypass our logger and write any remaining messages to + # stderr because stdout no longer works. + print("ERROR: Pipe to stdout was broken", file=sys.stderr) + if level_number <= logging.DEBUG: + traceback.print_exc(file=sys.stderr) + + return ERROR + except KeyboardInterrupt: + logger.critical("Operation cancelled by user") + logger.debug("Exception information:", exc_info=True) + + return ERROR + except BaseException: + logger.critical("Exception:", exc_info=True) + + return UNKNOWN_ERROR + + return exc_logging_wrapper + try: - status = self.run(options, args) - # FIXME: all commands should return an exit status - # and when it is done, isinstance is not needed anymore - if isinstance(status, int): - return status - except PreviousBuildDirError as exc: - logger.critical(str(exc)) - logger.debug('Exception information:', exc_info=True) - - return PREVIOUS_BUILD_DIR_ERROR - except (InstallationError, UninstallationError, BadCommand) as exc: - logger.critical(str(exc)) - logger.debug('Exception information:', exc_info=True) - - return ERROR - except CommandError as exc: - logger.critical('%s', exc) - logger.debug('Exception information:', exc_info=True) - - return ERROR - except BrokenStdoutLoggingError: - # Bypass our logger and write any remaining messages to stderr - # because stdout no longer works. - print('ERROR: Pipe to stdout was broken', file=sys.stderr) - if level_number <= logging.DEBUG: - traceback.print_exc(file=sys.stderr) - - return ERROR - except KeyboardInterrupt: - logger.critical('Operation cancelled by user') - logger.debug('Exception information:', exc_info=True) - - return ERROR - except BaseException: - logger.critical('Exception:', exc_info=True) - - return UNKNOWN_ERROR + if not options.debug_mode: + run = intercepts_unhandled_exc(self.run) + else: + run = self.run + return run(options, args) finally: self.handle_pip_version_check(options) - - return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py b/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py index 5b5647d64f4e9b37bc5399ed633001cdf1063594..626fd00427c2655ea94cf1a71c999bad97c5eb46 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py +++ b/venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py @@ -10,17 +10,17 @@ pass on state. To be consistent, all options will follow this design. # The following comment should be removed at some point in the future. # mypy: strict-optional=False -from __future__ import absolute_import - -import logging import os import textwrap import warnings -from distutils.util import strtobool from functools import partial -from optparse import SUPPRESS_HELP, Option, OptionGroup +from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values from textwrap import dedent +from typing import Any, Callable, Dict, Optional, Tuple + +from pip._vendor.packaging.utils import canonicalize_name +from pip._internal.cli.parser import ConfigOptionParser from pip._internal.cli.progress_bars import BAR_TYPES from pip._internal.exceptions import CommandError from pip._internal.locations import USER_CACHE_DIR, get_src_prefix @@ -28,18 +28,10 @@ from pip._internal.models.format_control import FormatControl from pip._internal.models.index import PyPI from pip._internal.models.target_python import TargetPython from pip._internal.utils.hashes import STRONG_HASHES -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Any, Callable, Dict, Optional, Tuple - from optparse import OptionParser, Values - from pip._internal.cli.parser import ConfigOptionParser +from pip._internal.utils.misc import strtobool -logger = logging.getLogger(__name__) - -def raise_option_error(parser, option, msg): - # type: (OptionParser, Option, str) -> None +def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None: """ Raise an option parsing error using parser.error(). @@ -48,26 +40,26 @@ def raise_option_error(parser, option, msg): option: an Option instance. msg: the error text. """ - msg = '{} error: {}'.format(option, msg) - msg = textwrap.fill(' '.join(msg.split())) + msg = f"{option} error: {msg}" + msg = textwrap.fill(" ".join(msg.split())) parser.error(msg) -def make_option_group(group, parser): - # type: (Dict[str, Any], ConfigOptionParser) -> OptionGroup +def make_option_group(group: Dict[str, Any], parser: ConfigOptionParser) -> OptionGroup: """ Return an OptionGroup object group -- assumed to be dict with 'name' and 'options' keys parser -- an optparse Parser """ - option_group = OptionGroup(parser, group['name']) - for option in group['options']: + option_group = OptionGroup(parser, group["name"]) + for option in group["options"]: option_group.add_option(option()) return option_group -def check_install_build_global(options, check_options=None): - # type: (Values, Optional[Values]) -> None +def check_install_build_global( + options: Values, check_options: Optional[Values] = None +) -> None: """Disable wheels if per-setup.py call options are set. :param options: The OptionParser options to update. @@ -77,37 +69,38 @@ def check_install_build_global(options, check_options=None): if check_options is None: check_options = options - def getname(n): - # type: (str) -> Optional[Any] + def getname(n: str) -> Optional[Any]: return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] if any(map(getname, names)): control = options.format_control control.disallow_binaries() warnings.warn( - 'Disabling all use of wheels due to the use of --build-option ' - '/ --global-option / --install-option.', stacklevel=2, + "Disabling all use of wheels due to the use of --build-option " + "/ --global-option / --install-option.", + stacklevel=2, ) -def check_dist_restriction(options, check_target=False): - # type: (Values, bool) -> None +def check_dist_restriction(options: Values, check_target: bool = False) -> None: """Function for determining if custom platform options are allowed. :param options: The OptionParser options. :param check_target: Whether or not to check if --target is being used. """ - dist_restriction_set = any([ - options.python_version, - options.platform, - options.abi, - options.implementation, - ]) - - binary_only = FormatControl(set(), {':all:'}) + dist_restriction_set = any( + [ + options.python_version, + options.platforms, + options.abis, + options.implementation, + ] + ) + + binary_only = FormatControl(set(), {":all:"}) sdist_dependencies_allowed = ( - options.format_control != binary_only and - not options.ignore_dependencies + options.format_control != binary_only and not options.ignore_dependencies ) # Installations or downloads using dist restrictions must not combine @@ -130,14 +123,18 @@ def check_dist_restriction(options, check_target=False): ) -def _path_option_check(option, opt, value): - # type: (Option, str, str) -> str +def _path_option_check(option: Option, opt: str, value: str) -> str: return os.path.expanduser(value) +def _package_name_option_check(option: Option, opt: str, value: str) -> str: + return canonicalize_name(value) + + class PipOption(Option): - TYPES = Option.TYPES + ("path",) + TYPES = Option.TYPES + ("path", "package_name") TYPE_CHECKER = Option.TYPE_CHECKER.copy() + TYPE_CHECKER["package_name"] = _package_name_option_check TYPE_CHECKER["path"] = _path_option_check @@ -145,15 +142,28 @@ class PipOption(Option): # options # ########### -help_ = partial( +help_: Callable[..., Option] = partial( Option, - '-h', '--help', - dest='help', - action='help', - help='Show help.', -) # type: Callable[..., Option] + "-h", + "--help", + dest="help", + action="help", + help="Show help.", +) -isolated_mode = partial( +debug_mode: Callable[..., Option] = partial( + Option, + "--debug", + dest="debug_mode", + action="store_true", + default=False, + help=( + "Let unhandled exceptions propagate outside the main subroutine, " + "instead of logging them to stderr." + ), +) + +isolated_mode: Callable[..., Option] = partial( Option, "--isolated", dest="isolated_mode", @@ -163,210 +173,222 @@ isolated_mode = partial( "Run pip in an isolated mode, ignoring environment variables and user " "configuration." ), -) # type: Callable[..., Option] +) -require_virtualenv = partial( +require_virtualenv: Callable[..., Option] = partial( Option, # Run only if inside a virtualenv, bail if not. - '--require-virtualenv', '--require-venv', - dest='require_venv', - action='store_true', + "--require-virtualenv", + "--require-venv", + dest="require_venv", + action="store_true", default=False, - help=SUPPRESS_HELP -) # type: Callable[..., Option] + help=SUPPRESS_HELP, +) -verbose = partial( +verbose: Callable[..., Option] = partial( Option, - '-v', '--verbose', - dest='verbose', - action='count', + "-v", + "--verbose", + dest="verbose", + action="count", default=0, - help='Give more output. Option is additive, and can be used up to 3 times.' -) # type: Callable[..., Option] + help="Give more output. Option is additive, and can be used up to 3 times.", +) -no_color = partial( +no_color: Callable[..., Option] = partial( Option, - '--no-color', - dest='no_color', - action='store_true', + "--no-color", + dest="no_color", + action="store_true", default=False, - help="Suppress colored output", -) # type: Callable[..., Option] + help="Suppress colored output.", +) -version = partial( +version: Callable[..., Option] = partial( Option, - '-V', '--version', - dest='version', - action='store_true', - help='Show version and exit.', -) # type: Callable[..., Option] + "-V", + "--version", + dest="version", + action="store_true", + help="Show version and exit.", +) -quiet = partial( +quiet: Callable[..., Option] = partial( Option, - '-q', '--quiet', - dest='quiet', - action='count', + "-q", + "--quiet", + dest="quiet", + action="count", default=0, help=( - 'Give less output. Option is additive, and can be used up to 3' - ' times (corresponding to WARNING, ERROR, and CRITICAL logging' - ' levels).' + "Give less output. Option is additive, and can be used up to 3" + " times (corresponding to WARNING, ERROR, and CRITICAL logging" + " levels)." ), -) # type: Callable[..., Option] +) -progress_bar = partial( +progress_bar: Callable[..., Option] = partial( Option, - '--progress-bar', - dest='progress_bar', - type='choice', + "--progress-bar", + dest="progress_bar", + type="choice", choices=list(BAR_TYPES.keys()), - default='on', + default="on", help=( - 'Specify type of progress to be displayed [' + - '|'.join(BAR_TYPES.keys()) + '] (default: %default)' + "Specify type of progress to be displayed [" + + "|".join(BAR_TYPES.keys()) + + "] (default: %default)" ), -) # type: Callable[..., Option] +) -log = partial( +log: Callable[..., Option] = partial( PipOption, - "--log", "--log-file", "--local-log", + "--log", + "--log-file", + "--local-log", dest="log", metavar="path", type="path", - help="Path to a verbose appending log." -) # type: Callable[..., Option] + help="Path to a verbose appending log.", +) -no_input = partial( +no_input: Callable[..., Option] = partial( Option, # Don't ask for input - '--no-input', - dest='no_input', - action='store_true', + "--no-input", + dest="no_input", + action="store_true", default=False, - help=SUPPRESS_HELP -) # type: Callable[..., Option] + help="Disable prompting for input.", +) -proxy = partial( +proxy: Callable[..., Option] = partial( Option, - '--proxy', - dest='proxy', - type='str', - default='', - help="Specify a proxy in the form [user:passwd@]proxy.server:port." -) # type: Callable[..., Option] + "--proxy", + dest="proxy", + type="str", + default="", + help="Specify a proxy in the form [user:passwd@]proxy.server:port.", +) -retries = partial( +retries: Callable[..., Option] = partial( Option, - '--retries', - dest='retries', - type='int', + "--retries", + dest="retries", + type="int", default=5, help="Maximum number of retries each connection should attempt " - "(default %default times).", -) # type: Callable[..., Option] + "(default %default times).", +) -timeout = partial( +timeout: Callable[..., Option] = partial( Option, - '--timeout', '--default-timeout', - metavar='sec', - dest='timeout', - type='float', + "--timeout", + "--default-timeout", + metavar="sec", + dest="timeout", + type="float", default=15, - help='Set the socket timeout (default %default seconds).', -) # type: Callable[..., Option] + help="Set the socket timeout (default %default seconds).", +) -def exists_action(): - # type: () -> Option +def exists_action() -> Option: return Option( # Option when path already exist - '--exists-action', - dest='exists_action', - type='choice', - choices=['s', 'i', 'w', 'b', 'a'], + "--exists-action", + dest="exists_action", + type="choice", + choices=["s", "i", "w", "b", "a"], default=[], - action='append', - metavar='action', + action="append", + metavar="action", help="Default action when a path already exists: " - "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", ) -cert = partial( +cert: Callable[..., Option] = partial( PipOption, - '--cert', - dest='cert', - type='path', - metavar='path', - help="Path to alternate CA bundle.", -) # type: Callable[..., Option] - -client_cert = partial( + "--cert", + dest="cert", + type="path", + metavar="path", + help=( + "Path to PEM-encoded CA certificate bundle. " + "If provided, overrides the default. " + "See 'SSL Certificate Verification' in pip documentation " + "for more information." + ), +) + +client_cert: Callable[..., Option] = partial( PipOption, - '--client-cert', - dest='client_cert', - type='path', + "--client-cert", + dest="client_cert", + type="path", default=None, - metavar='path', + metavar="path", help="Path to SSL client certificate, a single file containing the " - "private key and the certificate in PEM format.", -) # type: Callable[..., Option] + "private key and the certificate in PEM format.", +) -index_url = partial( +index_url: Callable[..., Option] = partial( Option, - '-i', '--index-url', '--pypi-url', - dest='index_url', - metavar='URL', + "-i", + "--index-url", + "--pypi-url", + dest="index_url", + metavar="URL", default=PyPI.simple_url, help="Base URL of the Python Package Index (default %default). " - "This should point to a repository compliant with PEP 503 " - "(the simple repository API) or a local directory laid out " - "in the same format.", -) # type: Callable[..., Option] + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) -def extra_index_url(): - # type: () -> Option +def extra_index_url() -> Option: return Option( - '--extra-index-url', - dest='extra_index_urls', - metavar='URL', - action='append', + "--extra-index-url", + dest="extra_index_urls", + metavar="URL", + action="append", default=[], help="Extra URLs of package indexes to use in addition to " - "--index-url. Should follow the same rules as " - "--index-url.", + "--index-url. Should follow the same rules as " + "--index-url.", ) -no_index = partial( +no_index: Callable[..., Option] = partial( Option, - '--no-index', - dest='no_index', - action='store_true', + "--no-index", + dest="no_index", + action="store_true", default=False, - help='Ignore package index (only looking at --find-links URLs instead).', -) # type: Callable[..., Option] + help="Ignore package index (only looking at --find-links URLs instead).", +) -def find_links(): - # type: () -> Option +def find_links() -> Option: return Option( - '-f', '--find-links', - dest='find_links', - action='append', + "-f", + "--find-links", + dest="find_links", + action="append", default=[], - metavar='url', + metavar="url", help="If a URL or path to an html file, then parse for links to " - "archives such as sdist (.tar.gz) or wheel (.whl) files. " - "If a local path or file:// URL that's a directory, " - "then look for archives in the directory listing. " - "Links to VCS project URLs are not supported.", + "archives such as sdist (.tar.gz) or wheel (.whl) files. " + "If a local path or file:// URL that's a directory, " + "then look for archives in the directory listing. " + "Links to VCS project URLs are not supported.", ) -def trusted_host(): - # type: () -> Option +def trusted_host() -> Option: return Option( "--trusted-host", dest="trusted_hosts", @@ -374,138 +396,154 @@ def trusted_host(): metavar="HOSTNAME", default=[], help="Mark this host or host:port pair as trusted, even though it " - "does not have valid or any HTTPS.", + "does not have valid or any HTTPS.", ) -def constraints(): - # type: () -> Option +def constraints() -> Option: return Option( - '-c', '--constraint', - dest='constraints', - action='append', + "-c", + "--constraint", + dest="constraints", + action="append", default=[], - metavar='file', - help='Constrain versions using the given constraints file. ' - 'This option can be used multiple times.' + metavar="file", + help="Constrain versions using the given constraints file. " + "This option can be used multiple times.", ) -def requirements(): - # type: () -> Option +def requirements() -> Option: return Option( - '-r', '--requirement', - dest='requirements', - action='append', + "-r", + "--requirement", + dest="requirements", + action="append", default=[], - metavar='file', - help='Install from the given requirements file. ' - 'This option can be used multiple times.' + metavar="file", + help="Install from the given requirements file. " + "This option can be used multiple times.", ) -def editable(): - # type: () -> Option +def editable() -> Option: return Option( - '-e', '--editable', - dest='editables', - action='append', + "-e", + "--editable", + dest="editables", + action="append", default=[], - metavar='path/url', - help=('Install a project in editable mode (i.e. setuptools ' - '"develop mode") from a local project path or a VCS url.'), + metavar="path/url", + help=( + "Install a project in editable mode (i.e. setuptools " + '"develop mode") from a local project path or a VCS url.' + ), ) -def _handle_src(option, opt_str, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) -> None: value = os.path.abspath(value) setattr(parser.values, option.dest, value) -src = partial( +src: Callable[..., Option] = partial( PipOption, - '--src', '--source', '--source-dir', '--source-directory', - dest='src_dir', - type='path', - metavar='dir', + "--src", + "--source", + "--source-dir", + "--source-directory", + dest="src_dir", + type="path", + metavar="dir", default=get_src_prefix(), - action='callback', + action="callback", callback=_handle_src, - help='Directory to check out editable projects into. ' + help="Directory to check out editable projects into. " 'The default in a virtualenv is "/src". ' - 'The default for global installs is "/src".' -) # type: Callable[..., Option] + 'The default for global installs is "/src".', +) -def _get_format_control(values, option): - # type: (Values, Option) -> Any +def _get_format_control(values: Values, option: Option) -> Any: """Get a format_control object.""" return getattr(values, option.dest) -def _handle_no_binary(option, opt_str, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_no_binary( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: existing = _get_format_control(parser.values, option) FormatControl.handle_mutual_excludes( - value, existing.no_binary, existing.only_binary, + value, + existing.no_binary, + existing.only_binary, ) -def _handle_only_binary(option, opt_str, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_only_binary( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: existing = _get_format_control(parser.values, option) FormatControl.handle_mutual_excludes( - value, existing.only_binary, existing.no_binary, + value, + existing.only_binary, + existing.no_binary, ) -def no_binary(): - # type: () -> Option +def no_binary() -> Option: format_control = FormatControl(set(), set()) return Option( - "--no-binary", dest="format_control", action="callback", - callback=_handle_no_binary, type="str", + "--no-binary", + dest="format_control", + action="callback", + callback=_handle_no_binary, + type="str", default=format_control, - help='Do not use binary packages. Can be supplied multiple times, and ' - 'each time adds to the existing value. Accepts either ":all:" to ' - 'disable all binary packages, ":none:" to empty the set (notice ' - 'the colons), or one or more package names with commas between ' - 'them (no colons). Note that some packages are tricky to compile ' - 'and may fail to install when this option is used on them.', + help="Do not use binary packages. Can be supplied multiple times, and " + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all binary packages, ":none:" to empty the set (notice ' + "the colons), or one or more package names with commas between " + "them (no colons). Note that some packages are tricky to compile " + "and may fail to install when this option is used on them.", ) -def only_binary(): - # type: () -> Option +def only_binary() -> Option: format_control = FormatControl(set(), set()) return Option( - "--only-binary", dest="format_control", action="callback", - callback=_handle_only_binary, type="str", + "--only-binary", + dest="format_control", + action="callback", + callback=_handle_only_binary, + type="str", default=format_control, - help='Do not use source packages. Can be supplied multiple times, and ' - 'each time adds to the existing value. Accepts either ":all:" to ' - 'disable all source packages, ":none:" to empty the set, or one ' - 'or more package names with commas between them. Packages ' - 'without binary distributions will fail to install when this ' - 'option is used on them.', + help="Do not use source packages. Can be supplied multiple times, and " + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all source packages, ":none:" to empty the set, or one ' + "or more package names with commas between them. Packages " + "without binary distributions will fail to install when this " + "option is used on them.", ) -platform = partial( +platforms: Callable[..., Option] = partial( Option, - '--platform', - dest='platform', - metavar='platform', + "--platform", + dest="platforms", + metavar="platform", + action="append", default=None, - help=("Only use wheels compatible with . " - "Defaults to the platform of the running system."), -) # type: Callable[..., Option] + help=( + "Only use wheels compatible with . Defaults to the " + "platform of the running system. Use this option multiple times to " + "specify multiple platforms supported by the target interpreter." + ), +) # This was made a separate function for unit-testing purposes. -def _convert_python_version(value): - # type: (str) -> Tuple[Tuple[int, ...], Optional[str]] +def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]]: """ Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. @@ -516,9 +554,9 @@ def _convert_python_version(value): # The empty string is the same as not providing a value. return (None, None) - parts = value.split('.') + parts = value.split(".") if len(parts) > 3: - return ((), 'at most three version parts are allowed') + return ((), "at most three version parts are allowed") if len(parts) == 1: # Then we are in the case of "3" or "37". @@ -529,119 +567,125 @@ def _convert_python_version(value): try: version_info = tuple(int(part) for part in parts) except ValueError: - return ((), 'each version part must be an integer') + return ((), "each version part must be an integer") return (version_info, None) -def _handle_python_version(option, opt_str, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_python_version( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: """ Handle a provided --python-version value. """ version_info, error_msg = _convert_python_version(value) if error_msg is not None: - msg = ( - 'invalid --python-version value: {!r}: {}'.format( - value, error_msg, - ) + msg = "invalid --python-version value: {!r}: {}".format( + value, + error_msg, ) raise_option_error(parser, option=option, msg=msg) parser.values.python_version = version_info -python_version = partial( +python_version: Callable[..., Option] = partial( Option, - '--python-version', - dest='python_version', - metavar='python_version', - action='callback', - callback=_handle_python_version, type='str', + "--python-version", + dest="python_version", + metavar="python_version", + action="callback", + callback=_handle_python_version, + type="str", default=None, - help=dedent("""\ + help=dedent( + """\ The Python interpreter version to use for wheel and "Requires-Python" compatibility checks. Defaults to a version derived from the running interpreter. The version can be specified using up to three dot-separated integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor version can also be given as a string without dots (e.g. "37" for 3.7.0). - """), -) # type: Callable[..., Option] + """ + ), +) -implementation = partial( +implementation: Callable[..., Option] = partial( Option, - '--implementation', - dest='implementation', - metavar='implementation', + "--implementation", + dest="implementation", + metavar="implementation", default=None, - help=("Only use wheels compatible with Python " - "implementation , e.g. 'pp', 'jy', 'cp', " - " or 'ip'. If not specified, then the current " - "interpreter implementation is used. Use 'py' to force " - "implementation-agnostic wheels."), -) # type: Callable[..., Option] + help=( + "Only use wheels compatible with Python " + "implementation , e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels." + ), +) -abi = partial( +abis: Callable[..., Option] = partial( Option, - '--abi', - dest='abi', - metavar='abi', + "--abi", + dest="abis", + metavar="abi", + action="append", default=None, - help=("Only use wheels compatible with Python " - "abi , e.g. 'pypy_41'. If not specified, then the " - "current interpreter abi tag is used. Generally " - "you will need to specify --implementation, " - "--platform, and --python-version when using " - "this option."), -) # type: Callable[..., Option] - - -def add_target_python_options(cmd_opts): - # type: (OptionGroup) -> None - cmd_opts.add_option(platform()) + help=( + "Only use wheels compatible with Python abi , e.g. 'pypy_41'. " + "If not specified, then the current interpreter abi tag is used. " + "Use this option multiple times to specify multiple abis supported " + "by the target interpreter. Generally you will need to specify " + "--implementation, --platform, and --python-version when using this " + "option." + ), +) + + +def add_target_python_options(cmd_opts: OptionGroup) -> None: + cmd_opts.add_option(platforms()) cmd_opts.add_option(python_version()) cmd_opts.add_option(implementation()) - cmd_opts.add_option(abi()) + cmd_opts.add_option(abis()) -def make_target_python(options): - # type: (Values) -> TargetPython +def make_target_python(options: Values) -> TargetPython: target_python = TargetPython( - platform=options.platform, + platforms=options.platforms, py_version_info=options.python_version, - abi=options.abi, + abis=options.abis, implementation=options.implementation, ) return target_python -def prefer_binary(): - # type: () -> Option +def prefer_binary() -> Option: return Option( "--prefer-binary", dest="prefer_binary", action="store_true", default=False, - help="Prefer older binary packages over newer source packages." + help="Prefer older binary packages over newer source packages.", ) -cache_dir = partial( +cache_dir: Callable[..., Option] = partial( PipOption, "--cache-dir", dest="cache_dir", default=USER_CACHE_DIR, metavar="dir", - type='path', - help="Store the cache data in ." -) # type: Callable[..., Option] + type="path", + help="Store the cache data in .", +) -def _handle_no_cache_dir(option, opt, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_no_cache_dir( + option: Option, opt: str, value: str, parser: OptionParser +) -> None: """ Process a value provided for the --no-cache-dir option. @@ -668,69 +712,48 @@ def _handle_no_cache_dir(option, opt, value, parser): parser.values.cache_dir = False -no_cache = partial( +no_cache: Callable[..., Option] = partial( Option, "--no-cache-dir", dest="cache_dir", action="callback", callback=_handle_no_cache_dir, help="Disable the cache.", -) # type: Callable[..., Option] +) -no_deps = partial( +no_deps: Callable[..., Option] = partial( Option, - '--no-deps', '--no-dependencies', - dest='ignore_dependencies', - action='store_true', + "--no-deps", + "--no-dependencies", + dest="ignore_dependencies", + action="store_true", default=False, help="Don't install package dependencies.", -) # type: Callable[..., Option] - - -def _handle_build_dir(option, opt, value, parser): - # type: (Option, str, str, OptionParser) -> None - if value: - value = os.path.abspath(value) - setattr(parser.values, option.dest, value) +) +ignore_requires_python: Callable[..., Option] = partial( + Option, + "--ignore-requires-python", + dest="ignore_requires_python", + action="store_true", + help="Ignore the Requires-Python information.", +) -build_dir = partial( - PipOption, - '-b', '--build', '--build-dir', '--build-directory', - dest='build_dir', - type='path', - metavar='dir', - action='callback', - callback=_handle_build_dir, - help='Directory to unpack packages into and build in. Note that ' - 'an initial build still takes place in a temporary directory. ' - 'The location of temporary directories can be controlled by setting ' - 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' - 'When passed, build directories are not cleaned in case of failures.' -) # type: Callable[..., Option] - -ignore_requires_python = partial( - Option, - '--ignore-requires-python', - dest='ignore_requires_python', - action='store_true', - help='Ignore the Requires-Python information.' -) # type: Callable[..., Option] - -no_build_isolation = partial( - Option, - '--no-build-isolation', - dest='build_isolation', - action='store_false', +no_build_isolation: Callable[..., Option] = partial( + Option, + "--no-build-isolation", + dest="build_isolation", + action="store_false", default=True, - help='Disable isolation when building a modern source distribution. ' - 'Build dependencies specified by PEP 518 must be already installed ' - 'if this option is used.' -) # type: Callable[..., Option] + help="Disable isolation when building a modern source distribution. " + "Build dependencies specified by PEP 518 must be already installed " + "if this option is used.", +) -def _handle_no_use_pep517(option, opt, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_no_use_pep517( + option: Option, opt: str, value: str, parser: OptionParser +) -> None: """ Process a value provided for the --no-use-pep517 option. @@ -753,181 +776,205 @@ def _handle_no_use_pep517(option, opt, value, parser): parser.values.use_pep517 = False -use_pep517 = partial( +use_pep517: Any = partial( Option, - '--use-pep517', - dest='use_pep517', - action='store_true', + "--use-pep517", + dest="use_pep517", + action="store_true", default=None, - help='Use PEP 517 for building source distributions ' - '(use --no-use-pep517 to force legacy behaviour).' -) # type: Any + help="Use PEP 517 for building source distributions " + "(use --no-use-pep517 to force legacy behaviour).", +) -no_use_pep517 = partial( +no_use_pep517: Any = partial( Option, - '--no-use-pep517', - dest='use_pep517', - action='callback', + "--no-use-pep517", + dest="use_pep517", + action="callback", callback=_handle_no_use_pep517, default=None, - help=SUPPRESS_HELP -) # type: Any + help=SUPPRESS_HELP, +) -install_options = partial( +install_options: Callable[..., Option] = partial( Option, - '--install-option', - dest='install_options', - action='append', - metavar='options', + "--install-option", + dest="install_options", + action="append", + metavar="options", help="Extra arguments to be supplied to the setup.py install " - "command (use like --install-option=\"--install-scripts=/usr/local/" - "bin\"). Use multiple --install-option options to pass multiple " - "options to setup.py install. If you are using an option with a " - "directory path, be sure to use absolute path.", -) # type: Callable[..., Option] - -global_options = partial( - Option, - '--global-option', - dest='global_options', - action='append', - metavar='options', + 'command (use like --install-option="--install-scripts=/usr/local/' + 'bin"). Use multiple --install-option options to pass multiple ' + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.", +) + +build_options: Callable[..., Option] = partial( + Option, + "--build-option", + dest="build_options", + metavar="options", + action="append", + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", +) + +global_options: Callable[..., Option] = partial( + Option, + "--global-option", + dest="global_options", + action="append", + metavar="options", help="Extra global options to be supplied to the setup.py " - "call before the install command.", -) # type: Callable[..., Option] + "call before the install or bdist_wheel command.", +) -no_clean = partial( +no_clean: Callable[..., Option] = partial( Option, - '--no-clean', - action='store_true', + "--no-clean", + action="store_true", default=False, - help="Don't clean up build directories." -) # type: Callable[..., Option] + help="Don't clean up build directories.", +) -pre = partial( +pre: Callable[..., Option] = partial( Option, - '--pre', - action='store_true', + "--pre", + action="store_true", default=False, help="Include pre-release and development versions. By default, " - "pip only finds stable versions.", -) # type: Callable[..., Option] + "pip only finds stable versions.", +) -disable_pip_version_check = partial( +disable_pip_version_check: Callable[..., Option] = partial( Option, "--disable-pip-version-check", dest="disable_pip_version_check", action="store_true", default=False, help="Don't periodically check PyPI to determine whether a new version " - "of pip is available for download. Implied with --no-index.", -) # type: Callable[..., Option] + "of pip is available for download. Implied with --no-index.", +) -# Deprecated, Remove later -always_unzip = partial( - Option, - '-Z', '--always-unzip', - dest='always_unzip', - action='store_true', - help=SUPPRESS_HELP, -) # type: Callable[..., Option] - - -def _handle_merge_hash(option, opt_str, value, parser): - # type: (Option, str, str, OptionParser) -> None +def _handle_merge_hash( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: """Given a value spelled "algo:digest", append the digest to a list pointed to in a dict by the algo name.""" if not parser.values.hashes: parser.values.hashes = {} try: - algo, digest = value.split(':', 1) + algo, digest = value.split(":", 1) except ValueError: - parser.error('Arguments to {} must be a hash name ' - 'followed by a value, like --hash=sha256:' - 'abcde...'.format(opt_str)) + parser.error( + "Arguments to {} must be a hash name " # noqa + "followed by a value, like --hash=sha256:" + "abcde...".format(opt_str) + ) if algo not in STRONG_HASHES: - parser.error('Allowed hash algorithms for {} are {}.'.format( - opt_str, ', '.join(STRONG_HASHES))) + parser.error( + "Allowed hash algorithms for {} are {}.".format( # noqa + opt_str, ", ".join(STRONG_HASHES) + ) + ) parser.values.hashes.setdefault(algo, []).append(digest) -hash = partial( +hash: Callable[..., Option] = partial( Option, - '--hash', + "--hash", # Hash values eventually end up in InstallRequirement.hashes due to # __dict__ copying in process_line(). - dest='hashes', - action='callback', + dest="hashes", + action="callback", callback=_handle_merge_hash, - type='string', + type="string", help="Verify that the package's archive matches this " - 'hash before installing. Example: --hash=sha256:abcdef...', -) # type: Callable[..., Option] + "hash before installing. Example: --hash=sha256:abcdef...", +) -require_hashes = partial( +require_hashes: Callable[..., Option] = partial( Option, - '--require-hashes', - dest='require_hashes', - action='store_true', + "--require-hashes", + dest="require_hashes", + action="store_true", default=False, - help='Require a hash to check each requirement against, for ' - 'repeatable installs. This option is implied when any package in a ' - 'requirements file has a --hash option.', -) # type: Callable[..., Option] + help="Require a hash to check each requirement against, for " + "repeatable installs. This option is implied when any package in a " + "requirements file has a --hash option.", +) -list_path = partial( +list_path: Callable[..., Option] = partial( PipOption, - '--path', - dest='path', - type='path', - action='append', - help='Restrict to the specified installation path for listing ' - 'packages (can be used multiple times).' -) # type: Callable[..., Option] + "--path", + dest="path", + type="path", + action="append", + help="Restrict to the specified installation path for listing " + "packages (can be used multiple times).", +) -def check_list_path_option(options): - # type: (Values) -> None +def check_list_path_option(options: Values) -> None: if options.path and (options.user or options.local): - raise CommandError( - "Cannot combine '--path' with '--user' or '--local'" - ) + raise CommandError("Cannot combine '--path' with '--user' or '--local'") + + +list_exclude: Callable[..., Option] = partial( + PipOption, + "--exclude", + dest="excludes", + action="append", + metavar="package", + type="package_name", + help="Exclude specified package from the output", +) -no_python_version_warning = partial( +no_python_version_warning: Callable[..., Option] = partial( Option, - '--no-python-version-warning', - dest='no_python_version_warning', - action='store_true', + "--no-python-version-warning", + dest="no_python_version_warning", + action="store_true", default=False, - help='Silence deprecation warnings for upcoming unsupported Pythons.', -) # type: Callable[..., Option] + help="Silence deprecation warnings for upcoming unsupported Pythons.", +) -unstable_feature = partial( +use_new_feature: Callable[..., Option] = partial( Option, - '--unstable-feature', - dest='unstable_features', - metavar='feature', - action='append', + "--use-feature", + dest="features_enabled", + metavar="feature", + action="append", default=[], - choices=['resolver'], - help=SUPPRESS_HELP, # TODO: Enable this when the resolver actually works. - # help='Enable unstable feature(s) that may be backward incompatible.', -) # type: Callable[..., Option] + choices=["2020-resolver", "fast-deps", "in-tree-build"], + help="Enable new functionality, that may be backward incompatible.", +) + +use_deprecated_feature: Callable[..., Option] = partial( + Option, + "--use-deprecated", + dest="deprecated_features_enabled", + metavar="feature", + action="append", + default=[], + choices=["legacy-resolver", "out-of-tree-build"], + help=("Enable deprecated functionality, that will be removed in the future."), +) ########## # groups # ########## -general_group = { - 'name': 'General Options', - 'options': [ +general_group: Dict[str, Any] = { + "name": "General Options", + "options": [ help_, + debug_mode, isolated_mode, require_virtualenv, verbose, @@ -947,16 +994,17 @@ general_group = { disable_pip_version_check, no_color, no_python_version_warning, - unstable_feature, - ] -} # type: Dict[str, Any] - -index_group = { - 'name': 'Package Index Options', - 'options': [ + use_new_feature, + use_deprecated_feature, + ], +} + +index_group: Dict[str, Any] = { + "name": "Package Index Options", + "options": [ index_url, extra_index_url, no_index, find_links, - ] -} # type: Dict[str, Any] + ], +} diff --git a/venv/Lib/site-packages/pip/_internal/cli/command_context.py b/venv/Lib/site-packages/pip/_internal/cli/command_context.py index d1a64a776062a95258d3331cdec9b987e433ddf9..ed68322376db4864d2fca2d3bca0b0a300658167 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/command_context.py +++ b/venv/Lib/site-packages/pip/_internal/cli/command_context.py @@ -1,25 +1,17 @@ -from contextlib import contextmanager +from contextlib import ExitStack, contextmanager +from typing import ContextManager, Iterator, TypeVar -from pip._vendor.contextlib2 import ExitStack +_T = TypeVar("_T", covariant=True) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Iterator, ContextManager, TypeVar - - _T = TypeVar('_T', covariant=True) - - -class CommandContextMixIn(object): - def __init__(self): - # type: () -> None - super(CommandContextMixIn, self).__init__() +class CommandContextMixIn: + def __init__(self) -> None: + super().__init__() self._in_main_context = False self._main_context = ExitStack() @contextmanager - def main_context(self): - # type: () -> Iterator[None] + def main_context(self) -> Iterator[None]: assert not self._in_main_context self._in_main_context = True @@ -29,8 +21,7 @@ class CommandContextMixIn(object): finally: self._in_main_context = False - def enter_context(self, context_provider): - # type: (ContextManager[_T]) -> _T + def enter_context(self, context_provider: ContextManager[_T]) -> _T: assert self._in_main_context return self._main_context.enter_context(context_provider) diff --git a/venv/Lib/site-packages/pip/_internal/cli/main.py b/venv/Lib/site-packages/pip/_internal/cli/main.py index 172f30dd5bf550158a6b24fe020001616839da11..0e31221543adcd5cbec489985bbf473dcf7503f6 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/main.py +++ b/venv/Lib/site-packages/pip/_internal/cli/main.py @@ -1,21 +1,16 @@ """Primary application entrypoint. """ -from __future__ import absolute_import - import locale import logging import os import sys +from typing import List, Optional from pip._internal.cli.autocompletion import autocomplete from pip._internal.cli.main_parser import parse_command from pip._internal.commands import create_command from pip._internal.exceptions import PipError from pip._internal.utils import deprecation -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional logger = logging.getLogger(__name__) @@ -46,8 +41,8 @@ logger = logging.getLogger(__name__) # call to main. As it is not safe to do any processing after calling # main, this should not be an issue in practice. -def main(args=None): - # type: (Optional[List[str]]) -> int + +def main(args: Optional[List[str]] = None) -> int: if args is None: args = sys.argv[1:] @@ -59,14 +54,14 @@ def main(args=None): try: cmd_name, cmd_args = parse_command(args) except PipError as exc: - sys.stderr.write("ERROR: {}".format(exc)) + sys.stderr.write(f"ERROR: {exc}") sys.stderr.write(os.linesep) sys.exit(1) # Needed for locale.getpreferredencoding(False) to work # in pip._internal.utils.encoding.auto_decode try: - locale.setlocale(locale.LC_ALL, '') + locale.setlocale(locale.LC_ALL, "") except locale.Error as e: # setlocale can apparently crash if locale are uninitialized logger.debug("Ignoring error %s when setting locale", e) diff --git a/venv/Lib/site-packages/pip/_internal/cli/main_parser.py b/venv/Lib/site-packages/pip/_internal/cli/main_parser.py index 08c82c1f711ad50219cbf41545f4318e5651db8d..3666ab04ca6460be9bc6944c0f045be7ff44c365 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/main_parser.py +++ b/venv/Lib/site-packages/pip/_internal/cli/main_parser.py @@ -3,38 +3,27 @@ import os import sys +from typing import List, Tuple from pip._internal.cli import cmdoptions -from pip._internal.cli.parser import ( - ConfigOptionParser, - UpdatingDefaultsHelpFormatter, -) +from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter from pip._internal.commands import commands_dict, get_similar_commands from pip._internal.exceptions import CommandError from pip._internal.utils.misc import get_pip_version, get_prog -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Tuple, List - __all__ = ["create_main_parser", "parse_command"] -def create_main_parser(): - # type: () -> ConfigOptionParser - """Creates and returns the main parser for pip's CLI - """ - - parser_kw = { - 'usage': '\n%prog [options]', - 'add_help_option': False, - 'formatter': UpdatingDefaultsHelpFormatter(), - 'name': 'global', - 'prog': get_prog(), - } +def create_main_parser() -> ConfigOptionParser: + """Creates and returns the main parser for pip's CLI""" - parser = ConfigOptionParser(**parser_kw) + parser = ConfigOptionParser( + usage="\n%prog [options]", + add_help_option=False, + formatter=UpdatingDefaultsHelpFormatter(), + name="global", + prog=get_prog(), + ) parser.disable_interspersed_args() parser.version = get_pip_version() @@ -47,17 +36,16 @@ def create_main_parser(): parser.main = True # type: ignore # create command listing for description - description = [''] + [ - '{name:27} {command_info.summary}'.format(**locals()) + description = [""] + [ + f"{name:27} {command_info.summary}" for name, command_info in commands_dict.items() ] - parser.description = '\n'.join(description) + parser.description = "\n".join(description) return parser -def parse_command(args): - # type: (List[str]) -> Tuple[str, List[str]] +def parse_command(args: List[str]) -> Tuple[str, List[str]]: parser = create_main_parser() # Note: parser calls disable_interspersed_args(), so the result of this @@ -71,12 +59,12 @@ def parse_command(args): # --version if general_options.version: - sys.stdout.write(parser.version) # type: ignore + sys.stdout.write(parser.version) sys.stdout.write(os.linesep) sys.exit() # pip || pip help -> print_help() - if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + if not args_else or (args_else[0] == "help" and len(args_else) == 1): parser.print_help() sys.exit() @@ -86,11 +74,11 @@ def parse_command(args): if cmd_name not in commands_dict: guess = get_similar_commands(cmd_name) - msg = ['unknown command "{}"'.format(cmd_name)] + msg = [f'unknown command "{cmd_name}"'] if guess: - msg.append('maybe you meant "{}"'.format(guess)) + msg.append(f'maybe you meant "{guess}"') - raise CommandError(' - '.join(msg)) + raise CommandError(" - ".join(msg)) # all the args without the subcommand cmd_args = args[:] diff --git a/venv/Lib/site-packages/pip/_internal/cli/parser.py b/venv/Lib/site-packages/pip/_internal/cli/parser.py index 04e00b7213239c5111f3c07c323f3351fd0b8470..a1c99a8cb301f222feb1845be4e80d9b1f9d2622 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/parser.py +++ b/venv/Lib/site-packages/pip/_internal/cli/parser.py @@ -1,21 +1,16 @@ """Base option parser setup""" -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import optparse +import shutil import sys import textwrap -from distutils.util import strtobool - -from pip._vendor.six import string_types +from contextlib import suppress +from typing import Any, Dict, Iterator, List, Tuple from pip._internal.cli.status_codes import UNKNOWN_ERROR from pip._internal.configuration import Configuration, ConfigurationError -from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.misc import redact_auth_from_url, strtobool logger = logging.getLogger(__name__) @@ -23,17 +18,19 @@ logger = logging.getLogger(__name__) class PrettyHelpFormatter(optparse.IndentedHelpFormatter): """A prettier/less verbose help formatter for optparse.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: # help position must be aligned with __init__.parseopts.description - kwargs['max_help_position'] = 30 - kwargs['indent_increment'] = 1 - kwargs['width'] = get_terminal_size()[0] - 2 - optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + kwargs["max_help_position"] = 30 + kwargs["indent_increment"] = 1 + kwargs["width"] = shutil.get_terminal_size()[0] - 2 + super().__init__(*args, **kwargs) - def format_option_strings(self, option): + def format_option_strings(self, option: optparse.Option) -> str: return self._format_option_strings(option) - def _format_option_strings(self, option, mvarfmt=' <{}>', optsep=', '): + def _format_option_strings( + self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", " + ) -> str: """ Return a comma-separated list of option strings and metavars. @@ -51,52 +48,52 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter): opts.insert(1, optsep) if option.takes_value(): + assert option.dest is not None metavar = option.metavar or option.dest.lower() opts.append(mvarfmt.format(metavar.lower())) - return ''.join(opts) + return "".join(opts) - def format_heading(self, heading): - if heading == 'Options': - return '' - return heading + ':\n' + def format_heading(self, heading: str) -> str: + if heading == "Options": + return "" + return heading + ":\n" - def format_usage(self, usage): + def format_usage(self, usage: str) -> str: """ Ensure there is only one newline between usage and the first heading if there is no description. """ - msg = '\nUsage: {}\n'.format( - self.indent_lines(textwrap.dedent(usage), " ")) + msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " ")) return msg - def format_description(self, description): + def format_description(self, description: str) -> str: # leave full control over description to us if description: - if hasattr(self.parser, 'main'): - label = 'Commands' + if hasattr(self.parser, "main"): + label = "Commands" else: - label = 'Description' + label = "Description" # some doc strings have initial newlines, some don't - description = description.lstrip('\n') + description = description.lstrip("\n") # some doc strings have final newlines and spaces, some don't description = description.rstrip() # dedent, then reindent description = self.indent_lines(textwrap.dedent(description), " ") - description = '{}:\n{}\n'.format(label, description) + description = f"{label}:\n{description}\n" return description else: - return '' + return "" - def format_epilog(self, epilog): + def format_epilog(self, epilog: str) -> str: # leave full control over epilog to us if epilog: return epilog else: - return '' + return "" - def indent_lines(self, text, indent): - new_lines = [indent + line for line in text.split('\n')] + def indent_lines(self, text: str, indent: str) -> str: + new_lines = [indent + line for line in text.split("\n")] return "\n".join(new_lines) @@ -105,17 +102,37 @@ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): This is updates the defaults before expanding them, allowing them to show up correctly in the help listing. + + Also redact auth from url type options """ - def expand_default(self, option): + def expand_default(self, option: optparse.Option) -> str: + default_values = None if self.parser is not None: + assert isinstance(self.parser, ConfigOptionParser) self.parser._update_defaults(self.parser.defaults) - return optparse.IndentedHelpFormatter.expand_default(self, option) + assert option.dest is not None + default_values = self.parser.defaults.get(option.dest) + help_text = super().expand_default(option) + if default_values and option.metavar == "URL": + if isinstance(default_values, str): + default_values = [default_values] -class CustomOptionParser(optparse.OptionParser): + # If its not a list, we should abort and just return the help text + if not isinstance(default_values, list): + default_values = [] - def insert_option_group(self, idx, *args, **kwargs): + for val in default_values: + help_text = help_text.replace(val, redact_auth_from_url(val)) + + return help_text + + +class CustomOptionParser(optparse.OptionParser): + def insert_option_group( + self, idx: int, *args: Any, **kwargs: Any + ) -> optparse.OptionGroup: """Insert an OptionGroup at a given position.""" group = self.add_option_group(*args, **kwargs) @@ -125,7 +142,7 @@ class CustomOptionParser(optparse.OptionParser): return group @property - def option_list_all(self): + def option_list_all(self) -> List[optparse.Option]: """Get a list of all options, including those in option groups.""" res = self.option_list[:] for i in self.option_groups: @@ -138,34 +155,40 @@ class ConfigOptionParser(CustomOptionParser): """Custom option parser which updates its defaults by checking the configuration files and environmental variables""" - def __init__(self, *args, **kwargs): - self.name = kwargs.pop('name') - - isolated = kwargs.pop("isolated", False) + def __init__( + self, + *args: Any, + name: str, + isolated: bool = False, + **kwargs: Any, + ) -> None: + self.name = name self.config = Configuration(isolated) assert self.name - optparse.OptionParser.__init__(self, *args, **kwargs) + super().__init__(*args, **kwargs) - def check_default(self, option, key, val): + def check_default(self, option: optparse.Option, key: str, val: Any) -> Any: try: return option.check_value(key, val) except optparse.OptionValueError as exc: - print("An error occurred during configuration: {}".format(exc)) + print(f"An error occurred during configuration: {exc}") sys.exit(3) - def _get_ordered_configuration_items(self): + def _get_ordered_configuration_items(self) -> Iterator[Tuple[str, Any]]: # Configuration gives keys in an unordered manner. Order them. override_order = ["global", self.name, ":env:"] # Pool the options into different groups - section_items = {name: [] for name in override_order} + section_items: Dict[str, List[Tuple[str, Any]]] = { + name: [] for name in override_order + } for section_key, val in self.config.items(): # ignore empty values if not val: logger.debug( "Ignoring configuration key '%s' as it's value is empty.", - section_key + section_key, ) continue @@ -178,7 +201,7 @@ class ConfigOptionParser(CustomOptionParser): for key, val in section_items[section]: yield key, val - def _update_defaults(self, defaults): + def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]: """Updates the given defaults with values from the config files and the environ. Does a little special handling for certain types of options (lists).""" @@ -189,7 +212,7 @@ class ConfigOptionParser(CustomOptionParser): # Then set the options with those values for key, val in self._get_ordered_configuration_items(): # '--' because configuration supports only long names - option = self.get_option('--' + key) + option = self.get_option("--" + key) # Ignore options not present in this parser. E.g. non-globals put # in [global] by users that want them to apply to all applicable @@ -197,19 +220,34 @@ class ConfigOptionParser(CustomOptionParser): if option is None: continue - if option.action in ('store_true', 'store_false', 'count'): + assert option.dest is not None + + if option.action in ("store_true", "store_false"): try: val = strtobool(val) except ValueError: - error_msg = invalid_config_error_message( - option.action, key, val + self.error( + "{} is not a valid value for {} option, " # noqa + "please specify a boolean value like yes/no, " + "true/false or 1/0 instead.".format(val, key) ) - self.error(error_msg) - - elif option.action == 'append': + elif option.action == "count": + with suppress(ValueError): + val = strtobool(val) + with suppress(ValueError): + val = int(val) + if not isinstance(val, int) or val < 0: + self.error( + "{} is not a valid value for {} option, " # noqa + "please instead specify either a non-negative integer " + "or a boolean value like yes/no or false/true " + "which is equivalent to 1/0.".format(val, key) + ) + elif option.action == "append": val = val.split() val = [self.check_default(option, key, v) for v in val] - elif option.action == 'callback': + elif option.action == "callback": + assert option.callback is not None late_eval.add(option.dest) opt_str = option.get_opt_string() val = option.convert_value(opt_str, val) @@ -227,7 +265,7 @@ class ConfigOptionParser(CustomOptionParser): self.values = None return defaults - def get_default_values(self): + def get_default_values(self) -> optparse.Values: """Overriding to make updating the defaults after instantiation of the option parser possible, _update_defaults() does the dirty work.""" if not self.process_default_values: @@ -242,25 +280,13 @@ class ConfigOptionParser(CustomOptionParser): defaults = self._update_defaults(self.defaults.copy()) # ours for option in self._get_all_options(): + assert option.dest is not None default = defaults.get(option.dest) - if isinstance(default, string_types): + if isinstance(default, str): opt_str = option.get_opt_string() defaults[option.dest] = option.check_value(opt_str, default) return optparse.Values(defaults) - def error(self, msg): + def error(self, msg: str) -> None: self.print_usage(sys.stderr) - self.exit(UNKNOWN_ERROR, "{}\n".format(msg)) - - -def invalid_config_error_message(action, key, val): - """Returns a better error message when invalid configuration option - is provided.""" - if action in ('store_true', 'store_false'): - return ("{0} is not a valid value for {1} option, " - "please specify a boolean value like yes/no, " - "true/false or 1/0 instead.").format(val, key) - - return ("{0} is not a valid value for {1} option, " - "please specify a numerical value like 1/0 " - "instead.").format(val, key) + self.exit(UNKNOWN_ERROR, f"{msg}\n") diff --git a/venv/Lib/site-packages/pip/_internal/cli/progress_bars.py b/venv/Lib/site-packages/pip/_internal/cli/progress_bars.py index 7ed224790cf6e4618772793f625a729e10197f78..f3db2951986495e71792a69d46ed651b2e1bcb2c 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/progress_bars.py +++ b/venv/Lib/site-packages/pip/_internal/cli/progress_bars.py @@ -1,20 +1,14 @@ -from __future__ import division - import itertools import sys from signal import SIGINT, default_int_handler, signal +from typing import Any -from pip._vendor import six from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar from pip._vendor.progress.spinner import Spinner from pip._internal.utils.compat import WINDOWS from pip._internal.utils.logging import get_indentation from pip._internal.utils.misc import format_size -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Any, Dict, List try: from pip._vendor import colorama @@ -24,8 +18,7 @@ except Exception: colorama = None -def _select_progress_class(preferred, fallback): - # type: (Bar, Bar) -> Bar +def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar: encoding = getattr(preferred.file, "encoding", None) # If we don't know what encoding this file is in, then we'll just assume @@ -36,8 +29,8 @@ def _select_progress_class(preferred, fallback): # Collect all of the possible characters we want to use with the preferred # bar. characters = [ - getattr(preferred, "empty_fill", six.text_type()), - getattr(preferred, "fill", six.text_type()), + getattr(preferred, "empty_fill", ""), + getattr(preferred, "fill", ""), ] characters += list(getattr(preferred, "phases", [])) @@ -45,17 +38,17 @@ def _select_progress_class(preferred, fallback): # of the given file, if this works then we'll assume that we can use the # fancier bar and if not we'll fall back to the plaintext bar. try: - six.text_type().join(characters).encode(encoding) + "".join(characters).encode(encoding) except UnicodeEncodeError: return fallback else: return preferred -_BaseBar = _select_progress_class(IncrementalBar, Bar) # type: Any +_BaseBar: Any = _select_progress_class(IncrementalBar, Bar) -class InterruptibleMixin(object): +class InterruptibleMixin: """ Helper to ensure that self.finish() gets called on keyboard interrupt. @@ -73,15 +66,12 @@ class InterruptibleMixin(object): download has already completed, for example. """ - def __init__(self, *args, **kwargs): - # type: (List[Any], Dict[Any, Any]) -> None + def __init__(self, *args: Any, **kwargs: Any) -> None: """ Save the original SIGINT handler for later. """ - super(InterruptibleMixin, self).__init__( # type: ignore - *args, - **kwargs - ) + # https://github.com/python/mypy/issues/5887 + super().__init__(*args, **kwargs) # type: ignore self.original_handler = signal(SIGINT, self.handle_sigint) @@ -93,15 +83,14 @@ class InterruptibleMixin(object): if self.original_handler is None: self.original_handler = default_int_handler - def finish(self): - # type: () -> None + def finish(self) -> None: """ Restore the original SIGINT handler after finishing. This should happen regardless of whether the progress display finishes normally, or gets interrupted. """ - super(InterruptibleMixin, self).finish() # type: ignore + super().finish() # type: ignore signal(SIGINT, self.original_handler) def handle_sigint(self, signum, frame): # type: ignore @@ -116,9 +105,7 @@ class InterruptibleMixin(object): class SilentBar(Bar): - - def update(self): - # type: () -> None + def update(self) -> None: pass @@ -127,52 +114,43 @@ class BlueEmojiBar(IncrementalBar): suffix = "%(percent)d%%" bar_prefix = " " bar_suffix = " " - phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any + phases = ("\U0001F539", "\U0001F537", "\U0001F535") -class DownloadProgressMixin(object): - - def __init__(self, *args, **kwargs): - # type: (List[Any], Dict[Any, Any]) -> None - super(DownloadProgressMixin, self).__init__( # type: ignore - *args, - **kwargs - ) - self.message = (" " * ( - get_indentation() + 2 - )) + self.message # type: str +class DownloadProgressMixin: + def __init__(self, *args: Any, **kwargs: Any) -> None: + # https://github.com/python/mypy/issues/5887 + super().__init__(*args, **kwargs) # type: ignore + self.message: str = (" " * (get_indentation() + 2)) + self.message @property - def downloaded(self): - # type: () -> str + def downloaded(self) -> str: return format_size(self.index) # type: ignore @property - def download_speed(self): - # type: () -> str + def download_speed(self) -> str: # Avoid zero division errors... if self.avg == 0.0: # type: ignore return "..." return format_size(1 / self.avg) + "/s" # type: ignore @property - def pretty_eta(self): - # type: () -> str + def pretty_eta(self) -> str: if self.eta: # type: ignore - return "eta {}".format(self.eta_td) # type: ignore + return f"eta {self.eta_td}" # type: ignore return "" def iter(self, it): # type: ignore for x in it: yield x - self.next(len(x)) + # B305 is incorrectly raised here + # https://github.com/PyCQA/flake8-bugbear/issues/59 + self.next(len(x)) # noqa: B305 self.finish() -class WindowsMixin(object): - - def __init__(self, *args, **kwargs): - # type: (List[Any], Dict[Any, Any]) -> None +class WindowsMixin: + def __init__(self, *args: Any, **kwargs: Any) -> None: # The Windows terminal does not support the hide/show cursor ANSI codes # even with colorama. So we'll ensure that hide_cursor is False on # Windows. @@ -183,7 +161,8 @@ class WindowsMixin(object): if WINDOWS and self.hide_cursor: # type: ignore self.hide_cursor = False - super(WindowsMixin, self).__init__(*args, **kwargs) # type: ignore + # https://github.com/python/mypy/issues/5887 + super().__init__(*args, **kwargs) # type: ignore # Check if we are running on Windows and we have the colorama module, # if we do then wrap our file with it. @@ -199,64 +178,58 @@ class WindowsMixin(object): self.file.flush = lambda: self.file.wrapped.flush() -class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, - DownloadProgressMixin): +class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, DownloadProgressMixin): file = sys.stdout message = "%(percent)d%%" suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" -# NOTE: The "type: ignore" comments on the following classes are there to -# work around https://github.com/python/typing/issues/241 - -class DefaultDownloadProgressBar(BaseDownloadProgressBar, - _BaseBar): +class DefaultDownloadProgressBar(BaseDownloadProgressBar, _BaseBar): pass -class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): # type: ignore +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): pass -class DownloadBar(BaseDownloadProgressBar, # type: ignore - Bar): +class DownloadBar(BaseDownloadProgressBar, Bar): pass -class DownloadFillingCirclesBar(BaseDownloadProgressBar, # type: ignore - FillingCirclesBar): +class DownloadFillingCirclesBar(BaseDownloadProgressBar, FillingCirclesBar): pass -class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, # type: ignore - BlueEmojiBar): +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, BlueEmojiBar): pass -class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, - DownloadProgressMixin, Spinner): +class DownloadProgressSpinner( + WindowsMixin, InterruptibleMixin, DownloadProgressMixin, Spinner +): file = sys.stdout suffix = "%(downloaded)s %(download_speed)s" - def next_phase(self): # type: ignore + def next_phase(self) -> str: if not hasattr(self, "_phaser"): self._phaser = itertools.cycle(self.phases) return next(self._phaser) - def update(self): - # type: () -> None + def update(self) -> None: message = self.message % self phase = self.next_phase() suffix = self.suffix % self - line = ''.join([ - message, - " " if message else "", - phase, - " " if suffix else "", - suffix, - ]) + line = "".join( + [ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ] + ) self.writeln(line) @@ -266,7 +239,7 @@ BAR_TYPES = { "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), "ascii": (DownloadBar, DownloadProgressSpinner), "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), - "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner), } diff --git a/venv/Lib/site-packages/pip/_internal/cli/req_command.py b/venv/Lib/site-packages/pip/_internal/cli/req_command.py index 104b033281fa9ba2d3e66d1abe2b0358fc39607d..dbd15cbce634638cb9af473b71e3cc3656fa97c7 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/req_command.py +++ b/venv/Lib/site-packages/pip/_internal/cli/req_command.py @@ -7,15 +7,20 @@ PackageFinder machinery and all its vendored dependencies, etc. import logging import os +import sys from functools import partial +from optparse import Values +from typing import Any, List, Optional, Tuple +from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command from pip._internal.cli.command_context import CommandContextMixIn from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder from pip._internal.models.selection_prefs import SelectionPreferences -from pip._internal.network.download import Downloader +from pip._internal.models.target_python import TargetPython from pip._internal.network.session import PipSession from pip._internal.operations.prepare import RequirementPreparer from pip._internal.req.constructors import ( @@ -25,28 +30,17 @@ from pip._internal.req.constructors import ( install_req_from_req_string, ) from pip._internal.req.req_file import parse_requirements -from pip._internal.req.req_set import RequirementSet -from pip._internal.self_outdated_check import ( - make_link_collector, - pip_self_version_check, +from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolution.base import BaseResolver +from pip._internal.self_outdated_check import pip_self_version_check +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.temp_dir import ( + TempDirectory, + TempDirectoryTypeRegistry, + tempdir_kinds, ) -from pip._internal.utils.temp_dir import tempdir_kinds -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Any, List, Optional, Tuple - - from pip._internal.cache import WheelCache - from pip._internal.models.target_python import TargetPython - from pip._internal.req.req_install import InstallRequirement - from pip._internal.req.req_tracker import RequirementTracker - from pip._internal.resolution.base import BaseResolver - from pip._internal.utils.temp_dir import ( - TempDirectory, - TempDirectoryTypeRegistry, - ) - +from pip._internal.utils.virtualenv import running_under_virtualenv logger = logging.getLogger(__name__) @@ -56,14 +50,13 @@ class SessionCommandMixin(CommandContextMixIn): """ A class mixin for command classes needing _build_session(). """ - def __init__(self): - # type: () -> None - super(SessionCommandMixin, self).__init__() - self._session = None # Optional[PipSession] + + def __init__(self) -> None: + super().__init__() + self._session: Optional[PipSession] = None @classmethod - def _get_index_urls(cls, options): - # type: (Values) -> Optional[List[str]] + def _get_index_urls(cls, options: Values) -> Optional[List[str]]: """Return a list of index urls from user-provided options.""" index_urls = [] if not getattr(options, "no_index", False): @@ -76,8 +69,7 @@ class SessionCommandMixin(CommandContextMixIn): # Return None rather than an empty list return index_urls or None - def get_default_session(self, options): - # type: (Values) -> PipSession + def get_default_session(self, options: Values) -> PipSession: """Get a default-managed session.""" if self._session is None: self._session = self.enter_context(self._build_session(options)) @@ -87,13 +79,16 @@ class SessionCommandMixin(CommandContextMixIn): assert self._session is not None return self._session - def _build_session(self, options, retries=None, timeout=None): - # type: (Values, Optional[int], Optional[int]) -> PipSession + def _build_session( + self, + options: Values, + retries: Optional[int] = None, + timeout: Optional[int] = None, + ) -> PipSession: assert not options.cache_dir or os.path.isabs(options.cache_dir) session = PipSession( cache=( - os.path.join(options.cache_dir, "http") - if options.cache_dir else None + os.path.join(options.cache_dir, "http") if options.cache_dir else None ), retries=retries if retries is not None else options.retries, trusted_hosts=options.trusted_hosts, @@ -110,9 +105,7 @@ class SessionCommandMixin(CommandContextMixIn): # Handle timeouts if options.timeout or timeout: - session.timeout = ( - timeout if timeout is not None else options.timeout - ) + session.timeout = timeout if timeout is not None else options.timeout # Handle configured proxies if options.proxy: @@ -135,24 +128,21 @@ class IndexGroupCommand(Command, SessionCommandMixin): This also corresponds to the commands that permit the pip version check. """ - def handle_pip_version_check(self, options): - # type: (Values) -> None + def handle_pip_version_check(self, options: Values) -> None: """ Do the pip version check if not disabled. This overrides the default behavior of not doing the check. """ # Make sure the index_group options are present. - assert hasattr(options, 'no_index') + assert hasattr(options, "no_index") if options.disable_pip_version_check or options.no_index: return # Otherwise, check if we're using the latest version of pip available. session = self._build_session( - options, - retries=0, - timeout=min(5, options.timeout) + options, retries=0, timeout=min(5, options.timeout) ) with session: pip_self_version_check(session, options) @@ -165,18 +155,48 @@ KEEPABLE_TEMPDIR_TYPES = [ ] -def with_cleanup(func): - # type: (Any) -> Any +def warn_if_run_as_root() -> None: + """Output a warning for sudo users on Unix. + + In a virtual environment, sudo pip still writes to virtualenv. + On Windows, users may run pip as Administrator without issues. + This warning only applies to Unix root users outside of virtualenv. + """ + if running_under_virtualenv(): + return + if not hasattr(os, "getuid"): + return + # On Windows, there are no "system managed" Python packages. Installing as + # Administrator via pip is the correct way of updating system environments. + # + # We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform + # checks: https://mypy.readthedocs.io/en/stable/common_issues.html + if sys.platform == "win32" or sys.platform == "cygwin": + return + + if os.getuid() != 0: + return + + logger.warning( + "Running pip as the 'root' user can result in broken permissions and " + "conflicting behaviour with the system package manager. " + "It is recommended to use a virtual environment instead: " + "https://pip.pypa.io/warnings/venv" + ) + + +def with_cleanup(func: Any) -> Any: """Decorator for common logic related to managing temporary directories. """ - def configure_tempdir_registry(registry): - # type: (TempDirectoryTypeRegistry) -> None + + def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None: for t in KEEPABLE_TEMPDIR_TYPES: registry.set_delete(t, False) - def wrapper(self, options, args): - # type: (RequirementCommand, Values, List[Any]) -> Optional[int] + def wrapper( + self: RequirementCommand, options: Values, args: List[Any] + ) -> Optional[int]: assert self.tempdir_registry is not None if options.no_clean: configure_tempdir_registry(self.tempdir_registry) @@ -194,61 +214,98 @@ def with_cleanup(func): class RequirementCommand(IndexGroupCommand): - - def __init__(self, *args, **kw): - # type: (Any, Any) -> None - super(RequirementCommand, self).__init__(*args, **kw) + def __init__(self, *args: Any, **kw: Any) -> None: + super().__init__(*args, **kw) self.cmd_opts.add_option(cmdoptions.no_clean()) @staticmethod + def determine_resolver_variant(options: Values) -> str: + """Determines which resolver should be used, based on the given options.""" + if "legacy-resolver" in options.deprecated_features_enabled: + return "legacy" + + return "2020-resolver" + + @classmethod def make_requirement_preparer( - temp_build_dir, # type: TempDirectory - options, # type: Values - req_tracker, # type: RequirementTracker - session, # type: PipSession - finder, # type: PackageFinder - use_user_site, # type: bool - download_dir=None, # type: str - wheel_download_dir=None, # type: str - ): - # type: (...) -> RequirementPreparer + cls, + temp_build_dir: TempDirectory, + options: Values, + req_tracker: RequirementTracker, + session: PipSession, + finder: PackageFinder, + use_user_site: bool, + download_dir: Optional[str] = None, + ) -> RequirementPreparer: """ Create a RequirementPreparer instance for the given parameters. """ - downloader = Downloader(session, progress_bar=options.progress_bar) - temp_build_dir_path = temp_build_dir.path assert temp_build_dir_path is not None + resolver_variant = cls.determine_resolver_variant(options) + if resolver_variant == "2020-resolver": + lazy_wheel = "fast-deps" in options.features_enabled + if lazy_wheel: + logger.warning( + "pip is using lazily downloaded wheels using HTTP " + "range requests to obtain dependency information. " + "This experimental feature is enabled through " + "--use-feature=fast-deps and it is not ready for " + "production." + ) + else: + lazy_wheel = False + if "fast-deps" in options.features_enabled: + logger.warning( + "fast-deps has no effect when used with the legacy resolver." + ) + + in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled + if "in-tree-build" in options.features_enabled: + deprecated( + reason="In-tree builds are now the default.", + replacement="to remove the --use-feature=in-tree-build flag", + gone_in="22.1", + ) + if "out-of-tree-build" in options.deprecated_features_enabled: + deprecated( + reason="Out-of-tree builds are deprecated.", + replacement=None, + gone_in="22.1", + ) + return RequirementPreparer( build_dir=temp_build_dir_path, src_dir=options.src_dir, download_dir=download_dir, - wheel_download_dir=wheel_download_dir, build_isolation=options.build_isolation, req_tracker=req_tracker, - downloader=downloader, + session=session, + progress_bar=options.progress_bar, finder=finder, require_hashes=options.require_hashes, use_user_site=use_user_site, + lazy_wheel=lazy_wheel, + in_tree_build=in_tree_build, ) - @staticmethod + @classmethod def make_resolver( - preparer, # type: RequirementPreparer - finder, # type: PackageFinder - options, # type: Values - wheel_cache=None, # type: Optional[WheelCache] - use_user_site=False, # type: bool - ignore_installed=True, # type: bool - ignore_requires_python=False, # type: bool - force_reinstall=False, # type: bool - upgrade_strategy="to-satisfy-only", # type: str - use_pep517=None, # type: Optional[bool] - py_version_info=None # type: Optional[Tuple[int, ...]] - ): - # type: (...) -> BaseResolver + cls, + preparer: RequirementPreparer, + finder: PackageFinder, + options: Values, + wheel_cache: Optional[WheelCache] = None, + use_user_site: bool = False, + ignore_installed: bool = True, + ignore_requires_python: bool = False, + force_reinstall: bool = False, + upgrade_strategy: str = "to-satisfy-only", + use_pep517: Optional[bool] = None, + py_version_info: Optional[Tuple[int, ...]] = None, + ) -> BaseResolver: """ Create a Resolver instance for the given parameters. """ @@ -257,11 +314,13 @@ class RequirementCommand(IndexGroupCommand): isolated=options.isolated_mode, use_pep517=use_pep517, ) + resolver_variant = cls.determine_resolver_variant(options) # The long import name and duplicated invocation is needed to convince # Mypy into correctly typechecking. Otherwise it would complain the # "Resolver" class being redefined. - if 'resolver' in options.unstable_features: + if resolver_variant == "2020-resolver": import pip._internal.resolution.resolvelib.resolver + return pip._internal.resolution.resolvelib.resolver.Resolver( preparer=preparer, finder=finder, @@ -276,6 +335,7 @@ class RequirementCommand(IndexGroupCommand): py_version_info=py_version_info, ) import pip._internal.resolution.legacy.resolver + return pip._internal.resolution.legacy.resolver.Resolver( preparer=preparer, finder=finder, @@ -292,83 +352,85 @@ class RequirementCommand(IndexGroupCommand): def get_requirements( self, - args, # type: List[str] - options, # type: Values - finder, # type: PackageFinder - session, # type: PipSession - check_supported_wheels=True, # type: bool - ): - # type: (...) -> List[InstallRequirement] + args: List[str], + options: Values, + finder: PackageFinder, + session: PipSession, + ) -> List[InstallRequirement]: """ Parse command-line arguments into the corresponding requirements. """ - requirement_set = RequirementSet( - check_supported_wheels=check_supported_wheels - ) + requirements: List[InstallRequirement] = [] for filename in options.constraints: for parsed_req in parse_requirements( - filename, - constraint=True, finder=finder, options=options, - session=session): + filename, + constraint=True, + finder=finder, + options=options, + session=session, + ): req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, + user_supplied=False, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) for req in args: req_to_add = install_req_from_line( - req, None, isolated=options.isolated_mode, + req, + None, + isolated=options.isolated_mode, use_pep517=options.use_pep517, + user_supplied=True, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, + user_supplied=True, isolated=options.isolated_mode, use_pep517=options.use_pep517, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True for filename in options.requirements: for parsed_req in parse_requirements( - filename, - finder=finder, options=options, session=session): + filename, finder=finder, options=options, session=session + ): req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, - use_pep517=options.use_pep517 + use_pep517=options.use_pep517, + user_supplied=True, ) - req_to_add.is_direct = True - requirement_set.add_requirement(req_to_add) + requirements.append(req_to_add) # If any requirement has hash options, enable hash checking. - requirements = requirement_set.all_requirements if any(req.has_hash_options for req in requirements): options.require_hashes = True if not (args or options.editables or options.requirements): - opts = {'name': self.name} + opts = {"name": self.name} if options.find_links: raise CommandError( - 'You must give at least one requirement to {name} ' + "You must give at least one requirement to {name} " '(maybe you meant "pip {name} {links}"?)'.format( - **dict(opts, links=' '.join(options.find_links)))) + **dict(opts, links=" ".join(options.find_links)) + ) + ) else: raise CommandError( - 'You must give at least one requirement to {name} ' - '(see "pip help {name}")'.format(**opts)) + "You must give at least one requirement to {name} " + '(see "pip help {name}")'.format(**opts) + ) return requirements @staticmethod - def trace_basic_info(finder): - # type: (PackageFinder) -> None + def trace_basic_info(finder: PackageFinder) -> None: """ Trace basic information about the provided objects. """ @@ -380,19 +442,18 @@ class RequirementCommand(IndexGroupCommand): def _build_package_finder( self, - options, # type: Values - session, # type: PipSession - target_python=None, # type: Optional[TargetPython] - ignore_requires_python=None, # type: Optional[bool] - ): - # type: (...) -> PackageFinder + options: Values, + session: PipSession, + target_python: Optional[TargetPython] = None, + ignore_requires_python: Optional[bool] = None, + ) -> PackageFinder: """ Create a package finder appropriate to this requirement command. :param ignore_requires_python: Whether to ignore incompatible "Requires-Python" values in links. Defaults to False. """ - link_collector = make_link_collector(session, options=options) + link_collector = LinkCollector.create(session, options=options) selection_prefs = SelectionPreferences( allow_yanked=True, format_control=options.format_control, diff --git a/venv/Lib/site-packages/pip/_internal/cli/spinners.py b/venv/Lib/site-packages/pip/_internal/cli/spinners.py index c6c4c5cd1b1bebd4f531fda7d4416aab689f4879..1e313e1090ad02f984d96b11b1b47fc72301fb94 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/spinners.py +++ b/venv/Lib/site-packages/pip/_internal/cli/spinners.py @@ -1,38 +1,35 @@ -from __future__ import absolute_import, division - import contextlib import itertools import logging import sys import time +from typing import IO, Iterator from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR from pip._internal.utils.compat import WINDOWS from pip._internal.utils.logging import get_indentation -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Iterator, IO logger = logging.getLogger(__name__) -class SpinnerInterface(object): - def spin(self): - # type: () -> None +class SpinnerInterface: + def spin(self) -> None: raise NotImplementedError() - def finish(self, final_status): - # type: (str) -> None + def finish(self, final_status: str) -> None: raise NotImplementedError() class InteractiveSpinner(SpinnerInterface): - def __init__(self, message, file=None, spin_chars="-\\|/", - # Empirically, 8 updates/second looks nice - min_update_interval_seconds=0.125): - # type: (str, IO[str], str, float) -> None + def __init__( + self, + message: str, + file: IO[str] = None, + spin_chars: str = "-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds: float = 0.125, + ): self._message = message if file is None: file = sys.stdout @@ -45,8 +42,7 @@ class InteractiveSpinner(SpinnerInterface): self._file.write(" " * get_indentation() + self._message + " ... ") self._width = 0 - def _write(self, status): - # type: (str) -> None + def _write(self, status: str) -> None: assert not self._finished # Erase what we wrote before by backspacing to the beginning, writing # spaces to overwrite the old text, and then backspacing again @@ -58,16 +54,14 @@ class InteractiveSpinner(SpinnerInterface): self._file.flush() self._rate_limiter.reset() - def spin(self): - # type: () -> None + def spin(self) -> None: if self._finished: return if not self._rate_limiter.ready(): return self._write(next(self._spin_cycle)) - def finish(self, final_status): - # type: (str) -> None + def finish(self, final_status: str) -> None: if self._finished: return self._write(final_status) @@ -81,63 +75,54 @@ class InteractiveSpinner(SpinnerInterface): # act as a keep-alive for systems like Travis-CI that take lack-of-output as # an indication that a task has frozen. class NonInteractiveSpinner(SpinnerInterface): - def __init__(self, message, min_update_interval_seconds=60): - # type: (str, float) -> None + def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None: self._message = message self._finished = False self._rate_limiter = RateLimiter(min_update_interval_seconds) self._update("started") - def _update(self, status): - # type: (str) -> None + def _update(self, status: str) -> None: assert not self._finished self._rate_limiter.reset() logger.info("%s: %s", self._message, status) - def spin(self): - # type: () -> None + def spin(self) -> None: if self._finished: return if not self._rate_limiter.ready(): return self._update("still running...") - def finish(self, final_status): - # type: (str) -> None + def finish(self, final_status: str) -> None: if self._finished: return - self._update( - "finished with status '{final_status}'".format(**locals())) + self._update(f"finished with status '{final_status}'") self._finished = True -class RateLimiter(object): - def __init__(self, min_update_interval_seconds): - # type: (float) -> None +class RateLimiter: + def __init__(self, min_update_interval_seconds: float) -> None: self._min_update_interval_seconds = min_update_interval_seconds - self._last_update = 0 # type: float + self._last_update: float = 0 - def ready(self): - # type: () -> bool + def ready(self) -> bool: now = time.time() delta = now - self._last_update return delta >= self._min_update_interval_seconds - def reset(self): - # type: () -> None + def reset(self) -> None: self._last_update = time.time() @contextlib.contextmanager -def open_spinner(message): - # type: (str) -> Iterator[SpinnerInterface] +def open_spinner(message: str) -> Iterator[SpinnerInterface]: # Interactive spinner goes directly to sys.stdout rather than being routed # through the logging system, but it acts like it has level INFO, # i.e. it's only displayed if we're at level INFO or better. # Non-interactive spinner goes through the logging system, so it is always # in sync with logging configuration. if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: - spinner = InteractiveSpinner(message) # type: SpinnerInterface + spinner: SpinnerInterface = InteractiveSpinner(message) else: spinner = NonInteractiveSpinner(message) try: @@ -154,8 +139,7 @@ def open_spinner(message): @contextlib.contextmanager -def hidden_cursor(file): - # type: (IO[str]) -> Iterator[None] +def hidden_cursor(file: IO[str]) -> Iterator[None]: # The Windows terminal does not support the hide/show cursor ANSI codes, # even via colorama. So don't even try. if WINDOWS: diff --git a/venv/Lib/site-packages/pip/_internal/cli/status_codes.py b/venv/Lib/site-packages/pip/_internal/cli/status_codes.py index 275360a3175abaeab86148d61b735904f96d72f6..5e29502cddfa9a9887a93399ab4193fb75dfe605 100644 --- a/venv/Lib/site-packages/pip/_internal/cli/status_codes.py +++ b/venv/Lib/site-packages/pip/_internal/cli/status_codes.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - SUCCESS = 0 ERROR = 1 UNKNOWN_ERROR = 2 diff --git a/venv/Lib/site-packages/pip/_internal/commands/__init__.py b/venv/Lib/site-packages/pip/_internal/commands/__init__.py index 6825fa6e2d47db0f9aedfc88d840ee0358df9fdc..c72f24f30e2924c415cf45cdb8628c3cb6ecfd5d 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/__init__.py +++ b/venv/Lib/site-packages/pip/_internal/commands/__init__.py @@ -2,101 +2,106 @@ Package containing all pip commands """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False -# There is currently a bug in python/typeshed mentioned at -# https://github.com/python/typeshed/issues/3906 which causes the -# return type of difflib.get_close_matches to be reported -# as List[Sequence[str]] whereas it should have been List[str] - -from __future__ import absolute_import - import importlib -from collections import OrderedDict, namedtuple - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Any - from pip._internal.cli.base_command import Command - - -CommandInfo = namedtuple('CommandInfo', 'module_path, class_name, summary') - -# The ordering matters for help display. -# Also, even though the module path starts with the same -# "pip._internal.commands" prefix in each case, we include the full path -# because it makes testing easier (specifically when modifying commands_dict -# in test setup / teardown by adding info for a FakeCommand class defined -# in a test-related module). -# Finally, we need to pass an iterable of pairs here rather than a dict -# so that the ordering won't be lost when using Python 2.7. -commands_dict = OrderedDict([ - ('install', CommandInfo( - 'pip._internal.commands.install', 'InstallCommand', - 'Install packages.', - )), - ('download', CommandInfo( - 'pip._internal.commands.download', 'DownloadCommand', - 'Download packages.', - )), - ('uninstall', CommandInfo( - 'pip._internal.commands.uninstall', 'UninstallCommand', - 'Uninstall packages.', - )), - ('freeze', CommandInfo( - 'pip._internal.commands.freeze', 'FreezeCommand', - 'Output installed packages in requirements format.', - )), - ('list', CommandInfo( - 'pip._internal.commands.list', 'ListCommand', - 'List installed packages.', - )), - ('show', CommandInfo( - 'pip._internal.commands.show', 'ShowCommand', - 'Show information about installed packages.', - )), - ('check', CommandInfo( - 'pip._internal.commands.check', 'CheckCommand', - 'Verify installed packages have compatible dependencies.', - )), - ('config', CommandInfo( - 'pip._internal.commands.configuration', 'ConfigurationCommand', - 'Manage local and global configuration.', - )), - ('search', CommandInfo( - 'pip._internal.commands.search', 'SearchCommand', - 'Search PyPI for packages.', - )), - ('cache', CommandInfo( - 'pip._internal.commands.cache', 'CacheCommand', +from collections import namedtuple +from typing import Any, Dict, Optional + +from pip._internal.cli.base_command import Command + +CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary") + +# This dictionary does a bunch of heavy lifting for help output: +# - Enables avoiding additional (costly) imports for presenting `--help`. +# - The ordering matters for help display. +# +# Even though the module path starts with the same "pip._internal.commands" +# prefix, the full path makes testing easier (specifically when modifying +# `commands_dict` in test setup / teardown). +commands_dict: Dict[str, CommandInfo] = { + "install": CommandInfo( + "pip._internal.commands.install", + "InstallCommand", + "Install packages.", + ), + "download": CommandInfo( + "pip._internal.commands.download", + "DownloadCommand", + "Download packages.", + ), + "uninstall": CommandInfo( + "pip._internal.commands.uninstall", + "UninstallCommand", + "Uninstall packages.", + ), + "freeze": CommandInfo( + "pip._internal.commands.freeze", + "FreezeCommand", + "Output installed packages in requirements format.", + ), + "list": CommandInfo( + "pip._internal.commands.list", + "ListCommand", + "List installed packages.", + ), + "show": CommandInfo( + "pip._internal.commands.show", + "ShowCommand", + "Show information about installed packages.", + ), + "check": CommandInfo( + "pip._internal.commands.check", + "CheckCommand", + "Verify installed packages have compatible dependencies.", + ), + "config": CommandInfo( + "pip._internal.commands.configuration", + "ConfigurationCommand", + "Manage local and global configuration.", + ), + "search": CommandInfo( + "pip._internal.commands.search", + "SearchCommand", + "Search PyPI for packages.", + ), + "cache": CommandInfo( + "pip._internal.commands.cache", + "CacheCommand", "Inspect and manage pip's wheel cache.", - )), - ('wheel', CommandInfo( - 'pip._internal.commands.wheel', 'WheelCommand', - 'Build wheels from your requirements.', - )), - ('hash', CommandInfo( - 'pip._internal.commands.hash', 'HashCommand', - 'Compute hashes of package archives.', - )), - ('completion', CommandInfo( - 'pip._internal.commands.completion', 'CompletionCommand', - 'A helper command used for command completion.', - )), - ('debug', CommandInfo( - 'pip._internal.commands.debug', 'DebugCommand', - 'Show information useful for debugging.', - )), - ('help', CommandInfo( - 'pip._internal.commands.help', 'HelpCommand', - 'Show help for commands.', - )), -]) # type: OrderedDict[str, CommandInfo] - - -def create_command(name, **kwargs): - # type: (str, **Any) -> Command + ), + "index": CommandInfo( + "pip._internal.commands.index", + "IndexCommand", + "Inspect information available from package indexes.", + ), + "wheel": CommandInfo( + "pip._internal.commands.wheel", + "WheelCommand", + "Build wheels from your requirements.", + ), + "hash": CommandInfo( + "pip._internal.commands.hash", + "HashCommand", + "Compute hashes of package archives.", + ), + "completion": CommandInfo( + "pip._internal.commands.completion", + "CompletionCommand", + "A helper command used for command completion.", + ), + "debug": CommandInfo( + "pip._internal.commands.debug", + "DebugCommand", + "Show information useful for debugging.", + ), + "help": CommandInfo( + "pip._internal.commands.help", + "HelpCommand", + "Show help for commands.", + ), +} + + +def create_command(name: str, **kwargs: Any) -> Command: """ Create an instance of the Command class with the given name. """ @@ -108,7 +113,7 @@ def create_command(name, **kwargs): return command -def get_similar_commands(name): +def get_similar_commands(name: str) -> Optional[str]: """Command name auto-correct.""" from difflib import get_close_matches @@ -119,4 +124,4 @@ def get_similar_commands(name): if close_commands: return close_commands[0] else: - return False + return None diff --git a/venv/Lib/site-packages/pip/_internal/commands/cache.py b/venv/Lib/site-packages/pip/_internal/commands/cache.py index ca6d4379be39d0db155c0bb05d57a4f610722bc7..f1a489d324f6b11ba4b6d56ef0427ac5a83de96c 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/cache.py +++ b/venv/Lib/site-packages/pip/_internal/commands/cache.py @@ -1,21 +1,15 @@ -from __future__ import absolute_import - -import logging import os import textwrap +from optparse import Values +from typing import Any, List import pip._internal.utils.filesystem as filesystem from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.exceptions import CommandError, PipError -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Any, List - +from pip._internal.utils.logging import getLogger -logger = logging.getLogger(__name__) +logger = getLogger(__name__) class CacheCommand(Command): @@ -24,26 +18,38 @@ class CacheCommand(Command): Subcommands: - dir: Show the cache directory. - info: Show information about the cache. - list: List filenames of packages stored in the cache. - remove: Remove one or more package from the cache. - purge: Remove all items from the cache. + - dir: Show the cache directory. + - info: Show information about the cache. + - list: List filenames of packages stored in the cache. + - remove: Remove one or more package from the cache. + - purge: Remove all items from the cache. - can be a glob expression or a package name. + ```` can be a glob expression or a package name. """ ignore_require_venv = True usage = """ %prog dir %prog info - %prog list [] + %prog list [] [--format=[human, abspath]] %prog remove %prog purge """ - def run(self, options, args): - # type: (Values, List[Any]) -> int + def add_options(self) -> None: + + self.cmd_opts.add_option( + "--format", + action="store", + dest="list_format", + default="human", + choices=("human", "abspath"), + help="Select the output format among: human (default) or abspath", + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: handlers = { "dir": self.get_cache_dir, "info": self.get_cache_info, @@ -53,14 +59,14 @@ class CacheCommand(Command): } if not options.cache_dir: - logger.error("pip cache commands can not " - "function since cache is disabled.") + logger.error("pip cache commands can not function since cache is disabled.") return ERROR # Determine action if not args or args[0] not in handlers: - logger.error("Need an action ({}) to perform.".format( - ", ".join(sorted(handlers))) + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), ) return ERROR @@ -75,90 +81,126 @@ class CacheCommand(Command): return SUCCESS - def get_cache_dir(self, options, args): - # type: (Values, List[Any]) -> None + def get_cache_dir(self, options: Values, args: List[Any]) -> None: if args: - raise CommandError('Too many arguments') + raise CommandError("Too many arguments") logger.info(options.cache_dir) - def get_cache_info(self, options, args): - # type: (Values, List[Any]) -> None + def get_cache_info(self, options: Values, args: List[Any]) -> None: if args: - raise CommandError('Too many arguments') - - num_packages = len(self._find_wheels(options, '*')) - - cache_location = self._wheels_cache_dir(options) - cache_size = filesystem.format_directory_size(cache_location) - - message = textwrap.dedent(""" - Location: {location} - Size: {size} - Number of wheels: {package_count} - """).format( - location=cache_location, - package_count=num_packages, - size=cache_size, - ).strip() + raise CommandError("Too many arguments") + + num_http_files = len(self._find_http_files(options)) + num_packages = len(self._find_wheels(options, "*")) + + http_cache_location = self._cache_dir(options, "http") + wheels_cache_location = self._cache_dir(options, "wheels") + http_cache_size = filesystem.format_directory_size(http_cache_location) + wheels_cache_size = filesystem.format_directory_size(wheels_cache_location) + + message = ( + textwrap.dedent( + """ + Package index page cache location: {http_cache_location} + Package index page cache size: {http_cache_size} + Number of HTTP files: {num_http_files} + Wheels location: {wheels_cache_location} + Wheels size: {wheels_cache_size} + Number of wheels: {package_count} + """ + ) + .format( + http_cache_location=http_cache_location, + http_cache_size=http_cache_size, + num_http_files=num_http_files, + wheels_cache_location=wheels_cache_location, + package_count=num_packages, + wheels_cache_size=wheels_cache_size, + ) + .strip() + ) logger.info(message) - def list_cache_items(self, options, args): - # type: (Values, List[Any]) -> None + def list_cache_items(self, options: Values, args: List[Any]) -> None: if len(args) > 1: - raise CommandError('Too many arguments') + raise CommandError("Too many arguments") if args: pattern = args[0] else: - pattern = '*' + pattern = "*" files = self._find_wheels(options, pattern) + if options.list_format == "human": + self.format_for_human(files) + else: + self.format_for_abspath(files) + def format_for_human(self, files: List[str]) -> None: if not files: - logger.info('Nothing cached.') + logger.info("Nothing cached.") return results = [] for filename in files: wheel = os.path.basename(filename) size = filesystem.format_file_size(filename) - results.append(' - {} ({})'.format(wheel, size)) - logger.info('Cache contents:\n') - logger.info('\n'.join(sorted(results))) + results.append(f" - {wheel} ({size})") + logger.info("Cache contents:\n") + logger.info("\n".join(sorted(results))) - def remove_cache_items(self, options, args): - # type: (Values, List[Any]) -> None + def format_for_abspath(self, files: List[str]) -> None: + if not files: + return + + results = [] + for filename in files: + results.append(filename) + + logger.info("\n".join(sorted(results))) + + def remove_cache_items(self, options: Values, args: List[Any]) -> None: if len(args) > 1: - raise CommandError('Too many arguments') + raise CommandError("Too many arguments") if not args: - raise CommandError('Please provide a pattern') + raise CommandError("Please provide a pattern") files = self._find_wheels(options, args[0]) + + no_matching_msg = "No matching packages" + if args[0] == "*": + # Only fetch http files if no specific pattern given + files += self._find_http_files(options) + else: + # Add the pattern to the log message + no_matching_msg += ' for pattern "{}"'.format(args[0]) + if not files: - raise CommandError('No matching packages') + logger.warning(no_matching_msg) for filename in files: os.unlink(filename) - logger.debug('Removed %s', filename) - logger.info('Files removed: %s', len(files)) + logger.verbose("Removed %s", filename) + logger.info("Files removed: %s", len(files)) - def purge_cache(self, options, args): - # type: (Values, List[Any]) -> None + def purge_cache(self, options: Values, args: List[Any]) -> None: if args: - raise CommandError('Too many arguments') + raise CommandError("Too many arguments") + + return self.remove_cache_items(options, ["*"]) - return self.remove_cache_items(options, ['*']) + def _cache_dir(self, options: Values, subdir: str) -> str: + return os.path.join(options.cache_dir, subdir) - def _wheels_cache_dir(self, options): - # type: (Values) -> str - return os.path.join(options.cache_dir, 'wheels') + def _find_http_files(self, options: Values) -> List[str]: + http_dir = self._cache_dir(options, "http") + return filesystem.find_files(http_dir, "*") - def _find_wheels(self, options, pattern): - # type: (Values, str) -> List[str] - wheel_dir = self._wheels_cache_dir(options) + def _find_wheels(self, options: Values, pattern: str) -> List[str]: + wheel_dir = self._cache_dir(options, "wheels") # The wheel filename format, as specified in PEP 427, is: # {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl diff --git a/venv/Lib/site-packages/pip/_internal/commands/check.py b/venv/Lib/site-packages/pip/_internal/commands/check.py index b557ca64113c8948b2144f3a6acbed93e9bf8424..3864220b2b4a2fd3803bdff0ab9e4c3941c1f313 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/check.py +++ b/venv/Lib/site-packages/pip/_internal/commands/check.py @@ -1,4 +1,6 @@ import logging +from optparse import Values +from typing import List from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import ERROR, SUCCESS @@ -7,14 +9,9 @@ from pip._internal.operations.check import ( create_package_set_from_installed, ) from pip._internal.utils.misc import write_output -from pip._internal.utils.typing import MYPY_CHECK_RUNNING logger = logging.getLogger(__name__) -if MYPY_CHECK_RUNNING: - from typing import List, Any - from optparse import Values - class CheckCommand(Command): """Verify installed packages have compatible dependencies.""" @@ -22,8 +19,7 @@ class CheckCommand(Command): usage = """ %prog [options]""" - def run(self, options, args): - # type: (Values, List[Any]) -> int + def run(self, options: Values, args: List[str]) -> int: package_set, parsing_probs = create_package_set_from_installed() missing, conflicting = check_package_set(package_set) @@ -33,7 +29,9 @@ class CheckCommand(Command): for dependency in missing[project_name]: write_output( "%s %s requires %s, which is not installed.", - project_name, version, dependency[0], + project_name, + version, + dependency[0], ) for project_name in conflicting: @@ -41,7 +39,11 @@ class CheckCommand(Command): for dep_name, dep_version, req in conflicting[project_name]: write_output( "%s %s has requirement %s, but you have %s %s.", - project_name, version, req, dep_name, dep_version, + project_name, + version, + req, + dep_name, + dep_version, ) if missing or conflicting or parsing_probs: diff --git a/venv/Lib/site-packages/pip/_internal/commands/completion.py b/venv/Lib/site-packages/pip/_internal/commands/completion.py index 910fcbfe358d6220e71c03cb9e3409590bc11802..c0fb4caf8b275ac343ce5fafbea797db4818fcdd 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/completion.py +++ b/venv/Lib/site-packages/pip/_internal/commands/completion.py @@ -1,12 +1,10 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import sys import textwrap +from optparse import Values +from typing import List from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS from pip._internal.utils.misc import get_prog BASE_COMPLETION = """ @@ -14,7 +12,7 @@ BASE_COMPLETION = """ """ COMPLETION_SCRIPTS = { - 'bash': """ + "bash": """ _pip_completion() {{ COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\ @@ -23,7 +21,7 @@ COMPLETION_SCRIPTS = { }} complete -o default -F _pip_completion {prog} """, - 'zsh': """ + "zsh": """ function _pip_completion {{ local words cword read -Ac words @@ -34,7 +32,7 @@ COMPLETION_SCRIPTS = { }} compctl -K _pip_completion {prog} """, - 'fish': """ + "fish": """ function __fish_complete_pip set -lx COMP_WORDS (commandline -o) "" set -lx COMP_CWORD ( \\ @@ -53,43 +51,46 @@ class CompletionCommand(Command): ignore_require_venv = True - def __init__(self, *args, **kw): - super(CompletionCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - - cmd_opts.add_option( - '--bash', '-b', - action='store_const', - const='bash', - dest='shell', - help='Emit completion code for bash') - cmd_opts.add_option( - '--zsh', '-z', - action='store_const', - const='zsh', - dest='shell', - help='Emit completion code for zsh') - cmd_opts.add_option( - '--fish', '-f', - action='store_const', - const='fish', - dest='shell', - help='Emit completion code for fish') + def add_options(self) -> None: + self.cmd_opts.add_option( + "--bash", + "-b", + action="store_const", + const="bash", + dest="shell", + help="Emit completion code for bash", + ) + self.cmd_opts.add_option( + "--zsh", + "-z", + action="store_const", + const="zsh", + dest="shell", + help="Emit completion code for zsh", + ) + self.cmd_opts.add_option( + "--fish", + "-f", + action="store_const", + const="fish", + dest="shell", + help="Emit completion code for fish", + ) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: """Prints the completion code of the given shell""" shells = COMPLETION_SCRIPTS.keys() - shell_options = ['--' + shell for shell in sorted(shells)] + shell_options = ["--" + shell for shell in sorted(shells)] if options.shell in shells: script = textwrap.dedent( - COMPLETION_SCRIPTS.get(options.shell, '').format( - prog=get_prog()) + COMPLETION_SCRIPTS.get(options.shell, "").format(prog=get_prog()) ) print(BASE_COMPLETION.format(script=script, shell=options.shell)) + return SUCCESS else: sys.stderr.write( - 'ERROR: You must pass {}\n' .format(' or '.join(shell_options)) + "ERROR: You must pass {}\n".format(" or ".join(shell_options)) ) + return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/commands/configuration.py b/venv/Lib/site-packages/pip/_internal/commands/configuration.py index b801be6a03cca55307f6f3b36e81932686b28134..c6c74ed50ba39b45f1bee6ead45e4285640ce7f3 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/configuration.py +++ b/venv/Lib/site-packages/pip/_internal/commands/configuration.py @@ -1,37 +1,40 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import logging import os import subprocess +from optparse import Values +from typing import Any, List, Optional from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.configuration import ( Configuration, + Kind, get_configuration_files, kinds, ) from pip._internal.exceptions import PipError +from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import get_prog, write_output logger = logging.getLogger(__name__) class ConfigurationCommand(Command): - """Manage local and global configuration. + """ + Manage local and global configuration. Subcommands: - list: List the active configuration (or from the file specified) - edit: Edit the configuration file in an editor - get: Get the value associated with name - set: Set the name=value - unset: Unset the value associated with name + - list: List the active configuration (or from the file specified) + - edit: Edit the configuration file in an editor + - get: Get the value associated with name + - set: Set the name=value + - unset: Unset the value associated with name + - debug: List the configuration files and values defined under them If none of --user, --global and --site are passed, a virtual environment configuration file is used if one is active and the file - exists. Otherwise, all modifications happen on the to the user file by + exists. Otherwise, all modifications happen to the user file by default. """ @@ -43,63 +46,62 @@ class ConfigurationCommand(Command): %prog [] get name %prog [] set name value %prog [] unset name + %prog [] debug """ - def __init__(self, *args, **kwargs): - super(ConfigurationCommand, self).__init__(*args, **kwargs) - - self.configuration = None - + def add_options(self) -> None: self.cmd_opts.add_option( - '--editor', - dest='editor', - action='store', + "--editor", + dest="editor", + action="store", default=None, help=( - 'Editor to use to edit the file. Uses VISUAL or EDITOR ' - 'environment variables if not provided.' - ) + "Editor to use to edit the file. Uses VISUAL or EDITOR " + "environment variables if not provided." + ), ) self.cmd_opts.add_option( - '--global', - dest='global_file', - action='store_true', + "--global", + dest="global_file", + action="store_true", default=False, - help='Use the system-wide configuration file only' + help="Use the system-wide configuration file only", ) self.cmd_opts.add_option( - '--user', - dest='user_file', - action='store_true', + "--user", + dest="user_file", + action="store_true", default=False, - help='Use the user configuration file only' + help="Use the user configuration file only", ) self.cmd_opts.add_option( - '--site', - dest='site_file', - action='store_true', + "--site", + dest="site_file", + action="store_true", default=False, - help='Use the current environment configuration file only' + help="Use the current environment configuration file only", ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: handlers = { "list": self.list_values, "edit": self.open_in_editor, "get": self.get_name, "set": self.set_name_value, - "unset": self.unset_name + "unset": self.unset_name, + "debug": self.list_config_values, } # Determine action if not args or args[0] not in handlers: - logger.error("Need an action ({}) to perform.".format( - ", ".join(sorted(handlers))) + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), ) return ERROR @@ -130,12 +132,16 @@ class ConfigurationCommand(Command): return SUCCESS - def _determine_file(self, options, need_value): - file_options = [key for key, value in ( - (kinds.USER, options.user_file), - (kinds.GLOBAL, options.global_file), - (kinds.SITE, options.site_file), - ) if value] + def _determine_file(self, options: Values, need_value: bool) -> Optional[Kind]: + file_options = [ + key + for key, value in ( + (kinds.USER, options.user_file), + (kinds.GLOBAL, options.global_file), + (kinds.SITE, options.site_file), + ) + if value + ] if not file_options: if not need_value: @@ -156,31 +162,61 @@ class ConfigurationCommand(Command): "(--user, --site, --global) to perform." ) - def list_values(self, options, args): + def list_values(self, options: Values, args: List[str]) -> None: self._get_n_args(args, "list", n=0) for key, value in sorted(self.configuration.items()): write_output("%s=%r", key, value) - def get_name(self, options, args): + def get_name(self, options: Values, args: List[str]) -> None: key = self._get_n_args(args, "get [name]", n=1) value = self.configuration.get_value(key) write_output("%s", value) - def set_name_value(self, options, args): + def set_name_value(self, options: Values, args: List[str]) -> None: key, value = self._get_n_args(args, "set [name] [value]", n=2) self.configuration.set_value(key, value) self._save_configuration() - def unset_name(self, options, args): + def unset_name(self, options: Values, args: List[str]) -> None: key = self._get_n_args(args, "unset [name]", n=1) self.configuration.unset_value(key) self._save_configuration() - def open_in_editor(self, options, args): + def list_config_values(self, options: Values, args: List[str]) -> None: + """List config key-value pairs across different config files""" + self._get_n_args(args, "debug", n=0) + + self.print_env_var_values() + # Iterate over config files and print if they exist, and the + # key-value pairs present in them if they do + for variant, files in sorted(self.configuration.iter_config_files()): + write_output("%s:", variant) + for fname in files: + with indent_log(): + file_exists = os.path.exists(fname) + write_output("%s, exists: %r", fname, file_exists) + if file_exists: + self.print_config_file_values(variant) + + def print_config_file_values(self, variant: Kind) -> None: + """Get key-value pairs from the file of a variant""" + for name, value in self.configuration.get_values_in_config(variant).items(): + with indent_log(): + write_output("%s: %s", name, value) + + def print_env_var_values(self) -> None: + """Get key-values pairs present as environment variables""" + write_output("%s:", "env_var") + with indent_log(): + for key, value in sorted(self.configuration.get_environ_vars()): + env_var = f"PIP_{key.upper()}" + write_output("%s=%r", env_var, value) + + def open_in_editor(self, options: Values, args: List[str]) -> None: editor = self._determine_editor(options) fname = self.configuration.get_file_to_edit() @@ -191,16 +227,14 @@ class ConfigurationCommand(Command): subprocess.check_call([editor, fname]) except subprocess.CalledProcessError as e: raise PipError( - "Editor Subprocess exited with exit code {}" - .format(e.returncode) + "Editor Subprocess exited with exit code {}".format(e.returncode) ) - def _get_n_args(self, args, example, n): - """Helper to make sure the command got the right number of arguments - """ + def _get_n_args(self, args: List[str], example: str, n: int) -> Any: + """Helper to make sure the command got the right number of arguments""" if len(args) != n: msg = ( - 'Got unexpected number of arguments, expected {}. ' + "Got unexpected number of arguments, expected {}. " '(example: "{} config {}")' ).format(n, get_prog(), example) raise PipError(msg) @@ -210,19 +244,18 @@ class ConfigurationCommand(Command): else: return args - def _save_configuration(self): + def _save_configuration(self) -> None: # We successfully ran a modifying command. Need to save the # configuration. try: self.configuration.save() except Exception: - logger.error( - "Unable to save configuration. Please report this as a bug.", - exc_info=1 + logger.exception( + "Unable to save configuration. Please report this as a bug." ) raise PipError("Internal Error.") - def _determine_editor(self, options): + def _determine_editor(self, options: Values) -> str: if options.editor is not None: return options.editor elif "VISUAL" in os.environ: diff --git a/venv/Lib/site-packages/pip/_internal/commands/debug.py b/venv/Lib/site-packages/pip/_internal/commands/debug.py index b7c92fa6598d8da1c030f192cbe7041af9677fc2..d3f1f28de4c9529607576d19daac3104a3fd7b0a 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/debug.py +++ b/venv/Lib/site-packages/pip/_internal/commands/debug.py @@ -1,141 +1,110 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import locale import logging import os import sys +from optparse import Values +from types import ModuleType +from typing import Any, Dict, List, Optional import pip._vendor -from pip._vendor import pkg_resources from pip._vendor.certifi import where +from pip._vendor.packaging.version import parse as parse_version from pip import __file__ as pip_location from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command from pip._internal.cli.cmdoptions import make_target_python from pip._internal.cli.status_codes import SUCCESS +from pip._internal.configuration import Configuration +from pip._internal.metadata import get_environment from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import get_pip_version -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from types import ModuleType - from typing import Any, List, Optional, Dict - from optparse import Values logger = logging.getLogger(__name__) -def show_value(name, value): - # type: (str, Optional[str]) -> None - logger.info('{}: {}'.format(name, value)) - +def show_value(name: str, value: Any) -> None: + logger.info("%s: %s", name, value) -def show_sys_implementation(): - # type: () -> None - logger.info('sys.implementation:') - if hasattr(sys, 'implementation'): - implementation = sys.implementation # type: ignore - implementation_name = implementation.name - else: - implementation_name = '' +def show_sys_implementation() -> None: + logger.info("sys.implementation:") + implementation_name = sys.implementation.name with indent_log(): - show_value('name', implementation_name) + show_value("name", implementation_name) -def create_vendor_txt_map(): - # type: () -> Dict[str, str] +def create_vendor_txt_map() -> Dict[str, str]: vendor_txt_path = os.path.join( - os.path.dirname(pip_location), - '_vendor', - 'vendor.txt' + os.path.dirname(pip_location), "_vendor", "vendor.txt" ) with open(vendor_txt_path) as f: # Purge non version specifying lines. # Also, remove any space prefix or suffixes (including comments). - lines = [line.strip().split(' ', 1)[0] - for line in f.readlines() if '==' in line] + lines = [ + line.strip().split(" ", 1)[0] for line in f.readlines() if "==" in line + ] # Transform into "module" -> version dict. - return dict(line.split('==', 1) for line in lines) # type: ignore - + return dict(line.split("==", 1) for line in lines) # type: ignore -def get_module_from_module_name(module_name): - # type: (str) -> ModuleType +def get_module_from_module_name(module_name: str) -> ModuleType: # Module name can be uppercase in vendor.txt for some reason... module_name = module_name.lower() # PATCH: setuptools is actually only pkg_resources. - if module_name == 'setuptools': - module_name = 'pkg_resources' - - __import__( - 'pip._vendor.{}'.format(module_name), - globals(), - locals(), - level=0 - ) - return getattr(pip._vendor, module_name) + if module_name == "setuptools": + module_name = "pkg_resources" + __import__(f"pip._vendor.{module_name}", globals(), locals(), level=0) + return getattr(pip._vendor, module_name) -def get_vendor_version_from_module(module_name): - # type: (str) -> str +def get_vendor_version_from_module(module_name: str) -> Optional[str]: module = get_module_from_module_name(module_name) - version = getattr(module, '__version__', None) + version = getattr(module, "__version__", None) if not version: - # Try to find version in debundled module info - pkg_set = pkg_resources.WorkingSet( - [os.path.dirname(getattr(module, '__file__'))] - ) - package = pkg_set.find(pkg_resources.Requirement.parse(module_name)) - version = getattr(package, 'version', None) + # Try to find version in debundled module info. + env = get_environment([os.path.dirname(module.__file__)]) + dist = env.get_distribution(module_name) + if dist: + version = str(dist.version) return version -def show_actual_vendor_versions(vendor_txt_versions): - # type: (Dict[str, str]) -> None - # Logs the actual version and print extra info - # if there is a conflict or if the actual version could not be imported. - +def show_actual_vendor_versions(vendor_txt_versions: Dict[str, str]) -> None: + """Log the actual version and print extra info if there is + a conflict or if the actual version could not be imported. + """ for module_name, expected_version in vendor_txt_versions.items(): - extra_message = '' + extra_message = "" actual_version = get_vendor_version_from_module(module_name) if not actual_version: - extra_message = ' (Unable to locate actual module version, using'\ - ' vendor.txt specified version)' + extra_message = ( + " (Unable to locate actual module version, using" + " vendor.txt specified version)" + ) actual_version = expected_version - elif actual_version != expected_version: - extra_message = ' (CONFLICT: vendor.txt suggests version should'\ - ' be {})'.format(expected_version) - - logger.info( - '{name}=={actual}{extra}'.format( - name=module_name, - actual=actual_version, - extra=extra_message + elif parse_version(actual_version) != parse_version(expected_version): + extra_message = ( + " (CONFLICT: vendor.txt suggests version should" + " be {})".format(expected_version) ) - ) + logger.info("%s==%s%s", module_name, actual_version, extra_message) -def show_vendor_versions(): - # type: () -> None - logger.info('vendored library versions:') +def show_vendor_versions() -> None: + logger.info("vendored library versions:") vendor_txt_versions = create_vendor_txt_map() with indent_log(): show_actual_vendor_versions(vendor_txt_versions) -def show_tags(options): - # type: (Values) -> None +def show_tags(options: Values) -> None: tag_limit = 10 target_python = make_target_python(options) @@ -143,11 +112,11 @@ def show_tags(options): # Display the target options that were explicitly provided. formatted_target = target_python.format_given() - suffix = '' + suffix = "" if formatted_target: - suffix = ' (target: {})'.format(formatted_target) + suffix = f" (target: {formatted_target})" - msg = 'Compatible tags: {}{}'.format(len(tags), suffix) + msg = "Compatible tags: {}{}".format(len(tags), suffix) logger.info(msg) if options.verbose < 1 and len(tags) > tag_limit: @@ -162,29 +131,28 @@ def show_tags(options): if tags_limited: msg = ( - '...\n' - '[First {tag_limit} tags shown. Pass --verbose to show all.]' + "...\n[First {tag_limit} tags shown. Pass --verbose to show all.]" ).format(tag_limit=tag_limit) logger.info(msg) -def ca_bundle_info(config): +def ca_bundle_info(config: Configuration) -> str: levels = set() - for key, value in config.items(): - levels.add(key.split('.')[0]) + for key, _ in config.items(): + levels.add(key.split(".")[0]) if not levels: return "Not specified" - levels_that_override_global = ['install', 'wheel', 'download'] + levels_that_override_global = ["install", "wheel", "download"] global_overriding_level = [ level for level in levels if level in levels_that_override_global ] if not global_overriding_level: - return 'global' + return "global" - if 'global' in levels: - levels.remove('global') + if "global" in levels: + levels.remove("global") return ", ".join(levels) @@ -197,36 +165,33 @@ class DebugCommand(Command): %prog """ ignore_require_venv = True - def __init__(self, *args, **kw): - super(DebugCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - cmdoptions.add_target_python_options(cmd_opts) - self.parser.insert_option_group(0, cmd_opts) + def add_options(self) -> None: + cmdoptions.add_target_python_options(self.cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) self.parser.config.load() - def run(self, options, args): - # type: (Values, List[Any]) -> int + def run(self, options: Values, args: List[str]) -> int: logger.warning( "This command is only meant for debugging. " "Do not use this with automation for parsing and getting these " "details, since the output and options of this command may " "change without notice." ) - show_value('pip version', get_pip_version()) - show_value('sys.version', sys.version) - show_value('sys.executable', sys.executable) - show_value('sys.getdefaultencoding', sys.getdefaultencoding()) - show_value('sys.getfilesystemencoding', sys.getfilesystemencoding()) + show_value("pip version", get_pip_version()) + show_value("sys.version", sys.version) + show_value("sys.executable", sys.executable) + show_value("sys.getdefaultencoding", sys.getdefaultencoding()) + show_value("sys.getfilesystemencoding", sys.getfilesystemencoding()) show_value( - 'locale.getpreferredencoding', locale.getpreferredencoding(), + "locale.getpreferredencoding", + locale.getpreferredencoding(), ) - show_value('sys.platform', sys.platform) + show_value("sys.platform", sys.platform) show_sys_implementation() show_value("'cert' config value", ca_bundle_info(self.parser.config)) - show_value("REQUESTS_CA_BUNDLE", os.environ.get('REQUESTS_CA_BUNDLE')) - show_value("CURL_CA_BUNDLE", os.environ.get('CURL_CA_BUNDLE')) + show_value("REQUESTS_CA_BUNDLE", os.environ.get("REQUESTS_CA_BUNDLE")) + show_value("CURL_CA_BUNDLE", os.environ.get("CURL_CA_BUNDLE")) show_value("pip._vendor.certifi.where()", where()) show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED) diff --git a/venv/Lib/site-packages/pip/_internal/commands/download.py b/venv/Lib/site-packages/pip/_internal/commands/download.py index c829550633e1b6abdc63ad0cd0577fd1b54a2095..7de207f1365c5a049349fa2fca1f92ed34d7b66d 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/download.py +++ b/venv/Lib/site-packages/pip/_internal/commands/download.py @@ -1,14 +1,12 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import os +from optparse import Values +from typing import List from pip._internal.cli import cmdoptions from pip._internal.cli.cmdoptions import make_target_python from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.misc import ensure_dir, normalize_path, write_output from pip._internal.utils.temp_dir import TempDirectory @@ -36,36 +34,35 @@ class DownloadCommand(RequirementCommand): %prog [options] ... %prog [options] ...""" - def __init__(self, *args, **kw): - super(DownloadCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - - cmd_opts.add_option(cmdoptions.constraints()) - cmd_opts.add_option(cmdoptions.requirements()) - cmd_opts.add_option(cmdoptions.build_dir()) - cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option(cmdoptions.global_options()) - cmd_opts.add_option(cmdoptions.no_binary()) - cmd_opts.add_option(cmdoptions.only_binary()) - cmd_opts.add_option(cmdoptions.prefer_binary()) - cmd_opts.add_option(cmdoptions.src()) - cmd_opts.add_option(cmdoptions.pre()) - cmd_opts.add_option(cmdoptions.require_hashes()) - cmd_opts.add_option(cmdoptions.progress_bar()) - cmd_opts.add_option(cmdoptions.no_build_isolation()) - cmd_opts.add_option(cmdoptions.use_pep517()) - cmd_opts.add_option(cmdoptions.no_use_pep517()) - - cmd_opts.add_option( - '-d', '--dest', '--destination-dir', '--destination-directory', - dest='download_dir', - metavar='dir', + def add_options(self) -> None: + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.pre()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + + self.cmd_opts.add_option( + "-d", + "--dest", + "--destination-dir", + "--destination-directory", + dest="download_dir", + metavar="dir", default=os.curdir, - help=("Download packages into ."), + help="Download packages into .", ) - cmdoptions.add_target_python_options(cmd_opts) + cmdoptions.add_target_python_options(self.cmd_opts) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -73,10 +70,11 @@ class DownloadCommand(RequirementCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: + options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. @@ -85,7 +83,6 @@ class DownloadCommand(RequirementCommand): cmdoptions.check_dist_restriction(options) options.download_dir = normalize_path(options.download_dir) - ensure_dir(options.download_dir) session = self.get_default_session(options) @@ -95,14 +92,13 @@ class DownloadCommand(RequirementCommand): options=options, session=session, target_python=target_python, + ignore_requires_python=options.ignore_requires_python, ) - build_delete = (not (options.no_clean or options.build_dir)) req_tracker = self.enter_context(get_requirement_tracker()) directory = TempDirectory( - options.build_dir, - delete=build_delete, + delete=not options.no_clean, kind="download", globally_managed=True, ) @@ -123,20 +119,21 @@ class DownloadCommand(RequirementCommand): preparer=preparer, finder=finder, options=options, + ignore_requires_python=options.ignore_requires_python, py_version_info=options.python_version, ) self.trace_basic_info(finder) - requirement_set = resolver.resolve( - reqs, check_supported_wheels=True - ) + requirement_set = resolver.resolve(reqs, check_supported_wheels=True) - downloaded = ' '.join([ - req.name for req in requirement_set.requirements.values() - if req.successfully_downloaded - ]) + downloaded: List[str] = [] + for req in requirement_set.requirements.values(): + if req.satisfied_by is None: + assert req.name is not None + preparer.save_linked_requirement(req) + downloaded.append(req.name) if downloaded: - write_output('Successfully downloaded %s', downloaded) + write_output("Successfully downloaded %s", " ".join(downloaded)) - return requirement_set + return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/commands/freeze.py b/venv/Lib/site-packages/pip/_internal/commands/freeze.py index 13171772e5c1f49a4f0e7643307c6d0973b828a0..5fa6d39b2c7c74635f9570c1e1665d03a45024b2 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/freeze.py +++ b/venv/Lib/site-packages/pip/_internal/commands/freeze.py @@ -1,18 +1,14 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import sys +from optparse import Values +from typing import List -from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command -from pip._internal.models.format_control import FormatControl +from pip._internal.cli.status_codes import SUCCESS from pip._internal.operations.freeze import freeze from pip._internal.utils.compat import stdlib_pkgs -DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} +DEV_PKGS = {"pip", "setuptools", "distribute", "wheel"} class FreezeCommand(Command): @@ -26,74 +22,76 @@ class FreezeCommand(Command): %prog [options]""" log_streams = ("ext://sys.stderr", "ext://sys.stderr") - def __init__(self, *args, **kw): - super(FreezeCommand, self).__init__(*args, **kw) - + def add_options(self) -> None: self.cmd_opts.add_option( - '-r', '--requirement', - dest='requirements', - action='append', + "-r", + "--requirement", + dest="requirements", + action="append", default=[], - metavar='file', - help="Use the order in the given requirements file and its " - "comments when generating output. This option can be " - "used multiple times.") - self.cmd_opts.add_option( - '-f', '--find-links', - dest='find_links', - action='append', - default=[], - metavar='URL', - help='URL for finding packages, which will be added to the ' - 'output.') + metavar="file", + help=( + "Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times." + ), + ) self.cmd_opts.add_option( - '-l', '--local', - dest='local', - action='store_true', + "-l", + "--local", + dest="local", + action="store_true", default=False, - help='If in a virtualenv that has global access, do not output ' - 'globally-installed packages.') + help=( + "If in a virtualenv that has global access, do not output " + "globally-installed packages." + ), + ) self.cmd_opts.add_option( - '--user', - dest='user', - action='store_true', + "--user", + dest="user", + action="store_true", default=False, - help='Only output packages installed in user-site.') + help="Only output packages installed in user-site.", + ) self.cmd_opts.add_option(cmdoptions.list_path()) self.cmd_opts.add_option( - '--all', - dest='freeze_all', - action='store_true', - help='Do not skip these packages in the output:' - ' {}'.format(', '.join(DEV_PKGS))) + "--all", + dest="freeze_all", + action="store_true", + help=( + "Do not skip these packages in the output:" + " {}".format(", ".join(DEV_PKGS)) + ), + ) self.cmd_opts.add_option( - '--exclude-editable', - dest='exclude_editable', - action='store_true', - help='Exclude editable package from output.') + "--exclude-editable", + dest="exclude_editable", + action="store_true", + help="Exclude editable package from output.", + ) + self.cmd_opts.add_option(cmdoptions.list_exclude()) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): - format_control = FormatControl(set(), set()) - wheel_cache = WheelCache(options.cache_dir, format_control) + def run(self, options: Values, args: List[str]) -> int: skip = set(stdlib_pkgs) if not options.freeze_all: skip.update(DEV_PKGS) + if options.excludes: + skip.update(options.excludes) + cmdoptions.check_list_path_option(options) - freeze_kwargs = dict( + for line in freeze( requirement=options.requirements, - find_links=options.find_links, local_only=options.local, user_only=options.user, paths=options.path, isolated=options.isolated_mode, - wheel_cache=wheel_cache, skip=skip, exclude_editable=options.exclude_editable, - ) - - for line in freeze(**freeze_kwargs): - sys.stdout.write(line + '\n') + ): + sys.stdout.write(line + "\n") + return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/commands/hash.py b/venv/Lib/site-packages/pip/_internal/commands/hash.py index f26686156ee4c54008a308b2a1dc0f186f4113ba..042dac813e74b8187c3754cb9a937c7f7183e331 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/hash.py +++ b/venv/Lib/site-packages/pip/_internal/commands/hash.py @@ -1,14 +1,11 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import hashlib import logging import sys +from optparse import Values +from typing import List from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES from pip._internal.utils.misc import read_chunks, write_output @@ -23,35 +20,39 @@ class HashCommand(Command): installs. """ - usage = '%prog [options] ...' + usage = "%prog [options] ..." ignore_require_venv = True - def __init__(self, *args, **kw): - super(HashCommand, self).__init__(*args, **kw) + def add_options(self) -> None: self.cmd_opts.add_option( - '-a', '--algorithm', - dest='algorithm', + "-a", + "--algorithm", + dest="algorithm", choices=STRONG_HASHES, - action='store', + action="store", default=FAVORITE_HASH, - help='The hash algorithm to use: one of {}'.format( - ', '.join(STRONG_HASHES))) + help="The hash algorithm to use: one of {}".format( + ", ".join(STRONG_HASHES) + ), + ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: if not args: self.parser.print_usage(sys.stderr) return ERROR algorithm = options.algorithm for path in args: - write_output('%s:\n--hash=%s:%s', - path, algorithm, _hash_of_file(path, algorithm)) + write_output( + "%s:\n--hash=%s:%s", path, algorithm, _hash_of_file(path, algorithm) + ) + return SUCCESS -def _hash_of_file(path, algorithm): +def _hash_of_file(path: str, algorithm: str) -> str: """Return the hash digest of a file.""" - with open(path, 'rb') as archive: + with open(path, "rb") as archive: hash = hashlib.new(algorithm) for chunk in read_chunks(archive): hash.update(chunk) diff --git a/venv/Lib/site-packages/pip/_internal/commands/help.py b/venv/Lib/site-packages/pip/_internal/commands/help.py index c17d7a457c463f9f0b0acff02f56433a0404c517..62066318b74dcc5c32bcd24b9493fb34d1ce52d7 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/help.py +++ b/venv/Lib/site-packages/pip/_internal/commands/help.py @@ -1,7 +1,5 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import +from optparse import Values +from typing import List from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import SUCCESS @@ -15,9 +13,11 @@ class HelpCommand(Command): %prog """ ignore_require_venv = True - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: from pip._internal.commands import ( - commands_dict, create_command, get_similar_commands, + commands_dict, + create_command, + get_similar_commands, ) try: @@ -29,11 +29,11 @@ class HelpCommand(Command): if cmd_name not in commands_dict: guess = get_similar_commands(cmd_name) - msg = ['unknown command "{}"'.format(cmd_name)] + msg = [f'unknown command "{cmd_name}"'] if guess: - msg.append('maybe you meant "{}"'.format(guess)) + msg.append(f'maybe you meant "{guess}"') - raise CommandError(' - '.join(msg)) + raise CommandError(" - ".join(msg)) command = create_command(cmd_name) command.parser.print_help() diff --git a/venv/Lib/site-packages/pip/_internal/commands/install.py b/venv/Lib/site-packages/pip/_internal/commands/install.py index 70bda2e2a925dd988df9f7222f925975e4ce574c..eedb1ff5d648586535d40c77113fc74157027d51 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/install.py +++ b/venv/Lib/site-packages/pip/_internal/commands/install.py @@ -1,66 +1,57 @@ -# The following comment should be removed at some point in the future. -# It's included for now because without it InstallCommand.run() has a -# couple errors where we have to know req.name is str rather than -# Optional[str] for the InstallRequirement req. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import errno -import logging import operator import os import shutil import site -from optparse import SUPPRESS_HELP +from optparse import SUPPRESS_HELP, Values +from typing import Iterable, List, Optional -from pip._vendor import pkg_resources from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.cmdoptions import make_target_python -from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.req_command import ( + RequirementCommand, + warn_if_run_as_root, + with_cleanup, +) from pip._internal.cli.status_codes import ERROR, SUCCESS from pip._internal.exceptions import CommandError, InstallationError -from pip._internal.locations import distutils_scheme -from pip._internal.operations.check import check_install_conflicts +from pip._internal.locations import get_scheme +from pip._internal.metadata import get_environment +from pip._internal.models.format_control import FormatControl +from pip._internal.operations.check import ConflictDetails, check_install_conflicts from pip._internal.req import install_given_reqs +from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import get_requirement_tracker -from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.compat import WINDOWS from pip._internal.utils.distutils_args import parse_distutils_args from pip._internal.utils.filesystem import test_writable_dir +from pip._internal.utils.logging import getLogger from pip._internal.utils.misc import ( ensure_dir, - get_installed_version, + get_pip_version, protect_pip_from_modification_on_windows, write_output, ) from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.utils.virtualenv import virtualenv_no_global -from pip._internal.wheel_builder import build, should_build_for_install_command - -if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Any, Iterable, List, Optional - - from pip._internal.models.format_control import FormatControl - from pip._internal.req.req_install import InstallRequirement - from pip._internal.wheel_builder import BinaryAllowedPredicate - +from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, +) +from pip._internal.wheel_builder import ( + BinaryAllowedPredicate, + build, + should_build_for_install_command, +) -logger = logging.getLogger(__name__) +logger = getLogger(__name__) -def get_check_binary_allowed(format_control): - # type: (FormatControl) -> BinaryAllowedPredicate - def check_binary_allowed(req): - # type: (InstallRequirement) -> bool - if req.use_pep517: - return True - canonical_name = canonicalize_name(req.name) +def get_check_binary_allowed(format_control: FormatControl) -> BinaryAllowedPredicate: + def check_binary_allowed(req: InstallRequirement) -> bool: + canonical_name = canonicalize_name(req.name or "") allowed_formats = format_control.get_allowed_formats(canonical_name) return "binary" in allowed_formats @@ -87,110 +78,122 @@ class InstallCommand(RequirementCommand): %prog [options] [-e] ... %prog [options] ...""" - def __init__(self, *args, **kw): - super(InstallCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - - cmd_opts.add_option(cmdoptions.requirements()) - cmd_opts.add_option(cmdoptions.constraints()) - cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option(cmdoptions.pre()) - - cmd_opts.add_option(cmdoptions.editable()) - cmd_opts.add_option( - '-t', '--target', - dest='target_dir', - metavar='dir', + def add_options(self) -> None: + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.pre()) + + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option( + "-t", + "--target", + dest="target_dir", + metavar="dir", default=None, - help='Install packages into . ' - 'By default this will not replace existing files/folders in ' - '. Use --upgrade to replace existing packages in ' - 'with new versions.' + help=( + "Install packages into . " + "By default this will not replace existing files/folders in " + ". Use --upgrade to replace existing packages in " + "with new versions." + ), ) - cmdoptions.add_target_python_options(cmd_opts) - - cmd_opts.add_option( - '--user', - dest='use_user_site', - action='store_true', - help="Install to the Python user install directory for your " - "platform. Typically ~/.local/, or %APPDATA%\\Python on " - "Windows. (See the Python documentation for site.USER_BASE " - "for full details.)") - cmd_opts.add_option( - '--no-user', - dest='use_user_site', - action='store_false', - help=SUPPRESS_HELP) - cmd_opts.add_option( - '--root', - dest='root_path', - metavar='dir', + cmdoptions.add_target_python_options(self.cmd_opts) + + self.cmd_opts.add_option( + "--user", + dest="use_user_site", + action="store_true", + help=( + "Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.)" + ), + ) + self.cmd_opts.add_option( + "--no-user", + dest="use_user_site", + action="store_false", + help=SUPPRESS_HELP, + ) + self.cmd_opts.add_option( + "--root", + dest="root_path", + metavar="dir", default=None, - help="Install everything relative to this alternate root " - "directory.") - cmd_opts.add_option( - '--prefix', - dest='prefix_path', - metavar='dir', + help="Install everything relative to this alternate root directory.", + ) + self.cmd_opts.add_option( + "--prefix", + dest="prefix_path", + metavar="dir", default=None, - help="Installation prefix where lib, bin and other top-level " - "folders are placed") + help=( + "Installation prefix where lib, bin and other top-level " + "folders are placed" + ), + ) - cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.src()) - cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option( + "-U", + "--upgrade", + dest="upgrade", + action="store_true", + help=( + "Upgrade all specified packages to the newest available " + "version. The handling of dependencies depends on the " + "upgrade-strategy used." + ), + ) - cmd_opts.add_option( - '-U', '--upgrade', - dest='upgrade', - action='store_true', - help='Upgrade all specified packages to the newest available ' - 'version. The handling of dependencies depends on the ' - 'upgrade-strategy used.' + self.cmd_opts.add_option( + "--upgrade-strategy", + dest="upgrade_strategy", + default="only-if-needed", + choices=["only-if-needed", "eager"], + help=( + "Determines how dependency upgrading should be handled " + "[default: %default]. " + '"eager" - dependencies are upgraded regardless of ' + "whether the currently installed version satisfies the " + "requirements of the upgraded package(s). " + '"only-if-needed" - are upgraded only when they do not ' + "satisfy the requirements of the upgraded package(s)." + ), ) - cmd_opts.add_option( - '--upgrade-strategy', - dest='upgrade_strategy', - default='only-if-needed', - choices=['only-if-needed', 'eager'], - help='Determines how dependency upgrading should be handled ' - '[default: %default]. ' - '"eager" - dependencies are upgraded regardless of ' - 'whether the currently installed version satisfies the ' - 'requirements of the upgraded package(s). ' - '"only-if-needed" - are upgraded only when they do not ' - 'satisfy the requirements of the upgraded package(s).' + self.cmd_opts.add_option( + "--force-reinstall", + dest="force_reinstall", + action="store_true", + help="Reinstall all packages even if they are already up-to-date.", ) - cmd_opts.add_option( - '--force-reinstall', - dest='force_reinstall', - action='store_true', - help='Reinstall all packages even if they are already ' - 'up-to-date.') - - cmd_opts.add_option( - '-I', '--ignore-installed', - dest='ignore_installed', - action='store_true', - help='Ignore the installed packages, overwriting them. ' - 'This can break your system if the existing package ' - 'is of a different version or was installed ' - 'with a different package manager!' + self.cmd_opts.add_option( + "-I", + "--ignore-installed", + dest="ignore_installed", + action="store_true", + help=( + "Ignore the installed packages, overwriting them. " + "This can break your system if the existing package " + "is of a different version or was installed " + "with a different package manager!" + ), ) - cmd_opts.add_option(cmdoptions.ignore_requires_python()) - cmd_opts.add_option(cmdoptions.no_build_isolation()) - cmd_opts.add_option(cmdoptions.use_pep517()) - cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) - cmd_opts.add_option(cmdoptions.install_options()) - cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.install_options()) + self.cmd_opts.add_option(cmdoptions.global_options()) - cmd_opts.add_option( + self.cmd_opts.add_option( "--compile", action="store_true", dest="compile", @@ -198,21 +201,21 @@ class InstallCommand(RequirementCommand): help="Compile Python source files to bytecode", ) - cmd_opts.add_option( + self.cmd_opts.add_option( "--no-compile", action="store_false", dest="compile", help="Do not compile Python source files to bytecode", ) - cmd_opts.add_option( + self.cmd_opts.add_option( "--no-warn-script-location", action="store_false", dest="warn_script_location", default=True, help="Do not warn when installing scripts outside PATH", ) - cmd_opts.add_option( + self.cmd_opts.add_option( "--no-warn-conflicts", action="store_false", dest="warn_about_conflicts", @@ -220,11 +223,11 @@ class InstallCommand(RequirementCommand): help="Do not warn about broken dependencies", ) - cmd_opts.add_option(cmdoptions.no_binary()) - cmd_opts.add_option(cmdoptions.only_binary()) - cmd_opts.add_option(cmdoptions.prefer_binary()) - cmd_opts.add_option(cmdoptions.require_hashes()) - cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -232,11 +235,10 @@ class InstallCommand(RequirementCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup - def run(self, options, args): - # type: (Values, List[Any]) -> int + def run(self, options: Values, args: List[str]) -> int: if options.use_user_site and options.target_dir is not None: raise CommandError("Can not combine '--user' and '--target'") @@ -249,6 +251,7 @@ class InstallCommand(RequirementCommand): install_options = options.install_options or [] + logger.verbose("Using %s", get_pip_version()) options.use_user_site = decide_user_install( options.use_user_site, prefix_path=options.prefix_path, @@ -257,21 +260,25 @@ class InstallCommand(RequirementCommand): isolated_mode=options.isolated_mode, ) - target_temp_dir = None # type: Optional[TempDirectory] - target_temp_dir_path = None # type: Optional[str] + target_temp_dir: Optional[TempDirectory] = None + target_temp_dir_path: Optional[str] = None if options.target_dir: options.ignore_installed = True options.target_dir = os.path.abspath(options.target_dir) - if (os.path.exists(options.target_dir) and not - os.path.isdir(options.target_dir)): + if ( + # fmt: off + os.path.exists(options.target_dir) and + not os.path.isdir(options.target_dir) + # fmt: on + ): raise CommandError( - "Target path exists but is not a directory, will not " - "continue." + "Target path exists but is not a directory, will not continue." ) # Create a target directory for using with the target option target_temp_dir = TempDirectory(kind="target") target_temp_dir_path = target_temp_dir.path + self.enter_context(target_temp_dir) global_options = options.global_options or [] @@ -284,27 +291,26 @@ class InstallCommand(RequirementCommand): target_python=target_python, ignore_requires_python=options.ignore_requires_python, ) - build_delete = (not (options.no_clean or options.build_dir)) wheel_cache = WheelCache(options.cache_dir, options.format_control) req_tracker = self.enter_context(get_requirement_tracker()) directory = TempDirectory( - options.build_dir, - delete=build_delete, + delete=not options.no_clean, kind="install", globally_managed=True, ) try: - reqs = self.get_requirements( - args, options, finder, session, - check_supported_wheels=not options.target_dir, - ) + reqs = self.get_requirements(args, options, finder, session) - warn_deprecated_install_options( - reqs, options.install_options - ) + # Only when installing is it permitted to use PEP 660. + # In other circumstances (pip wheel, pip download) we generate + # regular (i.e. non editable) metadata and wheels. + for req in reqs: + req.permit_editable_wheels = True + + reject_location_related_install_options(reqs, options.install_options) preparer = self.make_requirement_preparer( temp_build_dir=directory, @@ -336,63 +342,63 @@ class InstallCommand(RequirementCommand): try: pip_req = requirement_set.get_requirement("pip") except KeyError: - modifying_pip = None + modifying_pip = False else: # If we're not replacing an already installed pip, # we're not modifying it. modifying_pip = pip_req.satisfied_by is None - protect_pip_from_modification_on_windows( - modifying_pip=modifying_pip - ) + protect_pip_from_modification_on_windows(modifying_pip=modifying_pip) - check_binary_allowed = get_check_binary_allowed( - finder.format_control - ) + check_binary_allowed = get_check_binary_allowed(finder.format_control) reqs_to_build = [ - r for r in requirement_set.requirements.values() - if should_build_for_install_command( - r, check_binary_allowed - ) + r + for r in requirement_set.requirements.values() + if should_build_for_install_command(r, check_binary_allowed) ] _, build_failures = build( reqs_to_build, wheel_cache=wheel_cache, + verify=True, build_options=[], global_options=[], ) - # If we're using PEP 517, we cannot do a direct install + # If we're using PEP 517, we cannot do a legacy setup.py install # so we fail here. - # We don't care about failures building legacy - # requirements, as we'll fall through to a direct - # install for those. - pep517_build_failures = [ - r for r in build_failures if r.use_pep517 + pep517_build_failure_names: List[str] = [ + r.name for r in build_failures if r.use_pep517 # type: ignore ] - if pep517_build_failures: + if pep517_build_failure_names: raise InstallationError( - "Could not build wheels for {} which use" - " PEP 517 and cannot be installed directly".format( - ", ".join(r.name for r in pep517_build_failures))) + "Could not build wheels for {}, which is required to " + "install pyproject.toml-based projects".format( + ", ".join(pep517_build_failure_names) + ) + ) - to_install = resolver.get_installation_order( - requirement_set - ) + # For now, we just warn about failures building legacy + # requirements, as we'll fall through to a setup.py install for + # those. + for r in build_failures: + if not r.use_pep517: + r.legacy_install_reason = 8368 - # Consistency Checking of the package set we're installing. + to_install = resolver.get_installation_order(requirement_set) + + # Check for conflicts in the package set we're installing. + conflicts: Optional[ConflictDetails] = None should_warn_about_conflicts = ( - not options.ignore_dependencies and - options.warn_about_conflicts + not options.ignore_dependencies and options.warn_about_conflicts ) if should_warn_about_conflicts: - self._warn_about_conflicts(to_install) + conflicts = self._determine_conflicts(to_install) # Don't warn about script install locations if - # --target has been specified + # --target or --prefix has been specified warn_script_location = options.warn_script_location - if options.target_dir: + if options.target_dir or options.prefix_path: warn_script_location = False installed = install_given_reqs( @@ -402,9 +408,9 @@ class InstallCommand(RequirementCommand): root=options.root_path, home=target_temp_dir_path, prefix=options.prefix_path, - pycompile=options.compile, warn_script_location=warn_script_location, use_user_site=options.use_user_site, + pycompile=options.compile, ) lib_locations = get_lib_location_guesses( @@ -414,145 +420,206 @@ class InstallCommand(RequirementCommand): prefix=options.prefix_path, isolated=options.isolated_mode, ) - working_set = pkg_resources.WorkingSet(lib_locations) + env = get_environment(lib_locations) - installed.sort(key=operator.attrgetter('name')) + installed.sort(key=operator.attrgetter("name")) items = [] for result in installed: item = result.name try: - installed_version = get_installed_version( - result.name, working_set=working_set - ) - if installed_version: - item += '-' + installed_version + installed_dist = env.get_distribution(item) + if installed_dist is not None: + item = f"{item}-{installed_dist.version}" except Exception: pass items.append(item) - installed_desc = ' '.join(items) + + if conflicts is not None: + self._warn_about_conflicts( + conflicts, + resolver_variant=self.determine_resolver_variant(options), + ) + + installed_desc = " ".join(items) if installed_desc: write_output( - 'Successfully installed %s', installed_desc, + "Successfully installed %s", + installed_desc, ) - except EnvironmentError as error: - show_traceback = (self.verbosity >= 1) + except OSError as error: + show_traceback = self.verbosity >= 1 - message = create_env_error_message( - error, show_traceback, options.use_user_site, + message = create_os_error_message( + error, + show_traceback, + options.use_user_site, ) - logger.error(message, exc_info=show_traceback) + logger.error(message, exc_info=show_traceback) # noqa return ERROR if options.target_dir: + assert target_temp_dir self._handle_target_dir( options.target_dir, target_temp_dir, options.upgrade ) + warn_if_run_as_root() return SUCCESS - def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + def _handle_target_dir( + self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool + ) -> None: ensure_dir(target_dir) # Checking both purelib and platlib directories for installed # packages to be moved to target directory lib_dir_list = [] - with target_temp_dir: - # Checking both purelib and platlib directories for installed - # packages to be moved to target directory - scheme = distutils_scheme('', home=target_temp_dir.path) - purelib_dir = scheme['purelib'] - platlib_dir = scheme['platlib'] - data_dir = scheme['data'] - - if os.path.exists(purelib_dir): - lib_dir_list.append(purelib_dir) - if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: - lib_dir_list.append(platlib_dir) - if os.path.exists(data_dir): - lib_dir_list.append(data_dir) - - for lib_dir in lib_dir_list: - for item in os.listdir(lib_dir): - if lib_dir == data_dir: - ddir = os.path.join(data_dir, item) - if any(s.startswith(ddir) for s in lib_dir_list[:-1]): - continue - target_item_dir = os.path.join(target_dir, item) - if os.path.exists(target_item_dir): - if not upgrade: - logger.warning( - 'Target directory %s already exists. Specify ' - '--upgrade to force replacement.', - target_item_dir - ) - continue - if os.path.islink(target_item_dir): - logger.warning( - 'Target directory %s already exists and is ' - 'a link. pip will not automatically replace ' - 'links, please remove if replacement is ' - 'desired.', - target_item_dir - ) - continue - if os.path.isdir(target_item_dir): - shutil.rmtree(target_item_dir) - else: - os.remove(target_item_dir) - - shutil.move( - os.path.join(lib_dir, item), - target_item_dir - ) - - def _warn_about_conflicts(self, to_install): + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = get_scheme("", home=target_temp_dir.path) + purelib_dir = scheme.purelib + platlib_dir = scheme.platlib + data_dir = scheme.data + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + "Target directory %s already exists. Specify " + "--upgrade to force replacement.", + target_item_dir, + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + "Target directory %s already exists and is " + "a link. pip will not automatically replace " + "links, please remove if replacement is " + "desired.", + target_item_dir, + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move(os.path.join(lib_dir, item), target_item_dir) + + def _determine_conflicts( + self, to_install: List[InstallRequirement] + ) -> Optional[ConflictDetails]: try: - package_set, _dep_info = check_install_conflicts(to_install) + return check_install_conflicts(to_install) except Exception: - logger.error("Error checking for conflicts.", exc_info=True) + logger.exception( + "Error while checking for conflicts. Please file an issue on " + "pip's issue tracker: https://github.com/pypa/pip/issues/new" + ) + return None + + def _warn_about_conflicts( + self, conflict_details: ConflictDetails, resolver_variant: str + ) -> None: + package_set, (missing, conflicting) = conflict_details + if not missing and not conflicting: return - missing, conflicting = _dep_info - # NOTE: There is some duplication here from pip check + parts: List[str] = [] + if resolver_variant == "legacy": + parts.append( + "pip's legacy dependency resolver does not consider dependency " + "conflicts when selecting packages. This behaviour is the " + "source of the following dependency conflicts." + ) + else: + assert resolver_variant == "2020-resolver" + parts.append( + "pip's dependency resolver does not currently take into account " + "all the packages that are installed. This behaviour is the " + "source of the following dependency conflicts." + ) + + # NOTE: There is some duplication here, with commands/check.py for project_name in missing: version = package_set[project_name][0] for dependency in missing[project_name]: - logger.critical( - "%s %s requires %s, which is not installed.", - project_name, version, dependency[1], + message = ( + "{name} {version} requires {requirement}, " + "which is not installed." + ).format( + name=project_name, + version=version, + requirement=dependency[1], ) + parts.append(message) for project_name in conflicting: version = package_set[project_name][0] for dep_name, dep_version, req in conflicting[project_name]: - logger.critical( - "%s %s has requirement %s, but you'll have %s %s which is " - "incompatible.", - project_name, version, req, dep_name, dep_version, + message = ( + "{name} {version} requires {requirement}, but {you} have " + "{dep_name} {dep_version} which is incompatible." + ).format( + name=project_name, + version=version, + requirement=req, + dep_name=dep_name, + dep_version=dep_version, + you=("you" if resolver_variant == "2020-resolver" else "you'll"), ) + parts.append(message) + + logger.critical("\n".join(parts)) + + +def get_lib_location_guesses( + user: bool = False, + home: Optional[str] = None, + root: Optional[str] = None, + isolated: bool = False, + prefix: Optional[str] = None, +) -> List[str]: + scheme = get_scheme( + "", + user=user, + home=home, + root=root, + isolated=isolated, + prefix=prefix, + ) + return [scheme.purelib, scheme.platlib] -def get_lib_location_guesses(*args, **kwargs): - scheme = distutils_scheme('', *args, **kwargs) - return [scheme['purelib'], scheme['platlib']] - - -def site_packages_writable(**kwargs): +def site_packages_writable(root: Optional[str], isolated: bool) -> bool: return all( - test_writable_dir(d) for d in set(get_lib_location_guesses(**kwargs)) + test_writable_dir(d) + for d in set(get_lib_location_guesses(root=root, isolated=isolated)) ) def decide_user_install( - use_user_site, # type: Optional[bool] - prefix_path=None, # type: Optional[str] - target_dir=None, # type: Optional[str] - root_path=None, # type: Optional[str] - isolated_mode=False, # type: bool -): - # type: (...) -> bool + use_user_site: Optional[bool], + prefix_path: Optional[str] = None, + target_dir: Optional[str] = None, + root_path: Optional[str] = None, + isolated_mode: bool = False, +) -> bool: """Determine whether to do a user install based on the input options. If use_user_site is False, no additional checks are done. @@ -600,18 +667,21 @@ def decide_user_install( logger.debug("Non-user install because site-packages writeable") return False - logger.info("Defaulting to user installation because normal site-packages " - "is not writeable") + logger.info( + "Defaulting to user installation because normal site-packages " + "is not writeable" + ) return True -def warn_deprecated_install_options(requirements, options): - # type: (List[InstallRequirement], Optional[List[str]]) -> None +def reject_location_related_install_options( + requirements: List[InstallRequirement], options: Optional[List[str]] +) -> None: """If any location-changing --install-option arguments were passed for requirements or on the command-line, then show a deprecation warning. """ - def format_options(option_names): - # type: (Iterable[str]) -> List[str] + + def format_options(option_names: Iterable[str]) -> List[str]: return ["--{}".format(name.replace("_", "-")) for name in option_names] offenders = [] @@ -630,40 +700,30 @@ def warn_deprecated_install_options(requirements, options): location_options = parse_distutils_args(options) if location_options: offenders.append( - "{!r} from command line".format( - format_options(location_options.keys()) - ) + "{!r} from command line".format(format_options(location_options.keys())) ) if not offenders: return - deprecated( - reason=( - "Location-changing options found in --install-option: {}. " - "This configuration may cause unexpected behavior and is " - "unsupported.".format( - "; ".join(offenders) - ) - ), - replacement=( - "using pip-level options like --user, --prefix, --root, and " - "--target" - ), - gone_in="20.2", - issue=7309, + raise CommandError( + "Location-changing options found in --install-option: {}." + " This is unsupported, use pip-level options like --user," + " --prefix, --root, and --target instead.".format("; ".join(offenders)) ) -def create_env_error_message(error, show_traceback, using_user_site): - """Format an error message for an EnvironmentError +def create_os_error_message( + error: OSError, show_traceback: bool, using_user_site: bool +) -> str: + """Format an error message for an OSError It may occur anytime during the execution of the install command. """ parts = [] # Mention the error if we are not going to show a traceback - parts.append("Could not install packages due to an EnvironmentError") + parts.append("Could not install packages due to an OSError") if not show_traceback: parts.append(": ") parts.append(str(error)) @@ -679,13 +739,32 @@ def create_env_error_message(error, show_traceback, using_user_site): user_option_part = "Consider using the `--user` option" permissions_part = "Check the permissions" - if not using_user_site: - parts.extend([ - user_option_part, " or ", - permissions_part.lower(), - ]) + if not running_under_virtualenv() and not using_user_site: + parts.extend( + [ + user_option_part, + " or ", + permissions_part.lower(), + ] + ) else: parts.append(permissions_part) parts.append(".\n") + # Suggest the user to enable Long Paths if path length is + # more than 260 + if ( + WINDOWS + and error.errno == errno.ENOENT + and error.filename + and len(error.filename) > 260 + ): + parts.append( + "HINT: This error might have occurred since " + "this system does not have Windows Long Path " + "support enabled. You can find information on " + "how to enable this at " + "https://pip.pypa.io/warnings/enable-long-paths\n" + ) + return "".join(parts).strip() + "\n" diff --git a/venv/Lib/site-packages/pip/_internal/commands/list.py b/venv/Lib/site-packages/pip/_internal/commands/list.py index 2aa3075b1b71d85b37b145742f5715de874ee227..75d8dd465ba4f817e328a2a611d72ec333c87f74 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/list.py +++ b/venv/Lib/site-packages/pip/_internal/commands/list.py @@ -1,26 +1,38 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import json import logging +from optparse import Values +from typing import TYPE_CHECKING, Iterator, List, Optional, Sequence, Tuple, cast -from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli import cmdoptions from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError +from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution, get_environment from pip._internal.models.selection_prefs import SelectionPreferences -from pip._internal.self_outdated_check import make_link_collector -from pip._internal.utils.misc import ( - dist_is_editable, - get_installed_distributions, - tabulate, - write_output, -) -from pip._internal.utils.packaging import get_installer +from pip._internal.network.session import PipSession +from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.misc import tabulate, write_output +from pip._internal.utils.parallel import map_multithread + +if TYPE_CHECKING: + from pip._internal.metadata.base import DistributionVersion + + class _DistWithLatestInfo(BaseDistribution): + """Give the distribution object a couple of extra fields. + + These will be populated during ``get_outdated()``. This is dirty but + makes the rest of the code much cleaner. + """ + + latest_version: DistributionVersion + latest_filetype: str + + _ProcessedDists = Sequence[_DistWithLatestInfo] + logger = logging.getLogger(__name__) @@ -32,94 +44,102 @@ class ListCommand(IndexGroupCommand): Packages are listed in a case-insensitive sorted order. """ + ignore_require_venv = True usage = """ %prog [options]""" - def __init__(self, *args, **kw): - super(ListCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts - - cmd_opts.add_option( - '-o', '--outdated', - action='store_true', + def add_options(self) -> None: + self.cmd_opts.add_option( + "-o", + "--outdated", + action="store_true", default=False, - help='List outdated packages') - cmd_opts.add_option( - '-u', '--uptodate', - action='store_true', + help="List outdated packages", + ) + self.cmd_opts.add_option( + "-u", + "--uptodate", + action="store_true", default=False, - help='List uptodate packages') - cmd_opts.add_option( - '-e', '--editable', - action='store_true', + help="List uptodate packages", + ) + self.cmd_opts.add_option( + "-e", + "--editable", + action="store_true", default=False, - help='List editable projects.') - cmd_opts.add_option( - '-l', '--local', - action='store_true', + help="List editable projects.", + ) + self.cmd_opts.add_option( + "-l", + "--local", + action="store_true", default=False, - help=('If in a virtualenv that has global access, do not list ' - 'globally-installed packages.'), + help=( + "If in a virtualenv that has global access, do not list " + "globally-installed packages." + ), ) self.cmd_opts.add_option( - '--user', - dest='user', - action='store_true', + "--user", + dest="user", + action="store_true", default=False, - help='Only output packages installed in user-site.') - cmd_opts.add_option(cmdoptions.list_path()) - cmd_opts.add_option( - '--pre', - action='store_true', + help="Only output packages installed in user-site.", + ) + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + "--pre", + action="store_true", default=False, - help=("Include pre-release and development versions. By default, " - "pip only finds stable versions."), + help=( + "Include pre-release and development versions. By default, " + "pip only finds stable versions." + ), ) - cmd_opts.add_option( - '--format', - action='store', - dest='list_format', + self.cmd_opts.add_option( + "--format", + action="store", + dest="list_format", default="columns", - choices=('columns', 'freeze', 'json'), - help="Select the output format among: columns (default), freeze, " - "or json", + choices=("columns", "freeze", "json"), + help="Select the output format among: columns (default), freeze, or json", ) - cmd_opts.add_option( - '--not-required', - action='store_true', - dest='not_required', - help="List packages that are not dependencies of " - "installed packages.", + self.cmd_opts.add_option( + "--not-required", + action="store_true", + dest="not_required", + help="List packages that are not dependencies of installed packages.", ) - cmd_opts.add_option( - '--exclude-editable', - action='store_false', - dest='include_editable', - help='Exclude editable package from output.', + self.cmd_opts.add_option( + "--exclude-editable", + action="store_false", + dest="include_editable", + help="Exclude editable package from output.", ) - cmd_opts.add_option( - '--include-editable', - action='store_true', - dest='include_editable', - help='Include editable package from output.', + self.cmd_opts.add_option( + "--include-editable", + action="store_true", + dest="include_editable", + help="Include editable package from output.", default=True, ) - index_opts = cmdoptions.make_option_group( - cmdoptions.index_group, self.parser - ) + self.cmd_opts.add_option(cmdoptions.list_exclude()) + index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) - def _build_package_finder(self, options, session): + def _build_package_finder( + self, options: Values, session: PipSession + ) -> PackageFinder: """ Create a package finder appropriate to this list command. """ - link_collector = make_link_collector(session, options=options) + link_collector = LinkCollector.create(session, options=options) # Pass allow_yanked=False to ignore yanked versions. selection_prefs = SelectionPreferences( @@ -132,20 +152,26 @@ class ListCommand(IndexGroupCommand): selection_prefs=selection_prefs, ) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: if options.outdated and options.uptodate: - raise CommandError( - "Options --outdated and --uptodate cannot be combined.") + raise CommandError("Options --outdated and --uptodate cannot be combined.") cmdoptions.check_list_path_option(options) - packages = get_installed_distributions( - local_only=options.local, - user_only=options.user, - editables_only=options.editable, - include_editables=options.include_editable, - paths=options.path, - ) + skip = set(stdlib_pkgs) + if options.excludes: + skip.update(canonicalize_name(n) for n in options.excludes) + + packages: "_ProcessedDists" = [ + cast("_DistWithLatestInfo", d) + for d in get_environment(options.path).iter_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + skip=skip, + ) + ] # get_not_required must be called firstly in order to find and # filter out all dependencies correctly. Otherwise a package @@ -160,39 +186,60 @@ class ListCommand(IndexGroupCommand): packages = self.get_uptodate(packages, options) self.output_package_listing(packages, options) + return SUCCESS - def get_outdated(self, packages, options): + def get_outdated( + self, packages: "_ProcessedDists", options: Values + ) -> "_ProcessedDists": return [ - dist for dist in self.iter_packages_latest_infos(packages, options) - if dist.latest_version > dist.parsed_version + dist + for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.version ] - def get_uptodate(self, packages, options): + def get_uptodate( + self, packages: "_ProcessedDists", options: Values + ) -> "_ProcessedDists": return [ - dist for dist in self.iter_packages_latest_infos(packages, options) - if dist.latest_version == dist.parsed_version + dist + for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.version ] - def get_not_required(self, packages, options): - dep_keys = set() - for dist in packages: - dep_keys.update(requirement.key for requirement in dist.requires()) - return {pkg for pkg in packages if pkg.key not in dep_keys} + def get_not_required( + self, packages: "_ProcessedDists", options: Values + ) -> "_ProcessedDists": + dep_keys = { + canonicalize_name(dep.name) + for dist in packages + for dep in (dist.iter_dependencies() or ()) + } + + # Create a set to remove duplicate packages, and cast it to a list + # to keep the return type consistent with get_outdated and + # get_uptodate + return list({pkg for pkg in packages if pkg.canonical_name not in dep_keys}) - def iter_packages_latest_infos(self, packages, options): + def iter_packages_latest_infos( + self, packages: "_ProcessedDists", options: Values + ) -> Iterator["_DistWithLatestInfo"]: with self._build_session(options) as session: finder = self._build_package_finder(options, session) - def latest_info(dist): - typ = 'unknown' - all_candidates = finder.find_all_candidates(dist.key) + def latest_info( + dist: "_DistWithLatestInfo", + ) -> Optional["_DistWithLatestInfo"]: + all_candidates = finder.find_all_candidates(dist.canonical_name) if not options.pre: # Remove prereleases - all_candidates = [candidate for candidate in all_candidates - if not candidate.version.is_prerelease] + all_candidates = [ + candidate + for candidate in all_candidates + if not candidate.version.is_prerelease + ] evaluator = finder.make_candidate_evaluator( - project_name=dist.project_name, + project_name=dist.canonical_name, ) best_candidate = evaluator.sort_best_candidate(all_candidates) if best_candidate is None: @@ -200,37 +247,41 @@ class ListCommand(IndexGroupCommand): remote_version = best_candidate.version if best_candidate.link.is_wheel: - typ = 'wheel' + typ = "wheel" else: - typ = 'sdist' - # This is dirty but makes the rest of the code much cleaner + typ = "sdist" dist.latest_version = remote_version dist.latest_filetype = typ return dist - for dist in map(latest_info, packages): + for dist in map_multithread(latest_info, packages): if dist is not None: yield dist - def output_package_listing(self, packages, options): + def output_package_listing( + self, packages: "_ProcessedDists", options: Values + ) -> None: packages = sorted( packages, - key=lambda dist: dist.project_name.lower(), + key=lambda dist: dist.canonical_name, ) - if options.list_format == 'columns' and packages: + if options.list_format == "columns" and packages: data, header = format_for_columns(packages, options) self.output_package_listing_columns(data, header) - elif options.list_format == 'freeze': + elif options.list_format == "freeze": for dist in packages: if options.verbose >= 1: - write_output("%s==%s (%s)", dist.project_name, - dist.version, dist.location) + write_output( + "%s==%s (%s)", dist.raw_name, dist.version, dist.location + ) else: - write_output("%s==%s", dist.project_name, dist.version) - elif options.list_format == 'json': + write_output("%s==%s", dist.raw_name, dist.version) + elif options.list_format == "json": write_output(format_for_json(packages, options)) - def output_package_listing_columns(self, data, header): + def output_package_listing_columns( + self, data: List[List[str]], header: List[str] + ) -> None: # insert the header first: we need to know the size of column names if len(data) > 0: data.insert(0, header) @@ -239,61 +290,72 @@ class ListCommand(IndexGroupCommand): # Create and add a separator. if len(data) > 0: - pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + pkg_strings.insert(1, " ".join(map(lambda x: "-" * x, sizes))) for val in pkg_strings: write_output(val) -def format_for_columns(pkgs, options): +def format_for_columns( + pkgs: "_ProcessedDists", options: Values +) -> Tuple[List[List[str]], List[str]]: """ Convert the package data into something usable by output_package_listing_columns. """ + header = ["Package", "Version"] + running_outdated = options.outdated - # Adjust the header for the `pip list --outdated` case. if running_outdated: - header = ["Package", "Version", "Latest", "Type"] - else: - header = ["Package", "Version"] + header.extend(["Latest", "Type"]) - data = [] - if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): + has_editables = any(x.editable for x in pkgs) + if has_editables: + header.append("Editable project location") + + if options.verbose >= 1: header.append("Location") if options.verbose >= 1: header.append("Installer") + data = [] for proj in pkgs: # if we're working on the 'outdated' list, separate out the # latest_version and type - row = [proj.project_name, proj.version] + row = [proj.raw_name, str(proj.version)] if running_outdated: - row.append(proj.latest_version) + row.append(str(proj.latest_version)) row.append(proj.latest_filetype) - if options.verbose >= 1 or dist_is_editable(proj): - row.append(proj.location) + if has_editables: + row.append(proj.editable_project_location or "") + + if options.verbose >= 1: + row.append(proj.location or "") if options.verbose >= 1: - row.append(get_installer(proj)) + row.append(proj.installer) data.append(row) return data, header -def format_for_json(packages, options): +def format_for_json(packages: "_ProcessedDists", options: Values) -> str: data = [] for dist in packages: info = { - 'name': dist.project_name, - 'version': six.text_type(dist.version), + "name": dist.raw_name, + "version": str(dist.version), } if options.verbose >= 1: - info['location'] = dist.location - info['installer'] = get_installer(dist) + info["location"] = dist.location or "" + info["installer"] = dist.installer if options.outdated: - info['latest_version'] = six.text_type(dist.latest_version) - info['latest_filetype'] = dist.latest_filetype + info["latest_version"] = str(dist.latest_version) + info["latest_filetype"] = dist.latest_filetype + editable_project_location = dist.editable_project_location + if editable_project_location: + info["editable_project_location"] = editable_project_location data.append(info) return json.dumps(data) diff --git a/venv/Lib/site-packages/pip/_internal/commands/search.py b/venv/Lib/site-packages/pip/_internal/commands/search.py index e5f286ea5bfc784fb7f69ad15e7e0c1135489070..03ed925b246dd551ec2ef45095ed6cad00fd2745 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/search.py +++ b/venv/Lib/site-packages/pip/_internal/commands/search.py @@ -1,29 +1,33 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging +import shutil import sys import textwrap +import xmlrpc.client from collections import OrderedDict +from optparse import Values +from typing import TYPE_CHECKING, Dict, List, Optional -from pip._vendor import pkg_resources from pip._vendor.packaging.version import parse as parse_version -# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is -# why we ignore the type on this import -from pip._vendor.six.moves import xmlrpc_client # type: ignore from pip._internal.cli.base_command import Command from pip._internal.cli.req_command import SessionCommandMixin from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS from pip._internal.exceptions import CommandError +from pip._internal.metadata import get_default_environment from pip._internal.models.index import PyPI from pip._internal.network.xmlrpc import PipXmlrpcTransport -from pip._internal.utils.compat import get_terminal_size from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import write_output +if TYPE_CHECKING: + from typing import TypedDict + + class TransformedHit(TypedDict): + name: str + summary: str + versions: List[str] + + logger = logging.getLogger(__name__) @@ -34,113 +38,137 @@ class SearchCommand(Command, SessionCommandMixin): %prog [options] """ ignore_require_venv = True - def __init__(self, *args, **kw): - super(SearchCommand, self).__init__(*args, **kw) + def add_options(self) -> None: self.cmd_opts.add_option( - '-i', '--index', - dest='index', - metavar='URL', + "-i", + "--index", + dest="index", + metavar="URL", default=PyPI.pypi_url, - help='Base URL of Python Package Index (default %default)') + help="Base URL of Python Package Index (default %default)", + ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: if not args: - raise CommandError('Missing required argument (search query).') + raise CommandError("Missing required argument (search query).") query = args pypi_hits = self.search(query, options) hits = transform_hits(pypi_hits) terminal_width = None if sys.stdout.isatty(): - terminal_width = get_terminal_size()[0] + terminal_width = shutil.get_terminal_size()[0] print_results(hits, terminal_width=terminal_width) if pypi_hits: return SUCCESS return NO_MATCHES_FOUND - def search(self, query, options): + def search(self, query: List[str], options: Values) -> List[Dict[str, str]]: index_url = options.index session = self.get_default_session(options) transport = PipXmlrpcTransport(index_url, session) - pypi = xmlrpc_client.ServerProxy(index_url, transport) - hits = pypi.search({'name': query, 'summary': query}, 'or') + pypi = xmlrpc.client.ServerProxy(index_url, transport) + try: + hits = pypi.search({"name": query, "summary": query}, "or") + except xmlrpc.client.Fault as fault: + message = "XMLRPC request failed [code: {code}]\n{string}".format( + code=fault.faultCode, + string=fault.faultString, + ) + raise CommandError(message) + assert isinstance(hits, list) return hits -def transform_hits(hits): +def transform_hits(hits: List[Dict[str, str]]) -> List["TransformedHit"]: """ The list from pypi is really a list of versions. We want a list of packages with the list of versions stored inline. This converts the list from pypi into one we can use. """ - packages = OrderedDict() + packages: Dict[str, "TransformedHit"] = OrderedDict() for hit in hits: - name = hit['name'] - summary = hit['summary'] - version = hit['version'] + name = hit["name"] + summary = hit["summary"] + version = hit["version"] if name not in packages.keys(): packages[name] = { - 'name': name, - 'summary': summary, - 'versions': [version], + "name": name, + "summary": summary, + "versions": [version], } else: - packages[name]['versions'].append(version) + packages[name]["versions"].append(version) # if this is the highest version, replace summary and score - if version == highest_version(packages[name]['versions']): - packages[name]['summary'] = summary + if version == highest_version(packages[name]["versions"]): + packages[name]["summary"] = summary return list(packages.values()) -def print_results(hits, name_column_width=None, terminal_width=None): +def print_dist_installation_info(name: str, latest: str) -> None: + env = get_default_environment() + dist = env.get_distribution(name) + if dist is not None: + with indent_log(): + if dist.version == latest: + write_output("INSTALLED: %s (latest)", dist.version) + else: + write_output("INSTALLED: %s", dist.version) + if parse_version(latest).pre: + write_output( + "LATEST: %s (pre-release; install" + " with `pip install --pre`)", + latest, + ) + else: + write_output("LATEST: %s", latest) + + +def print_results( + hits: List["TransformedHit"], + name_column_width: Optional[int] = None, + terminal_width: Optional[int] = None, +) -> None: if not hits: return if name_column_width is None: - name_column_width = max([ - len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) - for hit in hits - ]) + 4 + name_column_width = ( + max( + [ + len(hit["name"]) + len(highest_version(hit.get("versions", ["-"]))) + for hit in hits + ] + ) + + 4 + ) - installed_packages = [p.project_name for p in pkg_resources.working_set] for hit in hits: - name = hit['name'] - summary = hit['summary'] or '' - latest = highest_version(hit.get('versions', ['-'])) + name = hit["name"] + summary = hit["summary"] or "" + latest = highest_version(hit.get("versions", ["-"])) if terminal_width is not None: target_width = terminal_width - name_column_width - 5 if target_width > 10: # wrap and indent summary to fit terminal - summary = textwrap.wrap(summary, target_width) - summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + summary_lines = textwrap.wrap(summary, target_width) + summary = ("\n" + " " * (name_column_width + 3)).join(summary_lines) - line = '{name_latest:{name_column_width}} - {summary}'.format( - name_latest='{name} ({latest})'.format(**locals()), - **locals()) + name_latest = f"{name} ({latest})" + line = f"{name_latest:{name_column_width}} - {summary}" try: write_output(line) - if name in installed_packages: - dist = pkg_resources.get_distribution(name) - with indent_log(): - if dist.version == latest: - write_output('INSTALLED: %s (latest)', dist.version) - else: - write_output('INSTALLED: %s', dist.version) - if parse_version(latest).pre: - write_output('LATEST: %s (pre-release; install' - ' with "pip install --pre")', latest) - else: - write_output('LATEST: %s', latest) + print_dist_installation_info(name, latest) except UnicodeEncodeError: pass -def highest_version(versions): +def highest_version(versions: List[str]) -> str: return max(versions, key=parse_version) diff --git a/venv/Lib/site-packages/pip/_internal/commands/show.py b/venv/Lib/site-packages/pip/_internal/commands/show.py index a61294ba7bb81b47e518dbd26bd7106cc2ffb839..872292a2b6a8459c05aa68ae4e71bbb3219db01a 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/show.py +++ b/venv/Lib/site-packages/pip/_internal/commands/show.py @@ -1,17 +1,14 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - +import csv import logging -import os -from email.parser import FeedParser +import pathlib +from optparse import Values +from typing import Iterator, List, NamedTuple, Optional, Tuple -from pip._vendor import pkg_resources from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli.base_command import Command from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.metadata import BaseDistribution, get_default_environment from pip._internal.utils.misc import write_output logger = logging.getLogger(__name__) @@ -28,119 +25,177 @@ class ShowCommand(Command): %prog [options] ...""" ignore_require_venv = True - def __init__(self, *args, **kw): - super(ShowCommand, self).__init__(*args, **kw) + def add_options(self) -> None: self.cmd_opts.add_option( - '-f', '--files', - dest='files', - action='store_true', + "-f", + "--files", + dest="files", + action="store_true", default=False, - help='Show the full list of installed files for each package.') + help="Show the full list of installed files for each package.", + ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: if not args: - logger.warning('ERROR: Please provide a package name or names.') + logger.warning("ERROR: Please provide a package name or names.") return ERROR query = args results = search_packages_info(query) if not print_results( - results, list_files=options.files, verbose=options.verbose): + results, list_files=options.files, verbose=options.verbose + ): return ERROR return SUCCESS -def search_packages_info(query): +class _PackageInfo(NamedTuple): + name: str + version: str + location: str + requires: List[str] + required_by: List[str] + installer: str + metadata_version: str + classifiers: List[str] + summary: str + homepage: str + author: str + author_email: str + license: str + entry_points: List[str] + files: Optional[List[str]] + + +def _convert_legacy_entry(entry: Tuple[str, ...], info: Tuple[str, ...]) -> str: + """Convert a legacy installed-files.txt path into modern RECORD path. + + The legacy format stores paths relative to the info directory, while the + modern format stores paths relative to the package root, e.g. the + site-packages directory. + + :param entry: Path parts of the installed-files.txt entry. + :param info: Path parts of the egg-info directory relative to package root. + :returns: The converted entry. + + For best compatibility with symlinks, this does not use ``abspath()`` or + ``Path.resolve()``, but tries to work with path parts: + + 1. While ``entry`` starts with ``..``, remove the equal amounts of parts + from ``info``; if ``info`` is empty, start appending ``..`` instead. + 2. Join the two directly. + """ + while entry and entry[0] == "..": + if not info or info[-1] == "..": + info += ("..",) + else: + info = info[:-1] + entry = entry[1:] + return str(pathlib.Path(*info, *entry)) + + +def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: """ Gather details from installed distributions. Print distribution name, version, location, and installed files. Installed files requires a pip generated 'installed-files.txt' in the distributions '.egg-info' directory. """ - installed = {} - for p in pkg_resources.working_set: - installed[canonicalize_name(p.project_name)] = p + env = get_default_environment() + installed = {dist.canonical_name: dist for dist in env.iter_distributions()} query_names = [canonicalize_name(name) for name in query] missing = sorted( [name for name, pkg in zip(query, query_names) if pkg not in installed] ) if missing: - logger.warning('Package(s) not found: %s', ', '.join(missing)) - - def get_requiring_packages(package_name): - canonical_name = canonicalize_name(package_name) - return [ - pkg.project_name for pkg in pkg_resources.working_set - if canonical_name in - [canonicalize_name(required.name) for required in - pkg.requires()] - ] - - for dist in [installed[pkg] for pkg in query_names if pkg in installed]: - package = { - 'name': dist.project_name, - 'version': dist.version, - 'location': dist.location, - 'requires': [dep.project_name for dep in dist.requires()], - 'required_by': get_requiring_packages(dist.project_name) - } - file_list = None - metadata = None - if isinstance(dist, pkg_resources.DistInfoDistribution): - # RECORDs should be part of .dist-info metadatas - if dist.has_metadata('RECORD'): - lines = dist.get_metadata_lines('RECORD') - paths = [l.split(',')[0] for l in lines] - paths = [os.path.join(dist.location, p) for p in paths] - file_list = [os.path.relpath(p, dist.location) for p in paths] - - if dist.has_metadata('METADATA'): - metadata = dist.get_metadata('METADATA') + logger.warning("Package(s) not found: %s", ", ".join(missing)) + + def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: + return ( + dist.metadata["Name"] or "UNKNOWN" + for dist in installed.values() + if current_dist.canonical_name + in {canonicalize_name(d.name) for d in dist.iter_dependencies()} + ) + + def _files_from_record(dist: BaseDistribution) -> Optional[Iterator[str]]: + try: + text = dist.read_text("RECORD") + except FileNotFoundError: + return None + # This extra Path-str cast normalizes entries. + return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines())) + + def _files_from_legacy(dist: BaseDistribution) -> Optional[Iterator[str]]: + try: + text = dist.read_text("installed-files.txt") + except FileNotFoundError: + return None + paths = (p for p in text.splitlines(keepends=False) if p) + root = dist.location + info = dist.info_directory + if root is None or info is None: + return paths + try: + info_rel = pathlib.Path(info).relative_to(root) + except ValueError: # info is not relative to root. + return paths + if not info_rel.parts: # info *is* root. + return paths + return ( + _convert_legacy_entry(pathlib.Path(p).parts, info_rel.parts) for p in paths + ) + + for query_name in query_names: + try: + dist = installed[query_name] + except KeyError: + continue + + requires = sorted((req.name for req in dist.iter_dependencies()), key=str.lower) + required_by = sorted(_get_requiring_packages(dist), key=str.lower) + + try: + entry_points_text = dist.read_text("entry_points.txt") + entry_points = entry_points_text.splitlines(keepends=False) + except FileNotFoundError: + entry_points = [] + + files_iter = _files_from_record(dist) or _files_from_legacy(dist) + if files_iter is None: + files: Optional[List[str]] = None else: - # Otherwise use pip's log for .egg-info's - if dist.has_metadata('installed-files.txt'): - paths = dist.get_metadata_lines('installed-files.txt') - paths = [os.path.join(dist.egg_info, p) for p in paths] - file_list = [os.path.relpath(p, dist.location) for p in paths] - - if dist.has_metadata('PKG-INFO'): - metadata = dist.get_metadata('PKG-INFO') - - if dist.has_metadata('entry_points.txt'): - entry_points = dist.get_metadata_lines('entry_points.txt') - package['entry_points'] = entry_points - - if dist.has_metadata('INSTALLER'): - for line in dist.get_metadata_lines('INSTALLER'): - if line.strip(): - package['installer'] = line.strip() - break - - # @todo: Should pkg_resources.Distribution have a - # `get_pkg_info` method? - feed_parser = FeedParser() - feed_parser.feed(metadata) - pkg_info_dict = feed_parser.close() - for key in ('metadata-version', 'summary', - 'home-page', 'author', 'author-email', 'license'): - package[key] = pkg_info_dict.get(key) - - # It looks like FeedParser cannot deal with repeated headers - classifiers = [] - for line in metadata.splitlines(): - if line.startswith('Classifier: '): - classifiers.append(line[len('Classifier: '):]) - package['classifiers'] = classifiers - - if file_list: - package['files'] = sorted(file_list) - yield package - - -def print_results(distributions, list_files=False, verbose=False): + files = sorted(files_iter) + + metadata = dist.metadata + + yield _PackageInfo( + name=dist.raw_name, + version=str(dist.version), + location=dist.location or "", + requires=requires, + required_by=required_by, + installer=dist.installer, + metadata_version=dist.metadata_version or "", + classifiers=metadata.get_all("Classifier", []), + summary=metadata.get("Summary", ""), + homepage=metadata.get("Home-page", ""), + author=metadata.get("Author", ""), + author_email=metadata.get("Author-email", ""), + license=metadata.get("License", ""), + entry_points=entry_points, + files=files, + ) + + +def print_results( + distributions: Iterator[_PackageInfo], + list_files: bool, + verbose: bool, +) -> bool: """ Print the information from installed distributions found. """ @@ -150,31 +205,31 @@ def print_results(distributions, list_files=False, verbose=False): if i > 0: write_output("---") - write_output("Name: %s", dist.get('name', '')) - write_output("Version: %s", dist.get('version', '')) - write_output("Summary: %s", dist.get('summary', '')) - write_output("Home-page: %s", dist.get('home-page', '')) - write_output("Author: %s", dist.get('author', '')) - write_output("Author-email: %s", dist.get('author-email', '')) - write_output("License: %s", dist.get('license', '')) - write_output("Location: %s", dist.get('location', '')) - write_output("Requires: %s", ', '.join(dist.get('requires', []))) - write_output("Required-by: %s", ', '.join(dist.get('required_by', []))) + write_output("Name: %s", dist.name) + write_output("Version: %s", dist.version) + write_output("Summary: %s", dist.summary) + write_output("Home-page: %s", dist.homepage) + write_output("Author: %s", dist.author) + write_output("Author-email: %s", dist.author_email) + write_output("License: %s", dist.license) + write_output("Location: %s", dist.location) + write_output("Requires: %s", ", ".join(dist.requires)) + write_output("Required-by: %s", ", ".join(dist.required_by)) if verbose: - write_output("Metadata-Version: %s", - dist.get('metadata-version', '')) - write_output("Installer: %s", dist.get('installer', '')) + write_output("Metadata-Version: %s", dist.metadata_version) + write_output("Installer: %s", dist.installer) write_output("Classifiers:") - for classifier in dist.get('classifiers', []): + for classifier in dist.classifiers: write_output(" %s", classifier) write_output("Entry-points:") - for entry in dist.get('entry_points', []): + for entry in dist.entry_points: write_output(" %s", entry.strip()) if list_files: write_output("Files:") - for line in dist.get('files', []): - write_output(" %s", line.strip()) - if "files" not in dist: - write_output("Cannot locate installed-files.txt") + if dist.files is None: + write_output("Cannot locate RECORD or installed-files.txt") + else: + for line in dist.files: + write_output(" %s", line.strip()) return results_printed diff --git a/venv/Lib/site-packages/pip/_internal/commands/uninstall.py b/venv/Lib/site-packages/pip/_internal/commands/uninstall.py index 5db4fb46721b9d1a621aafc7c220ed48babc540d..bb9e8e6a380aac2b42a120a11ed1a8e292a0efe7 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/uninstall.py +++ b/venv/Lib/site-packages/pip/_internal/commands/uninstall.py @@ -1,12 +1,12 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import +import logging +from optparse import Values +from typing import List from pip._vendor.packaging.utils import canonicalize_name from pip._internal.cli.base_command import Command -from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.req_command import SessionCommandMixin, warn_if_run_as_root +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import InstallationError from pip._internal.req import parse_requirements from pip._internal.req.constructors import ( @@ -15,6 +15,8 @@ from pip._internal.req.constructors import ( ) from pip._internal.utils.misc import protect_pip_from_modification_on_windows +logger = logging.getLogger(__name__) + class UninstallCommand(Command, SessionCommandMixin): """ @@ -31,50 +33,60 @@ class UninstallCommand(Command, SessionCommandMixin): %prog [options] ... %prog [options] -r ...""" - def __init__(self, *args, **kw): - super(UninstallCommand, self).__init__(*args, **kw) + def add_options(self) -> None: self.cmd_opts.add_option( - '-r', '--requirement', - dest='requirements', - action='append', + "-r", + "--requirement", + dest="requirements", + action="append", default=[], - metavar='file', - help='Uninstall all the packages listed in the given requirements ' - 'file. This option can be used multiple times.', + metavar="file", + help=( + "Uninstall all the packages listed in the given requirements " + "file. This option can be used multiple times." + ), ) self.cmd_opts.add_option( - '-y', '--yes', - dest='yes', - action='store_true', - help="Don't ask for confirmation of uninstall deletions.") + "-y", + "--yes", + dest="yes", + action="store_true", + help="Don't ask for confirmation of uninstall deletions.", + ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options, args): + def run(self, options: Values, args: List[str]) -> int: session = self.get_default_session(options) reqs_to_uninstall = {} for name in args: req = install_req_from_line( - name, isolated=options.isolated_mode, + name, + isolated=options.isolated_mode, ) if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req + else: + logger.warning( + "Invalid requirement: %r ignored -" + " the uninstall command expects named" + " requirements.", + name, + ) for filename in options.requirements: for parsed_req in parse_requirements( - filename, - options=options, - session=session): + filename, options=options, session=session + ): req = install_req_from_parsed_requirement( - parsed_req, - isolated=options.isolated_mode + parsed_req, isolated=options.isolated_mode ) if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req if not reqs_to_uninstall: raise InstallationError( - 'You must give at least one requirement to {self.name} (see ' - '"pip help {self.name}")'.format(**locals()) + f"You must give at least one requirement to {self.name} (see " + f'"pip help {self.name}")' ) protect_pip_from_modification_on_windows( @@ -83,7 +95,11 @@ class UninstallCommand(Command, SessionCommandMixin): for req in reqs_to_uninstall.values(): uninstall_pathset = req.uninstall( - auto_confirm=options.yes, verbose=self.verbosity > 0, + auto_confirm=options.yes, + verbose=self.verbosity > 0, ) if uninstall_pathset: uninstall_pathset.commit() + + warn_if_run_as_root() + return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/commands/wheel.py b/venv/Lib/site-packages/pip/_internal/commands/wheel.py index 48f3bfa29ca8f10842e1278bffe290376c2f118f..cea81ee5195108204e0dd0d06a561b71718a88de 100644 --- a/venv/Lib/site-packages/pip/_internal/commands/wheel.py +++ b/venv/Lib/site-packages/pip/_internal/commands/wheel.py @@ -1,29 +1,20 @@ -# -*- coding: utf-8 -*- - -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import os import shutil +from optparse import Values +from typing import List from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError +from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.misc import ensure_dir, normalize_path from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.wheel_builder import build, should_build_for_wheel_command -if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Any, List - - logger = logging.getLogger(__name__) @@ -49,58 +40,55 @@ class WheelCommand(RequirementCommand): %prog [options] [-e] ... %prog [options] ...""" - def __init__(self, *args, **kw): - super(WheelCommand, self).__init__(*args, **kw) - - cmd_opts = self.cmd_opts + def add_options(self) -> None: - cmd_opts.add_option( - '-w', '--wheel-dir', - dest='wheel_dir', - metavar='dir', + self.cmd_opts.add_option( + "-w", + "--wheel-dir", + dest="wheel_dir", + metavar="dir", default=os.curdir, - help=("Build wheels into , where the default is the " - "current working directory."), + help=( + "Build wheels into , where the default is the " + "current working directory." + ), ) - cmd_opts.add_option(cmdoptions.no_binary()) - cmd_opts.add_option(cmdoptions.only_binary()) - cmd_opts.add_option(cmdoptions.prefer_binary()) - cmd_opts.add_option( - '--build-option', - dest='build_options', - metavar='options', - action='append', - help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + + self.cmd_opts.add_option( + "--no-verify", + dest="no_verify", + action="store_true", + default=False, + help="Don't verify if built wheel is valid.", ) - cmd_opts.add_option(cmdoptions.no_build_isolation()) - cmd_opts.add_option(cmdoptions.use_pep517()) - cmd_opts.add_option(cmdoptions.no_use_pep517()) - cmd_opts.add_option(cmdoptions.constraints()) - cmd_opts.add_option(cmdoptions.editable()) - cmd_opts.add_option(cmdoptions.requirements()) - cmd_opts.add_option(cmdoptions.src()) - cmd_opts.add_option(cmdoptions.ignore_requires_python()) - cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option(cmdoptions.build_dir()) - cmd_opts.add_option(cmdoptions.progress_bar()) - - cmd_opts.add_option( - '--global-option', - dest='global_options', - action='append', - metavar='options', - help="Extra global options to be supplied to the setup.py " - "call before the 'bdist_wheel' command.") - - cmd_opts.add_option( - '--pre', - action='store_true', + + self.cmd_opts.add_option(cmdoptions.build_options()) + self.cmd_opts.add_option(cmdoptions.global_options()) + + self.cmd_opts.add_option( + "--pre", + action="store_true", default=False, - help=("Include pre-release and development versions. By default, " - "pip only finds stable versions."), + help=( + "Include pre-release and development versions. By default, " + "pip only finds stable versions." + ), ) - cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) index_opts = cmdoptions.make_option_group( cmdoptions.index_group, @@ -108,17 +96,15 @@ class WheelCommand(RequirementCommand): ) self.parser.insert_option_group(0, index_opts) - self.parser.insert_option_group(0, cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup - def run(self, options, args): - # type: (Values, List[Any]) -> None + def run(self, options: Values, args: List[str]) -> int: cmdoptions.check_install_build_global(options) session = self.get_default_session(options) finder = self._build_package_finder(options, session) - build_delete = (not (options.no_clean or options.build_dir)) wheel_cache = WheelCache(options.cache_dir, options.format_control) options.wheel_dir = normalize_path(options.wheel_dir) @@ -127,8 +113,7 @@ class WheelCommand(RequirementCommand): req_tracker = self.enter_context(get_requirement_tracker()) directory = TempDirectory( - options.build_dir, - delete=build_delete, + delete=not options.no_clean, kind="wheel", globally_managed=True, ) @@ -141,7 +126,7 @@ class WheelCommand(RequirementCommand): req_tracker=req_tracker, session=session, finder=finder, - wheel_download_dir=options.wheel_dir, + download_dir=options.wheel_dir, use_user_site=False, ) @@ -156,19 +141,20 @@ class WheelCommand(RequirementCommand): self.trace_basic_info(finder) - requirement_set = resolver.resolve( - reqs, check_supported_wheels=True - ) + requirement_set = resolver.resolve(reqs, check_supported_wheels=True) - reqs_to_build = [ - r for r in requirement_set.requirements.values() - if should_build_for_wheel_command(r) - ] + reqs_to_build: List[InstallRequirement] = [] + for req in requirement_set.requirements.values(): + if req.is_wheel: + preparer.save_linked_requirement(req) + elif should_build_for_wheel_command(req): + reqs_to_build.append(req) # build wheels build_successes, build_failures = build( reqs_to_build, wheel_cache=wheel_cache, + verify=(not options.no_verify), build_options=options.build_options or [], global_options=options.global_options or [], ) @@ -181,10 +167,11 @@ class WheelCommand(RequirementCommand): except OSError as e: logger.warning( "Building wheel for %s failed: %s", - req.name, e, + req.name, + e, ) build_failures.append(req) if len(build_failures) != 0: - raise CommandError( - "Failed to build one or more wheels" - ) + raise CommandError("Failed to build one or more wheels") + + return SUCCESS diff --git a/venv/Lib/site-packages/pip/_internal/configuration.py b/venv/Lib/site-packages/pip/_internal/configuration.py index 2648b8af327a833a4cafc0ddaaa250d2c44bf66b..4c3a362fd2c13548010681702bceafc0ee2da426 100644 --- a/venv/Lib/site-packages/pip/_internal/configuration.py +++ b/venv/Lib/site-packages/pip/_internal/configuration.py @@ -11,49 +11,51 @@ Some terminology: A single word describing where the configuration key-value pair came from """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - +import configparser import locale -import logging import os import sys - -from pip._vendor.six.moves import configparser +from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple from pip._internal.exceptions import ( ConfigurationError, ConfigurationFileCouldNotBeLoaded, ) from pip._internal.utils import appdirs -from pip._internal.utils.compat import WINDOWS, expanduser +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import getLogger from pip._internal.utils.misc import ensure_dir, enum -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Dict, Iterable, List, NewType, Optional, Tuple - ) +RawConfigParser = configparser.RawConfigParser # Shorthand +Kind = NewType("Kind", str) - RawConfigParser = configparser.RawConfigParser # Shorthand - Kind = NewType("Kind", str) +CONFIG_BASENAME = "pip.ini" if WINDOWS else "pip.conf" +ENV_NAMES_IGNORED = "version", "help" -logger = logging.getLogger(__name__) +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + SITE="site", # [Virtual] Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) +OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR +VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE + +logger = getLogger(__name__) # NOTE: Maybe use the optionx attribute to normalize keynames. -def _normalize_name(name): - # type: (str) -> str - """Make a name consistent regardless of source (environment or file) - """ - name = name.lower().replace('_', '-') - if name.startswith('--'): +def _normalize_name(name: str) -> str: + """Make a name consistent regardless of source (environment or file)""" + name = name.lower().replace("_", "-") + if name.startswith("--"): name = name[2:] # only prefer long opts return name -def _disassemble_key(name): - # type: (str) -> List[str] +def _disassemble_key(name: str) -> List[str]: if "." not in name: error_message = ( "Key does not contain dot separated section and key. " @@ -63,35 +65,18 @@ def _disassemble_key(name): return name.split(".", 1) -# The kinds of configurations there are. -kinds = enum( - USER="user", # User Specific - GLOBAL="global", # System Wide - SITE="site", # [Virtual] Environment Specific - ENV="env", # from PIP_CONFIG_FILE - ENV_VAR="env-var", # from Environment Variables -) - - -CONFIG_BASENAME = 'pip.ini' if WINDOWS else 'pip.conf' - - -def get_configuration_files(): - # type: () -> Dict[Kind, List[str]] +def get_configuration_files() -> Dict[Kind, List[str]]: global_config_files = [ - os.path.join(path, CONFIG_BASENAME) - for path in appdirs.site_config_dirs('pip') + os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs("pip") ] site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME) legacy_config_file = os.path.join( - expanduser('~'), - 'pip' if WINDOWS else '.pip', + os.path.expanduser("~"), + "pip" if WINDOWS else ".pip", CONFIG_BASENAME, ) - new_config_file = os.path.join( - appdirs.user_config_dir("pip"), CONFIG_BASENAME - ) + new_config_file = os.path.join(appdirs.user_config_dir("pip"), CONFIG_BASENAME) return { kinds.GLOBAL: global_config_files, kinds.SITE: [site_config_file], @@ -99,7 +84,7 @@ def get_configuration_files(): } -class Configuration(object): +class Configuration: """Handles management of configuration. Provides an interface to accessing and managing configuration files. @@ -113,78 +98,60 @@ class Configuration(object): and the data stored is also nice. """ - def __init__(self, isolated, load_only=None): - # type: (bool, Kind) -> None - super(Configuration, self).__init__() + def __init__(self, isolated: bool, load_only: Optional[Kind] = None) -> None: + super().__init__() - _valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.SITE, None] - if load_only not in _valid_load_only: + if load_only is not None and load_only not in VALID_LOAD_ONLY: raise ConfigurationError( "Got invalid value for load_only - should be one of {}".format( - ", ".join(map(repr, _valid_load_only[:-1])) + ", ".join(map(repr, VALID_LOAD_ONLY)) ) ) - self.isolated = isolated # type: bool - self.load_only = load_only # type: Optional[Kind] - - # The order here determines the override order. - self._override_order = [ - kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR - ] - - self._ignore_env_names = ["version", "help"] + self.isolated = isolated + self.load_only = load_only # Because we keep track of where we got the data from - self._parsers = { - variant: [] for variant in self._override_order - } # type: Dict[Kind, List[Tuple[str, RawConfigParser]]] - self._config = { - variant: {} for variant in self._override_order - } # type: Dict[Kind, Dict[str, Any]] - self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]] - - def load(self): - # type: () -> None - """Loads configuration from configuration files and environment - """ + self._parsers: Dict[Kind, List[Tuple[str, RawConfigParser]]] = { + variant: [] for variant in OVERRIDE_ORDER + } + self._config: Dict[Kind, Dict[str, Any]] = { + variant: {} for variant in OVERRIDE_ORDER + } + self._modified_parsers: List[Tuple[str, RawConfigParser]] = [] + + def load(self) -> None: + """Loads configuration from configuration files and environment""" self._load_config_files() if not self.isolated: self._load_environment_vars() - def get_file_to_edit(self): - # type: () -> Optional[str] - """Returns the file with highest priority in configuration - """ - assert self.load_only is not None, \ - "Need to be specified a file to be editing" + def get_file_to_edit(self) -> Optional[str]: + """Returns the file with highest priority in configuration""" + assert self.load_only is not None, "Need to be specified a file to be editing" try: return self._get_parser_to_modify()[0] except IndexError: return None - def items(self): - # type: () -> Iterable[Tuple[str, Any]] + def items(self) -> Iterable[Tuple[str, Any]]: """Returns key-value pairs like dict.items() representing the loaded configuration """ return self._dictionary.items() - def get_value(self, key): - # type: (str) -> Any - """Get a value from the configuration. - """ + def get_value(self, key: str) -> Any: + """Get a value from the configuration.""" try: return self._dictionary[key] except KeyError: - raise ConfigurationError("No such key - {}".format(key)) + raise ConfigurationError(f"No such key - {key}") - def set_value(self, key, value): - # type: (str, Any) -> None - """Modify a value in the configuration. - """ + def set_value(self, key: str, value: Any) -> None: + """Modify a value in the configuration.""" self._ensure_have_load_only() + assert self.load_only fname, parser = self._get_parser_to_modify() if parser is not None: @@ -198,49 +165,35 @@ class Configuration(object): self._config[self.load_only][key] = value self._mark_as_modified(fname, parser) - def unset_value(self, key): - # type: (str) -> None - """Unset a value in the configuration. - """ + def unset_value(self, key: str) -> None: + """Unset a value in the configuration.""" self._ensure_have_load_only() + assert self.load_only if key not in self._config[self.load_only]: - raise ConfigurationError("No such key - {}".format(key)) + raise ConfigurationError(f"No such key - {key}") fname, parser = self._get_parser_to_modify() if parser is not None: section, name = _disassemble_key(key) - - # Remove the key in the parser - modified_something = False - if parser.has_section(section): - # Returns whether the option was removed or not - modified_something = parser.remove_option(section, name) - - if modified_something: - # name removed from parser, section may now be empty - section_iter = iter(parser.items(section)) - try: - val = next(section_iter) - except StopIteration: - val = None - - if val is None: - parser.remove_section(section) - - self._mark_as_modified(fname, parser) - else: + if not ( + parser.has_section(section) and parser.remove_option(section, name) + ): + # The option was not removed. raise ConfigurationError( "Fatal Internal error [id=1]. Please report as a bug." ) + # The section may be empty after the option was removed. + if not parser.items(section): + parser.remove_section(section) + self._mark_as_modified(fname, parser) + del self._config[self.load_only][key] - def save(self): - # type: () -> None - """Save the current in-memory state. - """ + def save(self) -> None: + """Save the current in-memory state.""" self._ensure_have_load_only() for fname, parser in self._modified_parsers: @@ -256,31 +209,26 @@ class Configuration(object): # Private routines # - def _ensure_have_load_only(self): - # type: () -> None + def _ensure_have_load_only(self) -> None: if self.load_only is None: raise ConfigurationError("Needed a specific file to be modifying.") logger.debug("Will be working with %s variant only", self.load_only) @property - def _dictionary(self): - # type: () -> Dict[str, Any] - """A dictionary representing the loaded configuration. - """ + def _dictionary(self) -> Dict[str, Any]: + """A dictionary representing the loaded configuration.""" # NOTE: Dictionaries are not populated if not loaded. So, conditionals # are not needed here. retval = {} - for variant in self._override_order: + for variant in OVERRIDE_ORDER: retval.update(self._config[variant]) return retval - def _load_config_files(self): - # type: () -> None - """Loads configuration from configuration files - """ - config_files = dict(self._iter_config_files()) + def _load_config_files(self) -> None: + """Loads configuration from configuration files""" + config_files = dict(self.iter_config_files()) if config_files[kinds.ENV][0:1] == [os.devnull]: logger.debug( "Skipping loading configuration files due to " @@ -293,9 +241,7 @@ class Configuration(object): # If there's specific variant set in `load_only`, load only # that variant, not the others. if self.load_only is not None and variant != self.load_only: - logger.debug( - "Skipping file '%s' (variant: %s)", fname, variant - ) + logger.debug("Skipping file '%s' (variant: %s)", fname, variant) continue parser = self._load_file(variant, fname) @@ -303,9 +249,8 @@ class Configuration(object): # Keeping track of the parsers used self._parsers[variant].append((fname, parser)) - def _load_file(self, variant, fname): - # type: (Kind, str) -> RawConfigParser - logger.debug("For variant '%s', will try loading '%s'", variant, fname) + def _load_file(self, variant: Kind, fname: str) -> RawConfigParser: + logger.verbose("For variant '%s', will try loading '%s'", variant, fname) parser = self._construct_parser(fname) for section in parser.sections(): @@ -314,8 +259,7 @@ class Configuration(object): return parser - def _construct_parser(self, fname): - # type: (str) -> RawConfigParser + def _construct_parser(self, fname: str) -> RawConfigParser: parser = configparser.RawConfigParser() # If there is no such file, don't bother reading it but create the # parser anyway, to hold the data. @@ -337,16 +281,15 @@ class Configuration(object): raise ConfigurationFileCouldNotBeLoaded(error=error) return parser - def _load_environment_vars(self): - # type: () -> None - """Loads configuration from environment variables - """ + def _load_environment_vars(self) -> None: + """Loads configuration from environment variables""" self._config[kinds.ENV_VAR].update( - self._normalized_keys(":env:", self._get_environ_vars()) + self._normalized_keys(":env:", self.get_environ_vars()) ) - def _normalized_keys(self, section, items): - # type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any] + def _normalized_keys( + self, section: str, items: Iterable[Tuple[str, Any]] + ) -> Dict[str, Any]: """Normalizes items to construct a dictionary with normalized keys. This routine is where the names become keys and are made the same @@ -358,20 +301,16 @@ class Configuration(object): normalized[key] = val return normalized - def _get_environ_vars(self): - # type: () -> Iterable[Tuple[str, str]] + def get_environ_vars(self) -> Iterable[Tuple[str, str]]: """Returns a generator with all environmental vars with prefix PIP_""" for key, val in os.environ.items(): - should_be_yielded = ( - key.startswith("PIP_") and - key[4:].lower() not in self._ignore_env_names - ) - if should_be_yielded: - yield key[4:].lower(), val + if key.startswith("PIP_"): + name = key[4:].lower() + if name not in ENV_NAMES_IGNORED: + yield name, val # XXX: This is patched in the tests. - def _iter_config_files(self): - # type: () -> Iterable[Tuple[Kind, List[str]]] + def iter_config_files(self) -> Iterable[Tuple[Kind, List[str]]]: """Yields variant and configuration files associated with it. This should be treated like items of a dictionary. @@ -379,7 +318,7 @@ class Configuration(object): # SMELL: Move the conditions out of this function # environment variables have the lowest priority - config_file = os.environ.get('PIP_CONFIG_FILE', None) + config_file = os.environ.get("PIP_CONFIG_FILE", None) if config_file is not None: yield kinds.ENV, [config_file] else: @@ -401,9 +340,13 @@ class Configuration(object): # finally virtualenv configuration first trumping others yield kinds.SITE, config_files[kinds.SITE] - def _get_parser_to_modify(self): - # type: () -> Tuple[str, RawConfigParser] + def get_values_in_config(self, variant: Kind) -> Dict[str, Any]: + """Get values present in a config file""" + return self._config[variant] + + def _get_parser_to_modify(self) -> Tuple[str, RawConfigParser]: # Determine which parser to modify + assert self.load_only parsers = self._parsers[self.load_only] if not parsers: # This should not happen if everything works correctly. @@ -415,12 +358,10 @@ class Configuration(object): return parsers[-1] # XXX: This is patched in the tests. - def _mark_as_modified(self, fname, parser): - # type: (str, RawConfigParser) -> None + def _mark_as_modified(self, fname: str, parser: RawConfigParser) -> None: file_parser_tuple = (fname, parser) if file_parser_tuple not in self._modified_parsers: self._modified_parsers.append(file_parser_tuple) - def __repr__(self): - # type: () -> str - return "{}({!r})".format(self.__class__.__name__, self._dictionary) + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._dictionary!r})" diff --git a/venv/Lib/site-packages/pip/_internal/distributions/__init__.py b/venv/Lib/site-packages/pip/_internal/distributions/__init__.py index d5c1afc5bc1ffd09c0ce46f4f2f700a1b996fe47..9a89a838b9a5cb264e9ae9d269fbedca6e2d6333 100644 --- a/venv/Lib/site-packages/pip/_internal/distributions/__init__.py +++ b/venv/Lib/site-packages/pip/_internal/distributions/__init__.py @@ -1,16 +1,13 @@ +from pip._internal.distributions.base import AbstractDistribution from pip._internal.distributions.sdist import SourceDistribution from pip._internal.distributions.wheel import WheelDistribution -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.req.req_install import InstallRequirement -if MYPY_CHECK_RUNNING: - from pip._internal.distributions.base import AbstractDistribution - from pip._internal.req.req_install import InstallRequirement - -def make_distribution_for_install_requirement(install_req): - # type: (InstallRequirement) -> AbstractDistribution - """Returns a Distribution for the given InstallRequirement - """ +def make_distribution_for_install_requirement( + install_req: InstallRequirement, +) -> AbstractDistribution: + """Returns a Distribution for the given InstallRequirement""" # Editable requirements will always be source distributions. They use the # legacy logic until we create a modern standard for them. if install_req.editable: diff --git a/venv/Lib/site-packages/pip/_internal/distributions/base.py b/venv/Lib/site-packages/pip/_internal/distributions/base.py index b836b98d162abda775f4b0c2b132eac4cf58a22d..149fff55dab0b7bf559e941d5db52bbf562548c7 100644 --- a/venv/Lib/site-packages/pip/_internal/distributions/base.py +++ b/venv/Lib/site-packages/pip/_internal/distributions/base.py @@ -1,19 +1,11 @@ import abc -from pip._vendor.six import add_metaclass +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata.base import BaseDistribution +from pip._internal.req import InstallRequirement -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Optional - - from pip._vendor.pkg_resources import Distribution - from pip._internal.req import InstallRequirement - from pip._internal.index.package_finder import PackageFinder - - -@add_metaclass(abc.ABCMeta) -class AbstractDistribution(object): +class AbstractDistribution(metaclass=abc.ABCMeta): """A base class for handling installable artifacts. The requirements for anything installable are as follows: @@ -29,17 +21,16 @@ class AbstractDistribution(object): above metadata. """ - def __init__(self, req): - # type: (InstallRequirement) -> None - super(AbstractDistribution, self).__init__() + def __init__(self, req: InstallRequirement) -> None: + super().__init__() self.req = req @abc.abstractmethod - def get_pkg_resources_distribution(self): - # type: () -> Optional[Distribution] + def get_metadata_distribution(self) -> BaseDistribution: raise NotImplementedError() @abc.abstractmethod - def prepare_distribution_metadata(self, finder, build_isolation): - # type: (PackageFinder, bool) -> None + def prepare_distribution_metadata( + self, finder: PackageFinder, build_isolation: bool + ) -> None: raise NotImplementedError() diff --git a/venv/Lib/site-packages/pip/_internal/distributions/installed.py b/venv/Lib/site-packages/pip/_internal/distributions/installed.py index 0d15bf42405e541b5154de307c54df47b9b7e2ec..6c8c179299bff0a0f57a9659c0978c536e41997f 100644 --- a/venv/Lib/site-packages/pip/_internal/distributions/installed.py +++ b/venv/Lib/site-packages/pip/_internal/distributions/installed.py @@ -1,11 +1,6 @@ from pip._internal.distributions.base import AbstractDistribution -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Optional - - from pip._vendor.pkg_resources import Distribution - from pip._internal.index.package_finder import PackageFinder +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution class InstalledDistribution(AbstractDistribution): @@ -15,10 +10,13 @@ class InstalledDistribution(AbstractDistribution): been computed. """ - def get_pkg_resources_distribution(self): - # type: () -> Optional[Distribution] - return self.req.satisfied_by + def get_metadata_distribution(self) -> BaseDistribution: + from pip._internal.metadata.pkg_resources import Distribution as _Dist + + assert self.req.satisfied_by is not None, "not actually installed" + return _Dist(self.req.satisfied_by) - def prepare_distribution_metadata(self, finder, build_isolation): - # type: (PackageFinder, bool) -> None + def prepare_distribution_metadata( + self, finder: PackageFinder, build_isolation: bool + ) -> None: pass diff --git a/venv/Lib/site-packages/pip/_internal/distributions/sdist.py b/venv/Lib/site-packages/pip/_internal/distributions/sdist.py index be3d7d97a1cfe877ce549603ebe1e17c65d68803..cd85ac5c4397c5539235c5c33fd3a62b982e8527 100644 --- a/venv/Lib/site-packages/pip/_internal/distributions/sdist.py +++ b/venv/Lib/site-packages/pip/_internal/distributions/sdist.py @@ -1,17 +1,12 @@ import logging +from typing import Iterable, Set, Tuple from pip._internal.build_env import BuildEnvironment from pip._internal.distributions.base import AbstractDistribution from pip._internal.exceptions import InstallationError +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution from pip._internal.utils.subprocess import runner_with_spinner_message -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Set, Tuple - - from pip._vendor.pkg_resources import Distribution - from pip._internal.index.package_finder import PackageFinder - logger = logging.getLogger(__name__) @@ -23,40 +18,37 @@ class SourceDistribution(AbstractDistribution): generated, either using PEP 517 or using the legacy `setup.py egg_info`. """ - def get_pkg_resources_distribution(self): - # type: () -> Distribution - return self.req.get_dist() + def get_metadata_distribution(self) -> BaseDistribution: + from pip._internal.metadata.pkg_resources import Distribution as _Dist - def prepare_distribution_metadata(self, finder, build_isolation): - # type: (PackageFinder, bool) -> None + return _Dist(self.req.get_dist()) + + def prepare_distribution_metadata( + self, finder: PackageFinder, build_isolation: bool + ) -> None: # Load pyproject.toml, to determine whether PEP 517 is to be used self.req.load_pyproject_toml() # Set up the build isolation, if this requirement should be isolated should_isolate = self.req.use_pep517 and build_isolation if should_isolate: - self._setup_isolation(finder) + # Setup an isolated environment and install the build backend static + # requirements in it. + self._prepare_build_backend(finder) + # Check that if the requirement is editable, it either supports PEP 660 or + # has a setup.py or a setup.cfg. This cannot be done earlier because we need + # to setup the build backend to verify it supports build_editable, nor can + # it be done later, because we want to avoid installing build requirements + # needlessly. Doing it here also works around setuptools generating + # UNKNOWN.egg-info when running get_requires_for_build_wheel on a directory + # without setup.py nor setup.cfg. + self.req.isolated_editable_sanity_check() + # Install the dynamic build requirements. + self._install_build_reqs(finder) self.req.prepare_metadata() - def _setup_isolation(self, finder): - # type: (PackageFinder) -> None - def _raise_conflicts(conflicting_with, conflicting_reqs): - # type: (str, Set[Tuple[str, str]]) -> None - format_string = ( - "Some build dependencies for {requirement} " - "conflict with {conflicting_with}: {description}." - ) - error_message = format_string.format( - requirement=self.req, - conflicting_with=conflicting_with, - description=', '.join( - '{} is incompatible with {}'.format(installed, wanted) - for installed, wanted in sorted(conflicting) - ) - ) - raise InstallationError(error_message) - + def _prepare_build_backend(self, finder: PackageFinder) -> None: # Isolate in a BuildEnvironment and install the build-time # requirements. pyproject_requires = self.req.pyproject_requires @@ -64,15 +56,13 @@ class SourceDistribution(AbstractDistribution): self.req.build_env = BuildEnvironment() self.req.build_env.install_requirements( - finder, pyproject_requires, 'overlay', - "Installing build dependencies" + finder, pyproject_requires, "overlay", "Installing build dependencies" ) conflicting, missing = self.req.build_env.check_requirements( self.req.requirements_to_check ) if conflicting: - _raise_conflicts("PEP 517/518 supported requirements", - conflicting) + self._raise_conflicts("PEP 517/518 supported requirements", conflicting) if missing: logger.warning( "Missing build requirements in pyproject.toml for %s.", @@ -81,24 +71,59 @@ class SourceDistribution(AbstractDistribution): logger.warning( "The project does not specify a build backend, and " "pip cannot fall back to setuptools without %s.", - " and ".join(map(repr, sorted(missing))) + " and ".join(map(repr, sorted(missing))), ) - # Install any extra build dependencies that the backend requests. - # This must be done in a second pass, as the pyproject.toml - # dependencies must be installed before we can call the backend. + + def _get_build_requires_wheel(self) -> Iterable[str]: + with self.req.build_env: + runner = runner_with_spinner_message("Getting requirements to build wheel") + backend = self.req.pep517_backend + assert backend is not None + with backend.subprocess_runner(runner): + return backend.get_requires_for_build_wheel() + + def _get_build_requires_editable(self) -> Iterable[str]: with self.req.build_env: runner = runner_with_spinner_message( - "Getting requirements to build wheel" + "Getting requirements to build editable" ) backend = self.req.pep517_backend assert backend is not None with backend.subprocess_runner(runner): - reqs = backend.get_requires_for_build_wheel() + return backend.get_requires_for_build_editable() - conflicting, missing = self.req.build_env.check_requirements(reqs) + def _install_build_reqs(self, finder: PackageFinder) -> None: + # Install any extra build dependencies that the backend requests. + # This must be done in a second pass, as the pyproject.toml + # dependencies must be installed before we can call the backend. + if ( + self.req.editable + and self.req.permit_editable_wheels + and self.req.supports_pyproject_editable() + ): + build_reqs = self._get_build_requires_editable() + else: + build_reqs = self._get_build_requires_wheel() + conflicting, missing = self.req.build_env.check_requirements(build_reqs) if conflicting: - _raise_conflicts("the backend dependencies", conflicting) + self._raise_conflicts("the backend dependencies", conflicting) self.req.build_env.install_requirements( - finder, missing, 'normal', - "Installing backend dependencies" + finder, missing, "normal", "Installing backend dependencies" + ) + + def _raise_conflicts( + self, conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]] + ) -> None: + format_string = ( + "Some build dependencies for {requirement} " + "conflict with {conflicting_with}: {description}." + ) + error_message = format_string.format( + requirement=self.req, + conflicting_with=conflicting_with, + description=", ".join( + f"{installed} is incompatible with {wanted}" + for installed, wanted in sorted(conflicting_reqs) + ), ) + raise InstallationError(error_message) diff --git a/venv/Lib/site-packages/pip/_internal/distributions/wheel.py b/venv/Lib/site-packages/pip/_internal/distributions/wheel.py index bf3482b151f08196c82e719a9c194dfc20e4182e..340b0f3c5c75f4ae0865c138dd7e26eae2c3c248 100644 --- a/venv/Lib/site-packages/pip/_internal/distributions/wheel.py +++ b/venv/Lib/site-packages/pip/_internal/distributions/wheel.py @@ -1,12 +1,12 @@ -from zipfile import ZipFile +from pip._vendor.packaging.utils import canonicalize_name from pip._internal.distributions.base import AbstractDistribution -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel - -if MYPY_CHECK_RUNNING: - from pip._vendor.pkg_resources import Distribution - from pip._internal.index.package_finder import PackageFinder +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import ( + BaseDistribution, + FilesystemWheel, + get_wheel_distribution, +) class WheelDistribution(AbstractDistribution): @@ -15,22 +15,17 @@ class WheelDistribution(AbstractDistribution): This does not need any preparation as wheels can be directly unpacked. """ - def get_pkg_resources_distribution(self): - # type: () -> Distribution + def get_metadata_distribution(self) -> BaseDistribution: """Loads the metadata from the wheel file into memory and returns a Distribution that uses it, not relying on the wheel file or requirement. """ - # Set as part of preparation during download. - assert self.req.local_file_path - # Wheels are never unnamed. - assert self.req.name - - with ZipFile(self.req.local_file_path, allowZip64=True) as z: - return pkg_resources_distribution_for_wheel( - z, self.req.name, self.req.local_file_path - ) - - def prepare_distribution_metadata(self, finder, build_isolation): - # type: (PackageFinder, bool) -> None + assert self.req.local_file_path, "Set as part of preparation during download" + assert self.req.name, "Wheels are never unnamed" + wheel = FilesystemWheel(self.req.local_file_path) + return get_wheel_distribution(wheel, canonicalize_name(self.req.name)) + + def prepare_distribution_metadata( + self, finder: PackageFinder, build_isolation: bool + ) -> None: pass diff --git a/venv/Lib/site-packages/pip/_internal/exceptions.py b/venv/Lib/site-packages/pip/_internal/exceptions.py index 8ac85485e1734bc48f7cf5b4e981ea27b4311be6..ef5bc75118bb94175e10ee88d12644e772ec8e34 100644 --- a/venv/Lib/site-packages/pip/_internal/exceptions.py +++ b/venv/Lib/site-packages/pip/_internal/exceptions.py @@ -1,19 +1,16 @@ """Exceptions used throughout package""" -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - +import configparser from itertools import chain, groupby, repeat +from typing import TYPE_CHECKING, Dict, List, Optional, Union -from pip._vendor.six import iteritems +from pip._vendor.pkg_resources import Distribution +from pip._vendor.requests.models import Request, Response -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +if TYPE_CHECKING: + from hashlib import _Hash -if MYPY_CHECK_RUNNING: - from typing import Optional - from pip._vendor.pkg_resources import Distribution + from pip._internal.metadata import BaseDistribution from pip._internal.req.req_install import InstallRequirement @@ -42,8 +39,11 @@ class NoneMetadataError(PipError): "PKG-INFO"). """ - def __init__(self, dist, metadata_name): - # type: (Distribution, str) -> None + def __init__( + self, + dist: Union[Distribution, "BaseDistribution"], + metadata_name: str, + ) -> None: """ :param dist: A Distribution object. :param metadata_name: The name of the metadata being accessed @@ -52,17 +52,28 @@ class NoneMetadataError(PipError): self.dist = dist self.metadata_name = metadata_name - def __str__(self): - # type: () -> str + def __str__(self) -> str: # Use `dist` in the error message because its stringification # includes more information, like the version and location. - return ( - 'None {} metadata found for distribution: {}'.format( - self.metadata_name, self.dist, - ) + return "None {} metadata found for distribution: {}".format( + self.metadata_name, + self.dist, ) +class UserInstallationInvalid(InstallationError): + """A --user install is requested on an environment without user site.""" + + def __str__(self) -> str: + return "User base directory is not specified" + + +class InvalidSchemeCombination(InstallationError): + def __str__(self) -> str: + before = ", ".join(str(a) for a in self.args[:-1]) + return f"Cannot set {before} and {self.args[-1]} together" + + class DistributionNotFound(InstallationError): """Raised when a distribution cannot be found to satisfy a requirement""" @@ -88,6 +99,31 @@ class PreviousBuildDirError(PipError): """Raised when there's a previous conflicting build directory""" +class NetworkConnectionError(PipError): + """HTTP connection error""" + + def __init__( + self, error_msg: str, response: Response = None, request: Request = None + ) -> None: + """ + Initialize NetworkConnectionError with `request` and `response` + objects. + """ + self.response = response + self.request = request + self.error_msg = error_msg + if ( + self.response is not None + and not self.request + and hasattr(response, "request") + ): + self.request = self.response.request + super().__init__(error_msg, response, request) + + def __str__(self) -> str: + return str(self.error_msg) + + class InvalidWheelFilename(InstallationError): """Invalid wheel filename.""" @@ -96,30 +132,66 @@ class UnsupportedWheel(InstallationError): """Unsupported wheel.""" +class MetadataInconsistent(InstallationError): + """Built metadata contains inconsistent information. + + This is raised when the metadata contains values (e.g. name and version) + that do not match the information previously obtained from sdist filename + or user-supplied ``#egg=`` value. + """ + + def __init__( + self, ireq: "InstallRequirement", field: str, f_val: str, m_val: str + ) -> None: + self.ireq = ireq + self.field = field + self.f_val = f_val + self.m_val = m_val + + def __str__(self) -> str: + template = ( + "Requested {} has inconsistent {}: " + "filename has {!r}, but metadata has {!r}" + ) + return template.format(self.ireq, self.field, self.f_val, self.m_val) + + +class InstallationSubprocessError(InstallationError): + """A subprocess call failed during installation.""" + + def __init__(self, returncode: int, description: str) -> None: + self.returncode = returncode + self.description = description + + def __str__(self) -> str: + return ( + "Command errored out with exit status {}: {} " + "Check the logs for full command output." + ).format(self.returncode, self.description) + + class HashErrors(InstallationError): """Multiple HashError instances rolled into one for reporting""" - def __init__(self): - self.errors = [] + def __init__(self) -> None: + self.errors: List["HashError"] = [] - def append(self, error): + def append(self, error: "HashError") -> None: self.errors.append(error) - def __str__(self): + def __str__(self) -> str: lines = [] self.errors.sort(key=lambda e: e.order) for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): lines.append(cls.head) lines.extend(e.body() for e in errors_of_cls) if lines: - return '\n'.join(lines) + return "\n".join(lines) + return "" - def __nonzero__(self): + def __bool__(self) -> bool: return bool(self.errors) - def __bool__(self): - return self.__nonzero__() - class HashError(InstallationError): """ @@ -137,10 +209,12 @@ class HashError(InstallationError): typically available earlier. """ - req = None # type: Optional[InstallRequirement] - head = '' - def body(self): + req: Optional["InstallRequirement"] = None + head = "" + order: int = -1 + + def body(self) -> str: """Return a summary of me for display under the heading. This default implementation simply prints a description of the @@ -150,19 +224,19 @@ class HashError(InstallationError): its link already populated by the resolver's _populate_link(). """ - return ' {}'.format(self._requirement_name()) + return f" {self._requirement_name()}" - def __str__(self): - return '{}\n{}'.format(self.head, self.body()) + def __str__(self) -> str: + return f"{self.head}\n{self.body()}" - def _requirement_name(self): + def _requirement_name(self) -> str: """Return a description of the requirement that triggered me. This default implementation returns long description of the req, with line numbers """ - return str(self.req) if self.req else 'unknown package' + return str(self.req) if self.req else "unknown package" class VcsHashUnsupported(HashError): @@ -170,8 +244,10 @@ class VcsHashUnsupported(HashError): we don't have a method for hashing those.""" order = 0 - head = ("Can't verify hashes for these requirements because we don't " - "have a way to hash version control repositories:") + head = ( + "Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:" + ) class DirectoryUrlHashUnsupported(HashError): @@ -179,30 +255,34 @@ class DirectoryUrlHashUnsupported(HashError): we don't have a method for hashing those.""" order = 1 - head = ("Can't verify hashes for these file:// requirements because they " - "point to directories:") + head = ( + "Can't verify hashes for these file:// requirements because they " + "point to directories:" + ) class HashMissing(HashError): """A hash was needed for a requirement but is absent.""" order = 2 - head = ('Hashes are required in --require-hashes mode, but they are ' - 'missing from some requirements. Here is a list of those ' - 'requirements along with the hashes their downloaded archives ' - 'actually had. Add lines like these to your requirements files to ' - 'prevent tampering. (If you did not enable --require-hashes ' - 'manually, note that it turns on automatically when any package ' - 'has a hash.)') - - def __init__(self, gotten_hash): + head = ( + "Hashes are required in --require-hashes mode, but they are " + "missing from some requirements. Here is a list of those " + "requirements along with the hashes their downloaded archives " + "actually had. Add lines like these to your requirements files to " + "prevent tampering. (If you did not enable --require-hashes " + "manually, note that it turns on automatically when any package " + "has a hash.)" + ) + + def __init__(self, gotten_hash: str) -> None: """ :param gotten_hash: The hash of the (possibly malicious) archive we just downloaded """ self.gotten_hash = gotten_hash - def body(self): + def body(self) -> str: # Dodge circular import. from pip._internal.utils.hashes import FAVORITE_HASH @@ -211,13 +291,16 @@ class HashMissing(HashError): # In the case of URL-based requirements, display the original URL # seen in the requirements file rather than the package name, # so the output can be directly copied into the requirements file. - package = (self.req.original_link if self.req.original_link - # In case someone feeds something downright stupid - # to InstallRequirement's constructor. - else getattr(self.req, 'req', None)) - return ' {} --hash={}:{}'.format(package or 'unknown package', - FAVORITE_HASH, - self.gotten_hash) + package = ( + self.req.original_link + if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, "req", None) + ) + return " {} --hash={}:{}".format( + package or "unknown package", FAVORITE_HASH, self.gotten_hash + ) class HashUnpinned(HashError): @@ -225,8 +308,10 @@ class HashUnpinned(HashError): version.""" order = 3 - head = ('In --require-hashes mode, all requirements must have their ' - 'versions pinned with ==. These do not:') + head = ( + "In --require-hashes mode, all requirements must have their " + "versions pinned with ==. These do not:" + ) class HashMismatch(HashError): @@ -238,13 +323,16 @@ class HashMismatch(HashError): improve its error message. """ - order = 4 - head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' - 'FILE. If you have updated the package versions, please update ' - 'the hashes. Otherwise, examine the package contents carefully; ' - 'someone may have tampered with them.') - def __init__(self, allowed, gots): + order = 4 + head = ( + "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS " + "FILE. If you have updated the package versions, please update " + "the hashes. Otherwise, examine the package contents carefully; " + "someone may have tampered with them." + ) + + def __init__(self, allowed: Dict[str, List[str]], gots: Dict[str, "_Hash"]) -> None: """ :param allowed: A dict of algorithm names pointing to lists of allowed hex digests @@ -254,11 +342,10 @@ class HashMismatch(HashError): self.allowed = allowed self.gots = gots - def body(self): - return ' {}:\n{}'.format(self._requirement_name(), - self._hash_comparison()) + def body(self) -> str: + return " {}:\n{}".format(self._requirement_name(), self._hash_comparison()) - def _hash_comparison(self): + def _hash_comparison(self) -> str: """ Return a comparison of actual and expected hash values. @@ -269,19 +356,22 @@ class HashMismatch(HashError): Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef """ - def hash_then_or(hash_name): + + def hash_then_or(hash_name: str) -> "chain[str]": # For now, all the decent hashes have 6-char names, so we can get # away with hard-coding space literals. - return chain([hash_name], repeat(' or')) + return chain([hash_name], repeat(" or")) - lines = [] - for hash_name, expecteds in iteritems(self.allowed): + lines: List[str] = [] + for hash_name, expecteds in self.allowed.items(): prefix = hash_then_or(hash_name) - lines.extend((' Expected {} {}'.format(next(prefix), e)) - for e in expecteds) - lines.append(' Got {}\n'.format( - self.gots[hash_name].hexdigest())) - return '\n'.join(lines) + lines.extend( + (" Expected {} {}".format(next(prefix), e)) for e in expecteds + ) + lines.append( + " Got {}\n".format(self.gots[hash_name].hexdigest()) + ) + return "\n".join(lines) class UnsupportedPythonVersion(InstallationError): @@ -290,19 +380,23 @@ class UnsupportedPythonVersion(InstallationError): class ConfigurationFileCouldNotBeLoaded(ConfigurationError): - """When there are errors while loading a configuration file - """ - - def __init__(self, reason="could not be loaded", fname=None, error=None): - super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) + """When there are errors while loading a configuration file""" + + def __init__( + self, + reason: str = "could not be loaded", + fname: Optional[str] = None, + error: Optional[configparser.Error] = None, + ) -> None: + super().__init__(error) self.reason = reason self.fname = fname self.error = error - def __str__(self): + def __str__(self) -> str: if self.fname is not None: - message_part = " in {}.".format(self.fname) + message_part = f" in {self.fname}." else: assert self.error is not None - message_part = ".\n{}\n".format(self.error.message) - return "Configuration file {}{}".format(self.reason, message_part) + message_part = f".\n{self.error}\n" + return f"Configuration file {self.reason}{message_part}" diff --git a/venv/Lib/site-packages/pip/_internal/index/collector.py b/venv/Lib/site-packages/pip/_internal/index/collector.py index e2c800c2cde7db18a54b54c13836e81883ed9484..d9412234eed10514724606b4b2a345aec1cf8c76 100644 --- a/venv/Lib/site-packages/pip/_internal/index/collector.py +++ b/venv/Lib/site-packages/pip/_internal/index/collector.py @@ -1,102 +1,69 @@ """ -The main purpose of this module is to expose LinkCollector.collect_links(). +The main purpose of this module is to expose LinkCollector.collect_sources(). """ import cgi +import collections import functools import itertools import logging -import mimetypes import os import re -from collections import OrderedDict +import urllib.parse +import urllib.request +import xml.etree.ElementTree +from optparse import Values +from typing import ( + Callable, + Iterable, + List, + MutableMapping, + NamedTuple, + Optional, + Sequence, + Union, +) from pip._vendor import html5lib, requests -from pip._vendor.distlib.compat import unescape -from pip._vendor.requests.exceptions import HTTPError, RetryError, SSLError -from pip._vendor.six.moves.urllib import parse as urllib_parse -from pip._vendor.six.moves.urllib import request as urllib_request +from pip._vendor.requests import Response +from pip._vendor.requests.exceptions import RetryError, SSLError +from pip._internal.exceptions import NetworkConnectionError from pip._internal.models.link import Link -from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.filetypes import is_archive_file from pip._internal.utils.misc import pairwise, redact_auth_from_url -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.utils.urls import path_to_url, url_to_path -from pip._internal.vcs import is_url, vcs - -if MYPY_CHECK_RUNNING: - from typing import ( - Callable, Iterable, List, MutableMapping, Optional, - Protocol, Sequence, Tuple, TypeVar, Union, - ) - import xml.etree.ElementTree - - from pip._vendor.requests import Response - - from pip._internal.models.search_scope import SearchScope - from pip._internal.network.session import PipSession - - HTMLElement = xml.etree.ElementTree.Element - ResponseHeaders = MutableMapping[str, str] - - # Used in the @lru_cache polyfill. - F = TypeVar('F') - - class LruCache(Protocol): - def __call__(self, maxsize=None): - # type: (Optional[int]) -> Callable[[F], F] - raise NotImplementedError +from pip._internal.vcs import vcs +from .sources import CandidatesFromPage, LinkSource, build_source logger = logging.getLogger(__name__) - -# Fallback to noop_lru_cache in Python 2 -# TODO: this can be removed when python 2 support is dropped! -def noop_lru_cache(maxsize=None): - # type: (Optional[int]) -> Callable[[F], F] - def _wrapper(f): - # type: (F) -> F - return f - return _wrapper +HTMLElement = xml.etree.ElementTree.Element +ResponseHeaders = MutableMapping[str, str] -_lru_cache = getattr(functools, "lru_cache", noop_lru_cache) # type: LruCache - - -def _match_vcs_scheme(url): - # type: (str) -> Optional[str] +def _match_vcs_scheme(url: str) -> Optional[str]: """Look for VCS schemes in the URL. Returns the matched VCS scheme, or None if there's no match. """ for scheme in vcs.schemes: - if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + if url.lower().startswith(scheme) and url[len(scheme)] in "+:": return scheme return None -def _is_url_like_archive(url): - # type: (str) -> bool - """Return whether the URL looks like an archive. - """ - filename = Link(url).filename - for bad_ext in ARCHIVE_EXTENSIONS: - if filename.endswith(bad_ext): - return True - return False - - class _NotHTML(Exception): - def __init__(self, content_type, request_desc): - # type: (str, str) -> None - super(_NotHTML, self).__init__(content_type, request_desc) + def __init__(self, content_type: str, request_desc: str) -> None: + super().__init__(content_type, request_desc) self.content_type = content_type self.request_desc = request_desc -def _ensure_html_header(response): - # type: (Response) -> None +def _ensure_html_header(response: Response) -> None: """Check the Content-Type header to ensure the response contains HTML. Raises `_NotHTML` if the content type is not text/html. @@ -110,25 +77,23 @@ class _NotHTTP(Exception): pass -def _ensure_html_response(url, session): - # type: (str, PipSession) -> None +def _ensure_html_response(url: str, session: PipSession) -> None: """Send a HEAD request to the URL, and ensure the response contains HTML. Raises `_NotHTTP` if the URL is not available for a HEAD request, or `_NotHTML` if the content type is not text/html. """ - scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) - if scheme not in {'http', 'https'}: + scheme, netloc, path, query, fragment = urllib.parse.urlsplit(url) + if scheme not in {"http", "https"}: raise _NotHTTP() resp = session.head(url, allow_redirects=True) - resp.raise_for_status() + raise_for_status(resp) _ensure_html_header(resp) -def _get_html_response(url, session): - # type: (str, PipSession) -> Response +def _get_html_response(url: str, session: PipSession) -> Response: """Access an HTML page with GET, and return the response. This consists of three parts: @@ -141,10 +106,10 @@ def _get_html_response(url, session): 3. Check the Content-Type header to make sure we got HTML, and raise `_NotHTML` otherwise. """ - if _is_url_like_archive(url): + if is_archive_file(Link(url).filename): _ensure_html_response(url, session=session) - logger.debug('Getting page %s', redact_auth_from_url(url)) + logger.debug("Getting page %s", redact_auth_from_url(url)) resp = session.get( url, @@ -166,7 +131,7 @@ def _get_html_response(url, session): "Cache-Control": "max-age=0", }, ) - resp.raise_for_status() + raise_for_status(resp) # The check for archives above only works if the url ends with # something that looks like an archive. However that is not a @@ -178,19 +143,16 @@ def _get_html_response(url, session): return resp -def _get_encoding_from_headers(headers): - # type: (ResponseHeaders) -> Optional[str] - """Determine if we have any encoding information in our headers. - """ +def _get_encoding_from_headers(headers: ResponseHeaders) -> Optional[str]: + """Determine if we have any encoding information in our headers.""" if headers and "Content-Type" in headers: content_type, params = cgi.parse_header(headers["Content-Type"]) if "charset" in params: - return params['charset'] + return params["charset"] return None -def _determine_base_url(document, page_url): - # type: (HTMLElement, str) -> str +def _determine_base_url(document: HTMLElement, page_url: str) -> str: """Determine the HTML document's base URL. This looks for a ```` tag in the HTML document. If present, its href @@ -209,17 +171,15 @@ def _determine_base_url(document, page_url): return page_url -def _clean_url_path_part(part): - # type: (str) -> str +def _clean_url_path_part(part: str) -> str: """ Clean a "part" of a URL path (i.e. after splitting on "@" characters). """ # We unquote prior to quoting to make sure nothing is double quoted. - return urllib_parse.quote(urllib_parse.unquote(part)) + return urllib.parse.quote(urllib.parse.unquote(part)) -def _clean_file_url_path(part): - # type: (str) -> str +def _clean_file_url_path(part: str) -> str: """ Clean the first part of a URL path that corresponds to a local filesystem path (i.e. the first part after splitting on "@" characters). @@ -229,15 +189,14 @@ def _clean_file_url_path(part): # should not be quoted. On Linux where drive letters do not # exist, the colon should be quoted. We rely on urllib.request # to do the right thing here. - return urllib_request.pathname2url(urllib_request.url2pathname(part)) + return urllib.request.pathname2url(urllib.request.url2pathname(part)) # percent-encoded: / -_reserved_chars_re = re.compile('(@|%2F)', re.IGNORECASE) +_reserved_chars_re = re.compile("(@|%2F)", re.IGNORECASE) -def _clean_url_path(path, is_local_path): - # type: (str, bool) -> str +def _clean_url_path(path: str, is_local_path: bool) -> str: """ Clean the path portion of a URL. """ @@ -251,16 +210,15 @@ def _clean_url_path(path, is_local_path): parts = _reserved_chars_re.split(path) cleaned_parts = [] - for to_clean, reserved in pairwise(itertools.chain(parts, [''])): + for to_clean, reserved in pairwise(itertools.chain(parts, [""])): cleaned_parts.append(clean_func(to_clean)) # Normalize %xx escapes (e.g. %2f -> %2F) cleaned_parts.append(reserved.upper()) - return ''.join(cleaned_parts) + return "".join(cleaned_parts) -def _clean_link(url): - # type: (str) -> str +def _clean_link(url: str) -> str: """ Make sure a link is fully quoted. For example, if ' ' occurs in the URL, it will be replaced with "%20", @@ -268,19 +226,18 @@ def _clean_link(url): """ # Split the URL into parts according to the general structure # `scheme://netloc/path;parameters?query#fragment`. - result = urllib_parse.urlparse(url) + result = urllib.parse.urlparse(url) # If the netloc is empty, then the URL refers to a local filesystem path. is_local_path = not result.netloc path = _clean_url_path(result.path, is_local_path=is_local_path) - return urllib_parse.urlunparse(result._replace(path=path)) + return urllib.parse.urlunparse(result._replace(path=path)) def _create_link_from_element( - anchor, # type: HTMLElement - page_url, # type: str - base_url, # type: str -): - # type: (...) -> Optional[Link] + anchor: HTMLElement, + page_url: str, + base_url: str, +) -> Optional[Link]: """ Convert an anchor element in a simple repository page to a Link. """ @@ -288,14 +245,9 @@ def _create_link_from_element( if not href: return None - url = _clean_link(urllib_parse.urljoin(base_url, href)) - pyrequire = anchor.get('data-requires-python') - pyrequire = unescape(pyrequire) if pyrequire else None - - yanked_reason = anchor.get('data-yanked') - if yanked_reason: - # This is a unicode string in Python 2 (and 3). - yanked_reason = unescape(yanked_reason) + url = _clean_link(urllib.parse.urljoin(base_url, href)) + pyrequire = anchor.get("data-requires-python") + yanked_reason = anchor.get("data-yanked") link = Link( url, @@ -307,40 +259,33 @@ def _create_link_from_element( return link -class CacheablePageContent(object): - def __init__(self, page): - # type: (HTMLPage) -> None +class CacheablePageContent: + def __init__(self, page: "HTMLPage") -> None: assert page.cache_link_parsing self.page = page - def __eq__(self, other): - # type: (object) -> bool - return (isinstance(other, type(self)) and - self.page.url == other.page.url) + def __eq__(self, other: object) -> bool: + return isinstance(other, type(self)) and self.page.url == other.page.url - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash(self.page.url) def with_cached_html_pages( - fn, # type: Callable[[HTMLPage], Iterable[Link]] -): - # type: (...) -> Callable[[HTMLPage], List[Link]] + fn: Callable[["HTMLPage"], Iterable[Link]], +) -> Callable[["HTMLPage"], List[Link]]: """ Given a function that parses an Iterable[Link] from an HTMLPage, cache the function's result (keyed by CacheablePageContent), unless the HTMLPage `page` has `page.cache_link_parsing == False`. """ - @_lru_cache(maxsize=None) - def wrapper(cacheable_page): - # type: (CacheablePageContent) -> List[Link] + @functools.lru_cache(maxsize=None) + def wrapper(cacheable_page: CacheablePageContent) -> List[Link]: return list(fn(cacheable_page.page)) @functools.wraps(fn) - def wrapper_wrapper(page): - # type: (HTMLPage) -> List[Link] + def wrapper_wrapper(page: "HTMLPage") -> List[Link]: if page.cache_link_parsing: return wrapper(CacheablePageContent(page)) return list(fn(page)) @@ -349,8 +294,7 @@ def with_cached_html_pages( @with_cached_html_pages -def parse_links(page): - # type: (HTMLPage) -> Iterable[Link] +def parse_links(page: "HTMLPage") -> Iterable[Link]: """ Parse an HTML document, and yield its anchor elements as Link objects. """ @@ -373,17 +317,16 @@ def parse_links(page): yield link -class HTMLPage(object): +class HTMLPage: """Represents one page, along with its URL""" def __init__( self, - content, # type: bytes - encoding, # type: Optional[str] - url, # type: str - cache_link_parsing=True, # type: bool - ): - # type: (...) -> None + content: bytes, + encoding: Optional[str], + url: str, + cache_link_parsing: bool = True, + ) -> None: """ :param encoding: the encoding to decode the given content. :param url: the URL from which the HTML was downloaded. @@ -396,70 +339,77 @@ class HTMLPage(object): self.url = url self.cache_link_parsing = cache_link_parsing - def __str__(self): - # type: () -> str + def __str__(self) -> str: return redact_auth_from_url(self.url) def _handle_get_page_fail( - link, # type: Link - reason, # type: Union[str, Exception] - meth=None # type: Optional[Callable[..., None]] -): - # type: (...) -> None + link: Link, + reason: Union[str, Exception], + meth: Optional[Callable[..., None]] = None, +) -> None: if meth is None: meth = logger.debug meth("Could not fetch URL %s: %s - skipping", link, reason) -def _make_html_page(response, cache_link_parsing=True): - # type: (Response, bool) -> HTMLPage +def _make_html_page(response: Response, cache_link_parsing: bool = True) -> HTMLPage: encoding = _get_encoding_from_headers(response.headers) return HTMLPage( response.content, encoding=encoding, url=response.url, - cache_link_parsing=cache_link_parsing) + cache_link_parsing=cache_link_parsing, + ) -def _get_html_page(link, session=None): - # type: (Link, Optional[PipSession]) -> Optional[HTMLPage] +def _get_html_page( + link: Link, session: Optional[PipSession] = None +) -> Optional["HTMLPage"]: if session is None: raise TypeError( "_get_html_page() missing 1 required keyword argument: 'session'" ) - url = link.url.split('#', 1)[0] + url = link.url.split("#", 1)[0] # Check for VCS schemes that do not support lookup as web pages. vcs_scheme = _match_vcs_scheme(url) if vcs_scheme: - logger.debug('Cannot look at %s URL %s', vcs_scheme, link) + logger.warning( + "Cannot look at %s URL %s because it does not support lookup as web pages.", + vcs_scheme, + link, + ) return None # Tack index.html onto file:// URLs that point to directories - scheme, _, path, _, _, _ = urllib_parse.urlparse(url) - if (scheme == 'file' and os.path.isdir(urllib_request.url2pathname(path))): + scheme, _, path, _, _, _ = urllib.parse.urlparse(url) + if scheme == "file" and os.path.isdir(urllib.request.url2pathname(path)): # add trailing slash if not present so urljoin doesn't trim # final segment - if not url.endswith('/'): - url += '/' - url = urllib_parse.urljoin(url, 'index.html') - logger.debug(' file: URL is directory, getting %s', url) + if not url.endswith("/"): + url += "/" + url = urllib.parse.urljoin(url, "index.html") + logger.debug(" file: URL is directory, getting %s", url) try: resp = _get_html_response(url, session=session) except _NotHTTP: - logger.debug( - 'Skipping page %s because it looks like an archive, and cannot ' - 'be checked by HEAD.', link, + logger.warning( + "Skipping page %s because it looks like an archive, and cannot " + "be checked by a HTTP HEAD request.", + link, ) except _NotHTML as exc: - logger.debug( - 'Skipping page %s because the %s request got Content-Type: %s', - link, exc.request_desc, exc.content_type, + logger.warning( + "Skipping page %s because the %s request got Content-Type: %s." + "The only supported Content-Type is text/html", + link, + exc.request_desc, + exc.content_type, ) - except HTTPError as exc: + except NetworkConnectionError as exc: _handle_get_page_fail(link, exc) except RetryError as exc: _handle_get_page_fail(link, exc) @@ -468,194 +418,119 @@ def _get_html_page(link, session=None): reason += str(exc) _handle_get_page_fail(link, reason, meth=logger.info) except requests.ConnectionError as exc: - _handle_get_page_fail(link, "connection error: {}".format(exc)) + _handle_get_page_fail(link, f"connection error: {exc}") except requests.Timeout: _handle_get_page_fail(link, "timed out") else: - return _make_html_page(resp, - cache_link_parsing=link.cache_link_parsing) + return _make_html_page(resp, cache_link_parsing=link.cache_link_parsing) return None -def _remove_duplicate_links(links): - # type: (Iterable[Link]) -> List[Link] - """ - Return a list of links, with duplicates removed and ordering preserved. - """ - # We preserve the ordering when removing duplicates because we can. - return list(OrderedDict.fromkeys(links)) - - -def group_locations(locations, expand_dir=False): - # type: (Sequence[str], bool) -> Tuple[List[str], List[str]] - """ - Divide a list of locations into two groups: "files" (archives) and "urls." - - :return: A pair of lists (files, urls). - """ - files = [] - urls = [] - - # puts the url for the given file path into the appropriate list - def sort_path(path): - # type: (str) -> None - url = path_to_url(path) - if mimetypes.guess_type(url, strict=False)[0] == 'text/html': - urls.append(url) - else: - files.append(url) - - for url in locations: - - is_local_path = os.path.exists(url) - is_file_url = url.startswith('file:') - - if is_local_path or is_file_url: - if is_local_path: - path = url - else: - path = url_to_path(url) - if os.path.isdir(path): - if expand_dir: - path = os.path.realpath(path) - for item in os.listdir(path): - sort_path(os.path.join(path, item)) - elif is_file_url: - urls.append(url) - else: - logger.warning( - "Path '{0}' is ignored: " - "it is a directory.".format(path), - ) - elif os.path.isfile(path): - sort_path(path) - else: - logger.warning( - "Url '%s' is ignored: it is neither a file " - "nor a directory.", url, - ) - elif is_url(url): - # Only add url with clear scheme - urls.append(url) - else: - logger.warning( - "Url '%s' is ignored. It is either a non-existing " - "path or lacks a specific scheme.", url, - ) - - return files, urls - +class CollectedSources(NamedTuple): + find_links: Sequence[Optional[LinkSource]] + index_urls: Sequence[Optional[LinkSource]] -class CollectedLinks(object): - """ - Encapsulates the return value of a call to LinkCollector.collect_links(). - - The return value includes both URLs to project pages containing package - links, as well as individual package Link objects collected from other - sources. - - This info is stored separately as: - - (1) links from the configured file locations, - (2) links from the configured find_links, and - (3) urls to HTML project pages, as described by the PEP 503 simple - repository API. - """ - - def __init__( - self, - files, # type: List[Link] - find_links, # type: List[Link] - project_urls, # type: List[Link] - ): - # type: (...) -> None - """ - :param files: Links from file locations. - :param find_links: Links from find_links. - :param project_urls: URLs to HTML project pages, as described by - the PEP 503 simple repository API. - """ - self.files = files - self.find_links = find_links - self.project_urls = project_urls - - -class LinkCollector(object): +class LinkCollector: """ Responsible for collecting Link objects from all configured locations, making network requests as needed. - The class's main method is its collect_links() method. + The class's main method is its collect_sources() method. """ def __init__( self, - session, # type: PipSession - search_scope, # type: SearchScope - ): - # type: (...) -> None + session: PipSession, + search_scope: SearchScope, + ) -> None: self.search_scope = search_scope self.session = session + @classmethod + def create( + cls, + session: PipSession, + options: Values, + suppress_no_index: bool = False, + ) -> "LinkCollector": + """ + :param session: The Session to use to make requests. + :param suppress_no_index: Whether to ignore the --no-index option + when constructing the SearchScope object. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index and not suppress_no_index: + logger.debug( + "Ignoring indexes: %s", + ",".join(redact_auth_from_url(url) for url in index_urls), + ) + index_urls = [] + + # Make sure find_links is a list before passing to create(). + find_links = options.find_links or [] + + search_scope = SearchScope.create( + find_links=find_links, + index_urls=index_urls, + ) + link_collector = LinkCollector( + session=session, + search_scope=search_scope, + ) + return link_collector + @property - def find_links(self): - # type: () -> List[str] + def find_links(self) -> List[str]: return self.search_scope.find_links - def fetch_page(self, location): - # type: (Link) -> Optional[HTMLPage] + def fetch_page(self, location: Link) -> Optional[HTMLPage]: """ Fetch an HTML page containing package links. """ return _get_html_page(location, session=self.session) - def collect_links(self, project_name): - # type: (str) -> CollectedLinks - """Find all available links for the given project name. - - :return: All the Link objects (unfiltered), as a CollectedLinks object. - """ - search_scope = self.search_scope - index_locations = search_scope.get_index_urls_locations(project_name) - index_file_loc, index_url_loc = group_locations(index_locations) - fl_file_loc, fl_url_loc = group_locations( - self.find_links, expand_dir=True, - ) - - file_links = [ - Link(url) for url in itertools.chain(index_file_loc, fl_file_loc) - ] - - # We trust every directly linked archive in find_links - find_link_links = [Link(url, '-f') for url in self.find_links] - - # We trust every url that the user has given us whether it was given - # via --index-url or --find-links. - # We want to filter out anything that does not have a secure origin. - url_locations = [ - link for link in itertools.chain( - # Mark PyPI indices as "cache_link_parsing == False" -- this - # will avoid caching the result of parsing the page for links. - (Link(url, cache_link_parsing=False) for url in index_url_loc), - (Link(url) for url in fl_url_loc), + def collect_sources( + self, + project_name: str, + candidates_from_page: CandidatesFromPage, + ) -> CollectedSources: + # The OrderedDict calls deduplicate sources by URL. + index_url_sources = collections.OrderedDict( + build_source( + loc, + candidates_from_page=candidates_from_page, + page_validator=self.session.is_secure_origin, + expand_dir=False, + cache_link_parsing=False, + ) + for loc in self.search_scope.get_index_urls_locations(project_name) + ).values() + find_links_sources = collections.OrderedDict( + build_source( + loc, + candidates_from_page=candidates_from_page, + page_validator=self.session.is_secure_origin, + expand_dir=True, + cache_link_parsing=True, ) - if self.session.is_secure_origin(link) - ] - - url_locations = _remove_duplicate_links(url_locations) - lines = [ - '{} location(s) to search for versions of {}:'.format( - len(url_locations), project_name, - ), - ] - for link in url_locations: - lines.append('* {}'.format(link)) - logger.debug('\n'.join(lines)) - - return CollectedLinks( - files=file_links, - find_links=find_link_links, - project_urls=url_locations, + for loc in self.find_links + ).values() + + if logger.isEnabledFor(logging.DEBUG): + lines = [ + f"* {s.link}" + for s in itertools.chain(find_links_sources, index_url_sources) + if s is not None and s.link is not None + ] + lines = [ + f"{len(lines)} location(s) to search " + f"for versions of {project_name}:" + ] + lines + logger.debug("\n".join(lines)) + + return CollectedSources( + find_links=list(find_links_sources), + index_urls=list(index_url_sources), ) diff --git a/venv/Lib/site-packages/pip/_internal/index/package_finder.py b/venv/Lib/site-packages/pip/_internal/index/package_finder.py index e88ad9f5c695293ab59907f630953d9c7e4dd5fb..a2702db7d357a08a8c33e6abefa4ce62688b1cf4 100644 --- a/venv/Lib/site-packages/pip/_internal/index/package_finder.py +++ b/venv/Lib/site-packages/pip/_internal/index/package_finder.py @@ -3,13 +3,16 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False -from __future__ import absolute_import - +import functools +import itertools import logging import re +from typing import FrozenSet, Iterable, List, Optional, Set, Tuple, Union from pip._vendor.packaging import specifiers +from pip._vendor.packaging.tags import Tag from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import _BaseVersion from pip._vendor.packaging.version import parse as parse_version from pip._internal.exceptions import ( @@ -18,52 +21,38 @@ from pip._internal.exceptions import ( InvalidWheelFilename, UnsupportedWheel, ) -from pip._internal.index.collector import parse_links +from pip._internal.index.collector import LinkCollector, parse_links from pip._internal.models.candidate import InstallationCandidate from pip._internal.models.format_control import FormatControl from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.models.target_python import TargetPython from pip._internal.models.wheel import Wheel +from pip._internal.req import InstallRequirement +from pip._internal.utils._log import getLogger from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.hashes import Hashes from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import build_netloc from pip._internal.utils.packaging import check_requires_python -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS from pip._internal.utils.urls import url_to_path -if MYPY_CHECK_RUNNING: - from typing import ( - FrozenSet, Iterable, List, Optional, Set, Text, Tuple, Union, - ) - - from pip._vendor.packaging.tags import Tag - from pip._vendor.packaging.version import _BaseVersion +__all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"] - from pip._internal.index.collector import LinkCollector - from pip._internal.models.search_scope import SearchScope - from pip._internal.req import InstallRequirement - from pip._internal.utils.hashes import Hashes - - BuildTag = Union[Tuple[()], Tuple[int, str]] - CandidateSortingKey = ( - Tuple[int, int, int, _BaseVersion, BuildTag, Optional[int]] - ) +logger = getLogger(__name__) -__all__ = ['FormatControl', 'BestCandidateResult', 'PackageFinder'] - - -logger = logging.getLogger(__name__) +BuildTag = Union[Tuple[()], Tuple[int, str]] +CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag] def _check_link_requires_python( - link, # type: Link - version_info, # type: Tuple[int, int, int] - ignore_requires_python=False, # type: bool -): - # type: (...) -> bool + link: Link, + version_info: Tuple[int, int, int], + ignore_requires_python: bool = False, +) -> bool: """ Return whether the given Python version is compatible with a link's "Requires-Python" value. @@ -75,39 +64,44 @@ def _check_link_requires_python( """ try: is_compatible = check_requires_python( - link.requires_python, version_info=version_info, + link.requires_python, + version_info=version_info, ) except specifiers.InvalidSpecifier: logger.debug( "Ignoring invalid Requires-Python (%r) for link: %s", - link.requires_python, link, + link.requires_python, + link, ) else: if not is_compatible: - version = '.'.join(map(str, version_info)) + version = ".".join(map(str, version_info)) if not ignore_requires_python: - logger.debug( - 'Link requires a different Python (%s not in: %r): %s', - version, link.requires_python, link, + logger.verbose( + "Link requires a different Python (%s not in: %r): %s", + version, + link.requires_python, + link, ) return False logger.debug( - 'Ignoring failed Requires-Python check (%s not in: %r) ' - 'for link: %s', - version, link.requires_python, link, + "Ignoring failed Requires-Python check (%s not in: %r) for link: %s", + version, + link.requires_python, + link, ) return True -class LinkEvaluator(object): +class LinkEvaluator: """ Responsible for evaluating links for a particular project. """ - _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + _py_version_re = re.compile(r"-py([123]\.?[0-9]?)$") # Don't include an allow_yanked default value to make sure each call # site considers whether yanked releases are allowed. This also causes @@ -115,14 +109,13 @@ class LinkEvaluator(object): # people when reading the code. def __init__( self, - project_name, # type: str - canonical_name, # type: str - formats, # type: FrozenSet[str] - target_python, # type: TargetPython - allow_yanked, # type: bool - ignore_requires_python=None, # type: Optional[bool] - ): - # type: (...) -> None + project_name: str, + canonical_name: str, + formats: FrozenSet[str], + target_python: TargetPython, + allow_yanked: bool, + ignore_requires_python: Optional[bool] = None, + ) -> None: """ :param project_name: The user supplied package name. :param canonical_name: The canonical package name. @@ -151,8 +144,7 @@ class LinkEvaluator(object): self.project_name = project_name - def evaluate_link(self, link): - # type: (Link) -> Tuple[bool, Optional[Text]] + def evaluate_link(self, link: Link) -> Tuple[bool, Optional[str]]: """ Determine whether a link is a candidate for installation. @@ -163,11 +155,8 @@ class LinkEvaluator(object): """ version = None if link.is_yanked and not self._allow_yanked: - reason = link.yanked_reason or '' - # Mark this as a unicode string to prevent "UnicodeEncodeError: - # 'ascii' codec can't encode character" in Python 2 when - # the reason contains non-ascii characters. - return (False, u'yanked for reason: {}'.format(reason)) + reason = link.yanked_reason or "" + return (False, f"yanked for reason: {reason}") if link.egg_fragment: egg_info = link.egg_fragment @@ -175,23 +164,21 @@ class LinkEvaluator(object): else: egg_info, ext = link.splitext() if not ext: - return (False, 'not a file') + return (False, "not a file") if ext not in SUPPORTED_EXTENSIONS: - return (False, 'unsupported archive format: {}'.format(ext)) + return (False, f"unsupported archive format: {ext}") if "binary" not in self._formats and ext == WHEEL_EXTENSION: - reason = 'No binaries permitted for {}'.format( - self.project_name) + reason = "No binaries permitted for {}".format(self.project_name) return (False, reason) - if "macosx10" in link.path and ext == '.zip': - return (False, 'macosx10 one') + if "macosx10" in link.path and ext == ".zip": + return (False, "macosx10 one") if ext == WHEEL_EXTENSION: try: wheel = Wheel(link.filename) except InvalidWheelFilename: - return (False, 'invalid wheel filename') + return (False, "invalid wheel filename") if canonicalize_name(wheel.name) != self._canonical_name: - reason = 'wrong project name (not {})'.format( - self.project_name) + reason = "wrong project name (not {})".format(self.project_name) return (False, reason) supported_tags = self._target_python.get_tags() @@ -200,8 +187,9 @@ class LinkEvaluator(object): # simplify troubleshooting compatibility issues. file_tags = wheel.get_formatted_file_tags() reason = ( - "none of the wheel's tags match: {}".format( - ', '.join(file_tags) + "none of the wheel's tags ({}) are compatible " + "(run pip debug --verbose to show compatible tags)".format( + ", ".join(file_tags) ) ) return (False, reason) @@ -210,26 +198,28 @@ class LinkEvaluator(object): # This should be up by the self.ok_binary check, but see issue 2700. if "source" not in self._formats and ext != WHEEL_EXTENSION: - reason = 'No sources permitted for {}'.format(self.project_name) + reason = f"No sources permitted for {self.project_name}" return (False, reason) if not version: version = _extract_version_from_fragment( - egg_info, self._canonical_name, + egg_info, + self._canonical_name, ) if not version: - reason = 'Missing project version for {}'.format(self.project_name) + reason = f"Missing project version for {self.project_name}" return (False, reason) match = self._py_version_re.search(version) if match: - version = version[:match.start()] + version = version[: match.start()] py_version = match.group(1) if py_version != self._target_python.py_version: - return (False, 'Python version is incorrect') + return (False, "Python version is incorrect") supports_python = _check_link_requires_python( - link, version_info=self._target_python.py_version_info, + link, + version_info=self._target_python.py_version_info, ignore_requires_python=self._ignore_requires_python, ) if not supports_python: @@ -237,17 +227,16 @@ class LinkEvaluator(object): # _log_skipped_link(). return (False, None) - logger.debug('Found link %s, version: %s', link, version) + logger.debug("Found link %s, version: %s", link, version) return (True, version) def filter_unallowed_hashes( - candidates, # type: List[InstallationCandidate] - hashes, # type: Hashes - project_name, # type: str -): - # type: (...) -> List[InstallationCandidate] + candidates: List[InstallationCandidate], + hashes: Hashes, + project_name: str, +) -> List[InstallationCandidate]: """ Filter out candidates whose hashes aren't allowed, and return a new list of candidates. @@ -265,8 +254,8 @@ def filter_unallowed_hashes( """ if not hashes: logger.debug( - 'Given no hashes to check %s links for project %r: ' - 'discarding no candidates', + "Given no hashes to check %s links for project %r: " + "discarding no candidates", len(candidates), project_name, ) @@ -296,28 +285,28 @@ def filter_unallowed_hashes( filtered = list(candidates) if len(filtered) == len(candidates): - discard_message = 'discarding no candidates' + discard_message = "discarding no candidates" else: - discard_message = 'discarding {} non-matches:\n {}'.format( + discard_message = "discarding {} non-matches:\n {}".format( len(non_matches), - '\n '.join(str(candidate.link) for candidate in non_matches) + "\n ".join(str(candidate.link) for candidate in non_matches), ) logger.debug( - 'Checked %s links for project %r against %s hashes ' - '(%s matches, %s no digest): %s', + "Checked %s links for project %r against %s hashes " + "(%s matches, %s no digest): %s", len(candidates), project_name, hashes.digest_count, match_count, len(matches_or_no_digest) - match_count, - discard_message + discard_message, ) return filtered -class CandidatePreferences(object): +class CandidatePreferences: """ Encapsulates some of the preferences for filtering and sorting @@ -326,10 +315,9 @@ class CandidatePreferences(object): def __init__( self, - prefer_binary=False, # type: bool - allow_all_prereleases=False, # type: bool - ): - # type: (...) -> None + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + ) -> None: """ :param allow_all_prereleases: Whether to allow all pre-releases. """ @@ -337,7 +325,7 @@ class CandidatePreferences(object): self.prefer_binary = prefer_binary -class BestCandidateResult(object): +class BestCandidateResult: """A collection of candidates, returned by `PackageFinder.find_best_candidate`. This class is only intended to be instantiated by CandidateEvaluator's @@ -346,11 +334,10 @@ class BestCandidateResult(object): def __init__( self, - candidates, # type: List[InstallationCandidate] - applicable_candidates, # type: List[InstallationCandidate] - best_candidate, # type: Optional[InstallationCandidate] - ): - # type: (...) -> None + candidates: List[InstallationCandidate], + applicable_candidates: List[InstallationCandidate], + best_candidate: Optional[InstallationCandidate], + ) -> None: """ :param candidates: A sequence of all available candidates found. :param applicable_candidates: The applicable candidates. @@ -369,20 +356,16 @@ class BestCandidateResult(object): self.best_candidate = best_candidate - def iter_all(self): - # type: () -> Iterable[InstallationCandidate] - """Iterate through all candidates. - """ + def iter_all(self) -> Iterable[InstallationCandidate]: + """Iterate through all candidates.""" return iter(self._candidates) - def iter_applicable(self): - # type: () -> Iterable[InstallationCandidate] - """Iterate through the applicable candidates. - """ + def iter_applicable(self) -> Iterable[InstallationCandidate]: + """Iterate through the applicable candidates.""" return iter(self._applicable_candidates) -class CandidateEvaluator(object): +class CandidateEvaluator: """ Responsible for filtering and sorting candidates for installation based @@ -392,14 +375,13 @@ class CandidateEvaluator(object): @classmethod def create( cls, - project_name, # type: str - target_python=None, # type: Optional[TargetPython] - prefer_binary=False, # type: bool - allow_all_prereleases=False, # type: bool - specifier=None, # type: Optional[specifiers.BaseSpecifier] - hashes=None, # type: Optional[Hashes] - ): - # type: (...) -> CandidateEvaluator + project_name: str, + target_python: Optional[TargetPython] = None, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> "CandidateEvaluator": """Create a CandidateEvaluator object. :param target_python: The target Python interpreter to use when @@ -428,14 +410,13 @@ class CandidateEvaluator(object): def __init__( self, - project_name, # type: str - supported_tags, # type: List[Tag] - specifier, # type: specifiers.BaseSpecifier - prefer_binary=False, # type: bool - allow_all_prereleases=False, # type: bool - hashes=None, # type: Optional[Hashes] - ): - # type: (...) -> None + project_name: str, + supported_tags: List[Tag], + specifier: specifiers.BaseSpecifier, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + hashes: Optional[Hashes] = None, + ) -> None: """ :param supported_tags: The PEP 425 tags supported by the target Python in order of preference (most preferred first). @@ -446,12 +427,17 @@ class CandidateEvaluator(object): self._project_name = project_name self._specifier = specifier self._supported_tags = supported_tags + # Since the index of the tag in the _supported_tags list is used + # as a priority, precompute a map from tag to index/priority to be + # used in wheel.find_most_preferred_tag. + self._wheel_tag_preferences = { + tag: idx for idx, tag in enumerate(supported_tags) + } def get_applicable_candidates( self, - candidates, # type: List[InstallationCandidate] - ): - # type: (...) -> List[InstallationCandidate] + candidates: List[InstallationCandidate], + ) -> List[InstallationCandidate]: """ Return the applicable candidates from a list of candidates. """ @@ -459,7 +445,8 @@ class CandidateEvaluator(object): allow_prereleases = self._allow_all_prereleases or None specifier = self._specifier versions = { - str(v) for v in specifier.filter( + str(v) + for v in specifier.filter( # We turn the version object into a str here because otherwise # when we're debundled but setuptools isn't, Python will see # packaging.version.Version and @@ -473,9 +460,7 @@ class CandidateEvaluator(object): } # Again, converting version to str to deal with debundling. - applicable_candidates = [ - c for c in candidates if str(c.version) in versions - ] + applicable_candidates = [c for c in candidates if str(c.version) in versions] filtered_applicable_candidates = filter_unallowed_hashes( candidates=applicable_candidates, @@ -485,8 +470,7 @@ class CandidateEvaluator(object): return sorted(filtered_applicable_candidates, key=self._sort_key) - def _sort_key(self, candidate): - # type: (InstallationCandidate) -> CandidateSortingKey + def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey: """ Function to pass as the `key` argument to a call to sorted() to sort InstallationCandidates by preference. @@ -518,22 +502,27 @@ class CandidateEvaluator(object): """ valid_tags = self._supported_tags support_num = len(valid_tags) - build_tag = () # type: BuildTag + build_tag: BuildTag = () binary_preference = 0 link = candidate.link if link.is_wheel: # can raise InvalidWheelFilename wheel = Wheel(link.filename) - if not wheel.supported(valid_tags): + try: + pri = -( + wheel.find_most_preferred_tag( + valid_tags, self._wheel_tag_preferences + ) + ) + except ValueError: raise UnsupportedWheel( "{} is not a supported wheel for this platform. It " "can't be sorted.".format(wheel.filename) ) if self._prefer_binary: binary_preference = 1 - pri = -(wheel.support_index_min(valid_tags)) if wheel.build_tag is not None: - match = re.match(r'^(\d+)(.*)$', wheel.build_tag) + match = re.match(r"^(\d+)(.*)$", wheel.build_tag) build_tag_groups = match.groups() build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) else: # sdist @@ -541,45 +530,31 @@ class CandidateEvaluator(object): has_allowed_hash = int(link.is_hash_allowed(self._hashes)) yank_value = -1 * int(link.is_yanked) # -1 for yanked. return ( - has_allowed_hash, yank_value, binary_preference, candidate.version, - build_tag, pri, + has_allowed_hash, + yank_value, + binary_preference, + candidate.version, + pri, + build_tag, ) def sort_best_candidate( self, - candidates, # type: List[InstallationCandidate] - ): - # type: (...) -> Optional[InstallationCandidate] + candidates: List[InstallationCandidate], + ) -> Optional[InstallationCandidate]: """ Return the best candidate per the instance's sort order, or None if no candidate is acceptable. """ if not candidates: return None - best_candidate = max(candidates, key=self._sort_key) - - # Log a warning per PEP 592 if necessary before returning. - link = best_candidate.link - if link.is_yanked: - reason = link.yanked_reason or '' - msg = ( - # Mark this as a unicode string to prevent - # "UnicodeEncodeError: 'ascii' codec can't encode character" - # in Python 2 when the reason contains non-ascii characters. - u'The candidate selected for download or install is a ' - 'yanked version: {candidate}\n' - 'Reason for being yanked: {reason}' - ).format(candidate=best_candidate, reason=reason) - logger.warning(msg) - return best_candidate def compute_best_candidate( self, - candidates, # type: List[InstallationCandidate] - ): - # type: (...) -> BestCandidateResult + candidates: List[InstallationCandidate], + ) -> BestCandidateResult: """ Compute and return a `BestCandidateResult` instance. """ @@ -594,7 +569,7 @@ class CandidateEvaluator(object): ) -class PackageFinder(object): +class PackageFinder: """This finds packages. This is meant to match easy_install's technique for looking for @@ -603,14 +578,13 @@ class PackageFinder(object): def __init__( self, - link_collector, # type: LinkCollector - target_python, # type: TargetPython - allow_yanked, # type: bool - format_control=None, # type: Optional[FormatControl] - candidate_prefs=None, # type: CandidatePreferences - ignore_requires_python=None, # type: Optional[bool] - ): - # type: (...) -> None + link_collector: LinkCollector, + target_python: TargetPython, + allow_yanked: bool, + format_control: Optional[FormatControl] = None, + candidate_prefs: Optional[CandidatePreferences] = None, + ignore_requires_python: Optional[bool] = None, + ) -> None: """ This constructor is primarily meant to be used by the create() class method and from tests. @@ -635,7 +609,7 @@ class PackageFinder(object): self.format_control = format_control # These are boring links that have already been logged somehow. - self._logged_links = set() # type: Set[Link] + self._logged_links: Set[Link] = set() # Don't include an allow_yanked default value to make sure each call # site considers whether yanked releases are allowed. This also causes @@ -644,11 +618,10 @@ class PackageFinder(object): @classmethod def create( cls, - link_collector, # type: LinkCollector - selection_prefs, # type: SelectionPreferences - target_python=None, # type: Optional[TargetPython] - ): - # type: (...) -> PackageFinder + link_collector: LinkCollector, + selection_prefs: SelectionPreferences, + target_python: Optional[TargetPython] = None, + ) -> "PackageFinder": """Create a PackageFinder. :param selection_prefs: The candidate selection preferences, as a @@ -675,42 +648,45 @@ class PackageFinder(object): ) @property - def search_scope(self): - # type: () -> SearchScope + def target_python(self) -> TargetPython: + return self._target_python + + @property + def search_scope(self) -> SearchScope: return self._link_collector.search_scope @search_scope.setter - def search_scope(self, search_scope): - # type: (SearchScope) -> None + def search_scope(self, search_scope: SearchScope) -> None: self._link_collector.search_scope = search_scope @property - def find_links(self): - # type: () -> List[str] + def find_links(self) -> List[str]: return self._link_collector.find_links @property - def index_urls(self): - # type: () -> List[str] + def index_urls(self) -> List[str]: return self.search_scope.index_urls @property - def trusted_hosts(self): - # type: () -> Iterable[str] + def trusted_hosts(self) -> Iterable[str]: for host_port in self._link_collector.session.pip_trusted_origins: yield build_netloc(*host_port) @property - def allow_all_prereleases(self): - # type: () -> bool + def allow_all_prereleases(self) -> bool: return self._candidate_prefs.allow_all_prereleases - def set_allow_all_prereleases(self): - # type: () -> None + def set_allow_all_prereleases(self) -> None: self._candidate_prefs.allow_all_prereleases = True - def make_link_evaluator(self, project_name): - # type: (str) -> LinkEvaluator + @property + def prefer_binary(self) -> bool: + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self) -> None: + self._candidate_prefs.prefer_binary = True + + def make_link_evaluator(self, project_name: str) -> LinkEvaluator: canonical_name = canonicalize_name(project_name) formats = self.format_control.get_allowed_formats(canonical_name) @@ -723,14 +699,13 @@ class PackageFinder(object): ignore_requires_python=self._ignore_requires_python, ) - def _sort_links(self, links): - # type: (Iterable[Link]) -> List[Link] + def _sort_links(self, links: Iterable[Link]) -> List[Link]: """ Returns elements of links in order, non-egg links first, egg links second, while eliminating duplicates """ eggs, no_eggs = [], [] - seen = set() # type: Set[Link] + seen: Set[Link] = set() for link in links: if link not in seen: seen.add(link) @@ -740,19 +715,16 @@ class PackageFinder(object): no_eggs.append(link) return no_eggs + eggs - def _log_skipped_link(self, link, reason): - # type: (Link, Text) -> None + def _log_skipped_link(self, link: Link, reason: str) -> None: if link not in self._logged_links: - # Mark this as a unicode string to prevent "UnicodeEncodeError: - # 'ascii' codec can't encode character" in Python 2 when - # the reason contains non-ascii characters. - # Also, put the link at the end so the reason is more visible - # and because the link string is usually very long. - logger.debug(u'Skipping link: %s: %s', reason, link) + # Put the link at the end so the reason is more visible and because + # the link string is usually very long. + logger.debug("Skipping link: %s: %s", reason, link) self._logged_links.add(link) - def get_install_candidate(self, link_evaluator, link): - # type: (LinkEvaluator, Link) -> Optional[InstallationCandidate] + def get_install_candidate( + self, link_evaluator: LinkEvaluator, link: Link + ) -> Optional[InstallationCandidate]: """ If the link is a candidate for install, convert it to an InstallationCandidate and return it. Otherwise, return None. @@ -766,13 +738,12 @@ class PackageFinder(object): return InstallationCandidate( name=link_evaluator.project_name, link=link, - # Convert the Text result to str since InstallationCandidate - # accepts str. - version=str(result), + version=result, ) - def evaluate_links(self, link_evaluator, links): - # type: (LinkEvaluator, Iterable[Link]) -> List[InstallationCandidate] + def evaluate_links( + self, link_evaluator: LinkEvaluator, links: Iterable[Link] + ) -> List[InstallationCandidate]: """ Convert links that are candidates to InstallationCandidate objects. """ @@ -784,10 +755,12 @@ class PackageFinder(object): return candidates - def process_project_url(self, project_url, link_evaluator): - # type: (Link, LinkEvaluator) -> List[InstallationCandidate] + def process_project_url( + self, project_url: Link, link_evaluator: LinkEvaluator + ) -> List[InstallationCandidate]: logger.debug( - 'Fetching project page and analyzing links: %s', project_url, + "Fetching project page and analyzing links: %s", + project_url, ) html_page = self._link_collector.fetch_page(project_url) if html_page is None: @@ -803,8 +776,8 @@ class PackageFinder(object): return package_links - def find_all_candidates(self, project_name): - # type: (str) -> List[InstallationCandidate] + @functools.lru_cache(maxsize=None) + def find_all_candidates(self, project_name: str) -> List[InstallationCandidate]: """Find all available InstallationCandidate for project_name This checks index_urls and find_links. @@ -813,48 +786,49 @@ class PackageFinder(object): See LinkEvaluator.evaluate_link() for details on which files are accepted. """ - collected_links = self._link_collector.collect_links(project_name) - link_evaluator = self.make_link_evaluator(project_name) - find_links_versions = self.evaluate_links( - link_evaluator, - links=collected_links.find_links, + collected_sources = self._link_collector.collect_sources( + project_name=project_name, + candidates_from_page=functools.partial( + self.process_project_url, + link_evaluator=link_evaluator, + ), ) - page_versions = [] - for project_url in collected_links.project_urls: - package_links = self.process_project_url( - project_url, link_evaluator=link_evaluator, - ) - page_versions.extend(package_links) + page_candidates_it = itertools.chain.from_iterable( + source.page_candidates() + for sources in collected_sources + for source in sources + if source is not None + ) + page_candidates = list(page_candidates_it) - file_versions = self.evaluate_links( + file_links_it = itertools.chain.from_iterable( + source.file_links() + for sources in collected_sources + for source in sources + if source is not None + ) + file_candidates = self.evaluate_links( link_evaluator, - links=collected_links.files, + sorted(file_links_it, reverse=True), ) - if file_versions: - file_versions.sort(reverse=True) - logger.debug( - 'Local files found: %s', - ', '.join([ - url_to_path(candidate.link.url) - for candidate in file_versions - ]) - ) + + if logger.isEnabledFor(logging.DEBUG) and file_candidates: + paths = [url_to_path(c.link.url) for c in file_candidates] + logger.debug("Local files found: %s", ", ".join(paths)) # This is an intentional priority ordering - return file_versions + find_links_versions + page_versions + return file_candidates + page_candidates def make_candidate_evaluator( self, - project_name, # type: str - specifier=None, # type: Optional[specifiers.BaseSpecifier] - hashes=None, # type: Optional[Hashes] - ): - # type: (...) -> CandidateEvaluator - """Create a CandidateEvaluator object to use. - """ + project_name: str, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> CandidateEvaluator: + """Create a CandidateEvaluator object to use.""" candidate_prefs = self._candidate_prefs return CandidateEvaluator.create( project_name=project_name, @@ -865,13 +839,13 @@ class PackageFinder(object): hashes=hashes, ) + @functools.lru_cache(maxsize=None) def find_best_candidate( self, - project_name, # type: str - specifier=None, # type: Optional[specifiers.BaseSpecifier] - hashes=None, # type: Optional[Hashes] - ): - # type: (...) -> BestCandidateResult + project_name: str, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> BestCandidateResult: """Find matches for the given project and specifier. :param specifier: An optional object implementing `filter` @@ -888,65 +862,71 @@ class PackageFinder(object): ) return candidate_evaluator.compute_best_candidate(candidates) - def find_requirement(self, req, upgrade): - # type: (InstallRequirement, bool) -> Optional[Link] + def find_requirement( + self, req: InstallRequirement, upgrade: bool + ) -> Optional[InstallationCandidate]: """Try to find a Link matching req Expects req, an InstallRequirement and upgrade, a boolean - Returns a Link if found, + Returns a InstallationCandidate if found, Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise """ hashes = req.hashes(trust_internet=False) best_candidate_result = self.find_best_candidate( - req.name, specifier=req.specifier, hashes=hashes, + req.name, + specifier=req.specifier, + hashes=hashes, ) best_candidate = best_candidate_result.best_candidate - installed_version = None # type: Optional[_BaseVersion] + installed_version: Optional[_BaseVersion] = None if req.satisfied_by is not None: installed_version = parse_version(req.satisfied_by.version) - def _format_versions(cand_iter): - # type: (Iterable[InstallationCandidate]) -> str + def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str: # This repeated parse_version and str() conversion is needed to # handle different vendoring sources from pip and pkg_resources. # If we stop using the pkg_resources provided specifier and start # using our own, we can drop the cast to str(). - return ", ".join(sorted( - {str(c.version) for c in cand_iter}, - key=parse_version, - )) or "none" + return ( + ", ".join( + sorted( + {str(c.version) for c in cand_iter}, + key=parse_version, + ) + ) + or "none" + ) if installed_version is None and best_candidate is None: logger.critical( - 'Could not find a version that satisfies the requirement %s ' - '(from versions: %s)', + "Could not find a version that satisfies the requirement %s " + "(from versions: %s)", req, _format_versions(best_candidate_result.iter_all()), ) raise DistributionNotFound( - 'No matching distribution found for {}'.format( - req) + "No matching distribution found for {}".format(req) ) best_installed = False if installed_version and ( - best_candidate is None or - best_candidate.version <= installed_version): + best_candidate is None or best_candidate.version <= installed_version + ): best_installed = True if not upgrade and installed_version is not None: if best_installed: logger.debug( - 'Existing installed version (%s) is most up-to-date and ' - 'satisfies requirement', + "Existing installed version (%s) is most up-to-date and " + "satisfies requirement", installed_version, ) else: logger.debug( - 'Existing installed version (%s) satisfies requirement ' - '(most up-to-date version is %s)', + "Existing installed version (%s) satisfies requirement " + "(most up-to-date version is %s)", installed_version, best_candidate.version, ) @@ -955,23 +935,21 @@ class PackageFinder(object): if best_installed: # We have an existing version, and its the best version logger.debug( - 'Installed version (%s) is most up-to-date (past versions: ' - '%s)', + "Installed version (%s) is most up-to-date (past versions: %s)", installed_version, _format_versions(best_candidate_result.iter_applicable()), ) raise BestVersionAlreadyInstalled logger.debug( - 'Using version %s (newest of versions: %s)', + "Using version %s (newest of versions: %s)", best_candidate.version, _format_versions(best_candidate_result.iter_applicable()), ) - return best_candidate.link + return best_candidate -def _find_name_version_sep(fragment, canonical_name): - # type: (str, str) -> int +def _find_name_version_sep(fragment: str, canonical_name: str) -> int: """Find the separator's index based on the package's canonical name. :param fragment: A + filename "fragment" (stem) or @@ -994,11 +972,10 @@ def _find_name_version_sep(fragment, canonical_name): continue if canonicalize_name(fragment[:i]) == canonical_name: return i - raise ValueError("{} does not match {}".format(fragment, canonical_name)) + raise ValueError(f"{fragment} does not match {canonical_name}") -def _extract_version_from_fragment(fragment, canonical_name): - # type: (str, str) -> Optional[str] +def _extract_version_from_fragment(fragment: str, canonical_name: str) -> Optional[str]: """Parse the version string from a + filename "fragment" (stem) or egg fragment. diff --git a/venv/Lib/site-packages/pip/_internal/locations.py b/venv/Lib/site-packages/pip/_internal/locations.py deleted file mode 100644 index 0c115531911af77f5eab69775c7cdd8e43b47e1d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_internal/locations.py +++ /dev/null @@ -1,194 +0,0 @@ -"""Locations where we look for configs, install stuff, etc""" - -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - -import os -import os.path -import platform -import site -import sys -import sysconfig -from distutils import sysconfig as distutils_sysconfig -from distutils.command.install import SCHEME_KEYS # type: ignore -from distutils.command.install import install as distutils_install_command - -from pip._internal.models.scheme import Scheme -from pip._internal.utils import appdirs -from pip._internal.utils.compat import WINDOWS -from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast -from pip._internal.utils.virtualenv import running_under_virtualenv - -if MYPY_CHECK_RUNNING: - from typing import Dict, List, Optional, Union - - from distutils.cmd import Command as DistutilsCommand - - -# Application Directories -USER_CACHE_DIR = appdirs.user_cache_dir("pip") - - -def get_major_minor_version(): - # type: () -> str - """ - Return the major-minor version of the current Python as a string, e.g. - "3.7" or "3.10". - """ - return '{}.{}'.format(*sys.version_info) - - -def get_src_prefix(): - # type: () -> str - if running_under_virtualenv(): - src_prefix = os.path.join(sys.prefix, 'src') - else: - # FIXME: keep src in cwd for now (it is not a temporary folder) - try: - src_prefix = os.path.join(os.getcwd(), 'src') - except OSError: - # In case the current working directory has been renamed or deleted - sys.exit( - "The folder you are executing pip from can no longer be found." - ) - - # under macOS + virtualenv sys.prefix is not properly resolved - # it is something like /path/to/python/bin/.. - return os.path.abspath(src_prefix) - - -# FIXME doesn't account for venv linked to global site-packages - -site_packages = sysconfig.get_path("purelib") # type: Optional[str] - -# This is because of a bug in PyPy's sysconfig module, see -# https://bitbucket.org/pypy/pypy/issues/2506/sysconfig-returns-incorrect-paths -# for more information. -if platform.python_implementation().lower() == "pypy": - site_packages = distutils_sysconfig.get_python_lib() -try: - # Use getusersitepackages if this is present, as it ensures that the - # value is initialised properly. - user_site = site.getusersitepackages() -except AttributeError: - user_site = site.USER_SITE - -if WINDOWS: - bin_py = os.path.join(sys.prefix, 'Scripts') - bin_user = os.path.join(user_site, 'Scripts') - # buildout uses 'bin' on Windows too? - if not os.path.exists(bin_py): - bin_py = os.path.join(sys.prefix, 'bin') - bin_user = os.path.join(user_site, 'bin') -else: - bin_py = os.path.join(sys.prefix, 'bin') - bin_user = os.path.join(user_site, 'bin') - - # Forcing to use /usr/local/bin for standard macOS framework installs - # Also log to ~/Library/Logs/ for use with the Console.app log viewer - if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': - bin_py = '/usr/local/bin' - - -def distutils_scheme( - dist_name, user=False, home=None, root=None, isolated=False, prefix=None -): - # type:(str, bool, str, str, bool, str) -> Dict[str, str] - """ - Return a distutils install scheme - """ - from distutils.dist import Distribution - - dist_args = {'name': dist_name} # type: Dict[str, Union[str, List[str]]] - if isolated: - dist_args["script_args"] = ["--no-user-cfg"] - - d = Distribution(dist_args) - d.parse_config_files() - obj = None # type: Optional[DistutilsCommand] - obj = d.get_command_obj('install', create=True) - assert obj is not None - i = cast(distutils_install_command, obj) - # NOTE: setting user or home has the side-effect of creating the home dir - # or user base for installations during finalize_options() - # ideally, we'd prefer a scheme class that has no side-effects. - assert not (user and prefix), "user={} prefix={}".format(user, prefix) - assert not (home and prefix), "home={} prefix={}".format(home, prefix) - i.user = user or i.user - if user or home: - i.prefix = "" - i.prefix = prefix or i.prefix - i.home = home or i.home - i.root = root or i.root - i.finalize_options() - - scheme = {} - for key in SCHEME_KEYS: - scheme[key] = getattr(i, 'install_' + key) - - # install_lib specified in setup.cfg should install *everything* - # into there (i.e. it takes precedence over both purelib and - # platlib). Note, i.install_lib is *always* set after - # finalize_options(); we only want to override here if the user - # has explicitly requested it hence going back to the config - if 'install_lib' in d.get_option_dict('install'): - scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) - - if running_under_virtualenv(): - scheme['headers'] = os.path.join( - sys.prefix, - 'include', - 'site', - 'python{}'.format(get_major_minor_version()), - dist_name, - ) - - if root is not None: - path_no_drive = os.path.splitdrive( - os.path.abspath(scheme["headers"]))[1] - scheme["headers"] = os.path.join( - root, - path_no_drive[1:], - ) - - return scheme - - -def get_scheme( - dist_name, # type: str - user=False, # type: bool - home=None, # type: Optional[str] - root=None, # type: Optional[str] - isolated=False, # type: bool - prefix=None, # type: Optional[str] -): - # type: (...) -> Scheme - """ - Get the "scheme" corresponding to the input parameters. The distutils - documentation provides the context for the available schemes: - https://docs.python.org/3/install/index.html#alternate-installation - - :param dist_name: the name of the package to retrieve the scheme for, used - in the headers scheme path - :param user: indicates to use the "user" scheme - :param home: indicates to use the "home" scheme and provides the base - directory for the same - :param root: root under which other directories are re-based - :param isolated: equivalent to --no-user-cfg, i.e. do not consider - ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for - scheme paths - :param prefix: indicates to use the "prefix" scheme and provides the - base directory for the same - """ - scheme = distutils_scheme( - dist_name, user, home, root, isolated, prefix - ) - return Scheme( - platlib=scheme["platlib"], - purelib=scheme["purelib"], - headers=scheme["headers"], - scripts=scheme["scripts"], - data=scheme["data"], - ) diff --git a/venv/Lib/site-packages/pip/_internal/main.py b/venv/Lib/site-packages/pip/_internal/main.py index 3208d5b8820eadf8a1ebe4851c984c6033c289bd..33c6d24cd85b55a9fb1b1e6ab784f471e2b135f0 100644 --- a/venv/Lib/site-packages/pip/_internal/main.py +++ b/venv/Lib/site-packages/pip/_internal/main.py @@ -1,11 +1,7 @@ -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from typing import List, Optional -if MYPY_CHECK_RUNNING: - from typing import Optional, List - -def main(args=None): - # type: (Optional[List[str]]) -> int +def main(args: Optional[List[str]] = None) -> int: """This is preserved for old console scripts that may still be referencing it. diff --git a/venv/Lib/site-packages/pip/_internal/models/candidate.py b/venv/Lib/site-packages/pip/_internal/models/candidate.py index 1dc1a576eea788c23f5722bbb8e10ae950ef38bd..a4963aec6388c27c3beb064f0a730af200380aee 100644 --- a/venv/Lib/site-packages/pip/_internal/models/candidate.py +++ b/venv/Lib/site-packages/pip/_internal/models/candidate.py @@ -1,36 +1,34 @@ from pip._vendor.packaging.version import parse as parse_version +from pip._internal.models.link import Link from pip._internal.utils.models import KeyBasedCompareMixin -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from pip._vendor.packaging.version import _BaseVersion - from pip._internal.models.link import Link class InstallationCandidate(KeyBasedCompareMixin): - """Represents a potential "candidate" for installation. - """ + """Represents a potential "candidate" for installation.""" + + __slots__ = ["name", "version", "link"] - def __init__(self, name, version, link): - # type: (str, str, Link) -> None + def __init__(self, name: str, version: str, link: Link) -> None: self.name = name - self.version = parse_version(version) # type: _BaseVersion + self.version = parse_version(version) self.link = link - super(InstallationCandidate, self).__init__( + super().__init__( key=(self.name, self.version, self.link), - defining_class=InstallationCandidate + defining_class=InstallationCandidate, ) - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "".format( - self.name, self.version, self.link, + self.name, + self.version, + self.link, ) - def __str__(self): - # type: () -> str - return '{!r} candidate (version {} at {})'.format( - self.name, self.version, self.link, + def __str__(self) -> str: + return "{!r} candidate (version {} at {})".format( + self.name, + self.version, + self.link, ) diff --git a/venv/Lib/site-packages/pip/_internal/models/direct_url.py b/venv/Lib/site-packages/pip/_internal/models/direct_url.py index 87bd9fe4b8f5e42273ecb8198289b647babafb15..92060d45db8888500a94669a02af76b220b7a242 100644 --- a/venv/Lib/site-packages/pip/_internal/models/direct_url.py +++ b/venv/Lib/site-packages/pip/_internal/models/direct_url.py @@ -1,22 +1,8 @@ """ PEP 610 """ import json import re - -from pip._vendor import six -from pip._vendor.six.moves.urllib import parse as urllib_parse - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Dict, Iterable, Optional, Type, TypeVar, Union - ) - - T = TypeVar("T") - - -DIRECT_URL_METADATA_NAME = "direct_url.json" -ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") +import urllib.parse +from typing import Any, Dict, Iterable, Optional, Type, TypeVar, Union __all__ = [ "DirectUrl", @@ -26,19 +12,23 @@ __all__ = [ "VcsInfo", ] +T = TypeVar("T") + +DIRECT_URL_METADATA_NAME = "direct_url.json" +ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") + class DirectUrlValidationError(Exception): pass -def _get(d, expected_type, key, default=None): - # type: (Dict[str, Any], Type[T], str, Optional[T]) -> Optional[T] +def _get( + d: Dict[str, Any], expected_type: Type[T], key: str, default: Optional[T] = None +) -> Optional[T]: """Get value from dictionary and verify expected type.""" if key not in d: return default value = d[key] - if six.PY2 and expected_type is str: - expected_type = six.string_types # type: ignore if not isinstance(value, expected_type): raise DirectUrlValidationError( "{!r} has unexpected type for {} (expected {})".format( @@ -48,16 +38,16 @@ def _get(d, expected_type, key, default=None): return value -def _get_required(d, expected_type, key, default=None): - # type: (Dict[str, Any], Type[T], str, Optional[T]) -> T +def _get_required( + d: Dict[str, Any], expected_type: Type[T], key: str, default: Optional[T] = None +) -> T: value = _get(d, expected_type, key, default) if value is None: - raise DirectUrlValidationError("{} must have a value".format(key)) + raise DirectUrlValidationError(f"{key} must have a value") return value -def _exactly_one_of(infos): - # type: (Iterable[Optional[InfoType]]) -> InfoType +def _exactly_one_of(infos: Iterable[Optional["InfoType"]]) -> "InfoType": infos = [info for info in infos if info is not None] if not infos: raise DirectUrlValidationError( @@ -71,23 +61,22 @@ def _exactly_one_of(infos): return infos[0] -def _filter_none(**kwargs): - # type: (Any) -> Dict[str, Any] +def _filter_none(**kwargs: Any) -> Dict[str, Any]: """Make dict excluding None values.""" return {k: v for k, v in kwargs.items() if v is not None} -class VcsInfo(object): +class VcsInfo: name = "vcs_info" def __init__( self, - vcs, # type: str - commit_id, # type: str - requested_revision=None, # type: Optional[str] - resolved_revision=None, # type: Optional[str] - resolved_revision_type=None, # type: Optional[str] - ): + vcs: str, + commit_id: str, + requested_revision: Optional[str] = None, + resolved_revision: Optional[str] = None, + resolved_revision_type: Optional[str] = None, + ) -> None: self.vcs = vcs self.requested_revision = requested_revision self.commit_id = commit_id @@ -95,8 +84,7 @@ class VcsInfo(object): self.resolved_revision_type = resolved_revision_type @classmethod - def _from_dict(cls, d): - # type: (Optional[Dict[str, Any]]) -> Optional[VcsInfo] + def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["VcsInfo"]: if d is None: return None return cls( @@ -107,8 +95,7 @@ class VcsInfo(object): resolved_revision_type=_get(d, str, "resolved_revision_type"), ) - def _to_dict(self): - # type: () -> Dict[str, Any] + def _to_dict(self) -> Dict[str, Any]: return _filter_none( vcs=self.vcs, requested_revision=self.requested_revision, @@ -118,75 +105,66 @@ class VcsInfo(object): ) -class ArchiveInfo(object): +class ArchiveInfo: name = "archive_info" def __init__( self, - hash=None, # type: Optional[str] - ): + hash: Optional[str] = None, + ) -> None: self.hash = hash @classmethod - def _from_dict(cls, d): - # type: (Optional[Dict[str, Any]]) -> Optional[ArchiveInfo] + def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["ArchiveInfo"]: if d is None: return None return cls(hash=_get(d, str, "hash")) - def _to_dict(self): - # type: () -> Dict[str, Any] + def _to_dict(self) -> Dict[str, Any]: return _filter_none(hash=self.hash) -class DirInfo(object): +class DirInfo: name = "dir_info" def __init__( self, - editable=False, # type: bool - ): + editable: bool = False, + ) -> None: self.editable = editable @classmethod - def _from_dict(cls, d): - # type: (Optional[Dict[str, Any]]) -> Optional[DirInfo] + def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["DirInfo"]: if d is None: return None - return cls( - editable=_get_required(d, bool, "editable", default=False) - ) + return cls(editable=_get_required(d, bool, "editable", default=False)) - def _to_dict(self): - # type: () -> Dict[str, Any] + def _to_dict(self) -> Dict[str, Any]: return _filter_none(editable=self.editable or None) -if MYPY_CHECK_RUNNING: - InfoType = Union[ArchiveInfo, DirInfo, VcsInfo] +InfoType = Union[ArchiveInfo, DirInfo, VcsInfo] -class DirectUrl(object): - +class DirectUrl: def __init__( self, - url, # type: str - info, # type: InfoType - subdirectory=None, # type: Optional[str] - ): + url: str, + info: InfoType, + subdirectory: Optional[str] = None, + ) -> None: self.url = url self.info = info self.subdirectory = subdirectory - def _remove_auth_from_netloc(self, netloc): - # type: (str) -> str + def _remove_auth_from_netloc(self, netloc: str) -> str: if "@" not in netloc: return netloc user_pass, netloc_no_user_pass = netloc.split("@", 1) if ( - isinstance(self.info, VcsInfo) and - self.info.vcs == "git" and - user_pass == "git" + isinstance(self.info, VcsInfo) + and self.info.vcs == "git" + and user_pass == "git" ): return netloc if ENV_VAR_RE.match(user_pass): @@ -194,26 +172,23 @@ class DirectUrl(object): return netloc_no_user_pass @property - def redacted_url(self): - # type: () -> str + def redacted_url(self) -> str: """url with user:password part removed unless it is formed with environment variables as specified in PEP 610, or it is ``git`` in the case of a git URL. """ - purl = urllib_parse.urlsplit(self.url) + purl = urllib.parse.urlsplit(self.url) netloc = self._remove_auth_from_netloc(purl.netloc) - surl = urllib_parse.urlunsplit( + surl = urllib.parse.urlunsplit( (purl.scheme, netloc, purl.path, purl.query, purl.fragment) ) return surl - def validate(self): - # type: () -> None + def validate(self) -> None: self.from_dict(self.to_dict()) @classmethod - def from_dict(cls, d): - # type: (Dict[str, Any]) -> DirectUrl + def from_dict(cls, d: Dict[str, Any]) -> "DirectUrl": return DirectUrl( url=_get_required(d, str, "url"), subdirectory=_get(d, str, "subdirectory"), @@ -226,8 +201,7 @@ class DirectUrl(object): ), ) - def to_dict(self): - # type: () -> Dict[str, Any] + def to_dict(self) -> Dict[str, Any]: res = _filter_none( url=self.redacted_url, subdirectory=self.subdirectory, @@ -236,10 +210,11 @@ class DirectUrl(object): return res @classmethod - def from_json(cls, s): - # type: (str) -> DirectUrl + def from_json(cls, s: str) -> "DirectUrl": return cls.from_dict(json.loads(s)) - def to_json(self): - # type: () -> str + def to_json(self) -> str: return json.dumps(self.to_dict(), sort_keys=True) + + def is_local_editable(self) -> bool: + return isinstance(self.info, DirInfo) and self.info.editable diff --git a/venv/Lib/site-packages/pip/_internal/models/format_control.py b/venv/Lib/site-packages/pip/_internal/models/format_control.py index 2e13727ca006977f3fb2df30fd1a25bb1670cf3e..db3995eac9f9ec2450e0e2d4a18e666c0b178681 100644 --- a/venv/Lib/site-packages/pip/_internal/models/format_control.py +++ b/venv/Lib/site-packages/pip/_internal/models/format_control.py @@ -1,21 +1,20 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False +from typing import FrozenSet, Optional, Set from pip._vendor.packaging.utils import canonicalize_name from pip._internal.exceptions import CommandError -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Optional, Set, FrozenSet +class FormatControl: + """Helper for managing formats from which a package can be installed.""" -class FormatControl(object): - """Helper for managing formats from which a package can be installed. - """ + __slots__ = ["no_binary", "only_binary"] - def __init__(self, no_binary=None, only_binary=None): - # type: (Optional[Set[str]], Optional[Set[str]]) -> None + def __init__( + self, + no_binary: Optional[Set[str]] = None, + only_binary: Optional[Set[str]] = None, + ) -> None: if no_binary is None: no_binary = set() if only_binary is None: @@ -24,61 +23,58 @@ class FormatControl(object): self.no_binary = no_binary self.only_binary = only_binary - def __eq__(self, other): - # type: (object) -> bool - return self.__dict__ == other.__dict__ + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented - def __ne__(self, other): - # type: (object) -> bool - return not self.__eq__(other) + if self.__slots__ != other.__slots__: + return False - def __repr__(self): - # type: () -> str + return all(getattr(self, k) == getattr(other, k) for k in self.__slots__) + + def __repr__(self) -> str: return "{}({}, {})".format( - self.__class__.__name__, - self.no_binary, - self.only_binary + self.__class__.__name__, self.no_binary, self.only_binary ) @staticmethod - def handle_mutual_excludes(value, target, other): - # type: (str, Optional[Set[str]], Optional[Set[str]]) -> None - if value.startswith('-'): + def handle_mutual_excludes(value: str, target: Set[str], other: Set[str]) -> None: + if value.startswith("-"): raise CommandError( "--no-binary / --only-binary option requires 1 argument." ) - new = value.split(',') - while ':all:' in new: + new = value.split(",") + while ":all:" in new: other.clear() target.clear() - target.add(':all:') - del new[:new.index(':all:') + 1] + target.add(":all:") + del new[: new.index(":all:") + 1] # Without a none, we want to discard everything as :all: covers it - if ':none:' not in new: + if ":none:" not in new: return for name in new: - if name == ':none:': + if name == ":none:": target.clear() continue name = canonicalize_name(name) other.discard(name) target.add(name) - def get_allowed_formats(self, canonical_name): - # type: (str) -> FrozenSet[str] + def get_allowed_formats(self, canonical_name: str) -> FrozenSet[str]: result = {"binary", "source"} if canonical_name in self.only_binary: - result.discard('source') + result.discard("source") elif canonical_name in self.no_binary: - result.discard('binary') - elif ':all:' in self.only_binary: - result.discard('source') - elif ':all:' in self.no_binary: - result.discard('binary') + result.discard("binary") + elif ":all:" in self.only_binary: + result.discard("source") + elif ":all:" in self.no_binary: + result.discard("binary") return frozenset(result) - def disallow_binaries(self): - # type: () -> None + def disallow_binaries(self) -> None: self.handle_mutual_excludes( - ':all:', self.no_binary, self.only_binary, + ":all:", + self.no_binary, + self.only_binary, ) diff --git a/venv/Lib/site-packages/pip/_internal/models/index.py b/venv/Lib/site-packages/pip/_internal/models/index.py index ead1efbda761ebed373700ce9e69797838c2b9d9..b94c32511f0cda2363bfc4f29c9c8bfcc7101f9b 100644 --- a/venv/Lib/site-packages/pip/_internal/models/index.py +++ b/venv/Lib/site-packages/pip/_internal/models/index.py @@ -1,31 +1,28 @@ -from pip._vendor.six.moves.urllib import parse as urllib_parse +import urllib.parse -class PackageIndex(object): - """Represents a Package Index and provides easier access to endpoints - """ +class PackageIndex: + """Represents a Package Index and provides easier access to endpoints""" - def __init__(self, url, file_storage_domain): - # type: (str, str) -> None - super(PackageIndex, self).__init__() + __slots__ = ["url", "netloc", "simple_url", "pypi_url", "file_storage_domain"] + + def __init__(self, url: str, file_storage_domain: str) -> None: + super().__init__() self.url = url - self.netloc = urllib_parse.urlsplit(url).netloc - self.simple_url = self._url_for_path('simple') - self.pypi_url = self._url_for_path('pypi') + self.netloc = urllib.parse.urlsplit(url).netloc + self.simple_url = self._url_for_path("simple") + self.pypi_url = self._url_for_path("pypi") # This is part of a temporary hack used to block installs of PyPI # packages which depend on external urls only necessary until PyPI can # block such packages themselves self.file_storage_domain = file_storage_domain - def _url_for_path(self, path): - # type: (str) -> str - return urllib_parse.urljoin(self.url, path) + def _url_for_path(self, path: str) -> str: + return urllib.parse.urljoin(self.url, path) -PyPI = PackageIndex( - 'https://pypi.org/', file_storage_domain='files.pythonhosted.org' -) +PyPI = PackageIndex("https://pypi.org/", file_storage_domain="files.pythonhosted.org") TestPyPI = PackageIndex( - 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org' + "https://test.pypi.org/", file_storage_domain="test-files.pythonhosted.org" ) diff --git a/venv/Lib/site-packages/pip/_internal/models/link.py b/venv/Lib/site-packages/pip/_internal/models/link.py index df4f8f0168519d67978bdb9bf1fe664e518a55d9..6069b278b9bcbf64f1552c932ab909690bb7c149 100644 --- a/venv/Lib/site-packages/pip/_internal/models/link.py +++ b/venv/Lib/site-packages/pip/_internal/models/link.py @@ -1,38 +1,50 @@ +import functools +import logging import os import posixpath import re - -from pip._vendor.six.moves.urllib import parse as urllib_parse +import urllib.parse +from typing import TYPE_CHECKING, Dict, List, NamedTuple, Optional, Tuple, Union from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.hashes import Hashes from pip._internal.utils.misc import ( redact_auth_from_url, split_auth_from_netloc, splitext, ) from pip._internal.utils.models import KeyBasedCompareMixin -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url, url_to_path -if MYPY_CHECK_RUNNING: - from typing import Optional, Text, Tuple, Union +if TYPE_CHECKING: from pip._internal.index.collector import HTMLPage - from pip._internal.utils.hashes import Hashes + +logger = logging.getLogger(__name__) + + +_SUPPORTED_HASHES = ("sha1", "sha224", "sha384", "sha256", "sha512", "md5") class Link(KeyBasedCompareMixin): - """Represents a parsed link from a Package Index's simple URL - """ + """Represents a parsed link from a Package Index's simple URL""" + + __slots__ = [ + "_parsed_url", + "_url", + "comes_from", + "requires_python", + "yanked_reason", + "cache_link_parsing", + ] def __init__( self, - url, # type: str - comes_from=None, # type: Optional[Union[str, HTMLPage]] - requires_python=None, # type: Optional[str] - yanked_reason=None, # type: Optional[Text] - cache_link_parsing=True, # type: bool - ): - # type: (...) -> None + url: str, + comes_from: Optional[Union[str, "HTMLPage"]] = None, + requires_python: Optional[str] = None, + yanked_reason: Optional[str] = None, + cache_link_parsing: bool = True, + ) -> None: """ :param url: url of the resource pointed to (href of the link) :param comes_from: instance of HTMLPage where the link was found, @@ -55,10 +67,10 @@ class Link(KeyBasedCompareMixin): """ # url can be a UNC windows share - if url.startswith('\\\\'): + if url.startswith("\\\\"): url = path_to_url(url) - self._parsed_url = urllib_parse.urlsplit(url) + self._parsed_url = urllib.parse.urlsplit(url) # Store the url as a private attribute to prevent accidentally # trying to set a new value. self._url = url @@ -67,35 +79,32 @@ class Link(KeyBasedCompareMixin): self.requires_python = requires_python if requires_python else None self.yanked_reason = yanked_reason - super(Link, self).__init__(key=url, defining_class=Link) + super().__init__(key=url, defining_class=Link) self.cache_link_parsing = cache_link_parsing - def __str__(self): - # type: () -> str + def __str__(self) -> str: if self.requires_python: - rp = ' (requires-python:{})'.format(self.requires_python) + rp = f" (requires-python:{self.requires_python})" else: - rp = '' + rp = "" if self.comes_from: - return '{} (from {}){}'.format( - redact_auth_from_url(self._url), self.comes_from, rp) + return "{} (from {}){}".format( + redact_auth_from_url(self._url), self.comes_from, rp + ) else: return redact_auth_from_url(str(self._url)) - def __repr__(self): - # type: () -> str - return ''.format(self) + def __repr__(self) -> str: + return f"" @property - def url(self): - # type: () -> str + def url(self) -> str: return self._url @property - def filename(self): - # type: () -> str - path = self.path.rstrip('/') + def filename(self) -> str: + path = self.path.rstrip("/") name = posixpath.basename(path) if not name: # Make sure we don't leak auth information if the netloc @@ -103,127 +112,107 @@ class Link(KeyBasedCompareMixin): netloc, user_pass = split_auth_from_netloc(self.netloc) return netloc - name = urllib_parse.unquote(name) - assert name, ( - 'URL {self._url!r} produced no filename'.format(**locals())) + name = urllib.parse.unquote(name) + assert name, f"URL {self._url!r} produced no filename" return name @property - def file_path(self): - # type: () -> str + def file_path(self) -> str: return url_to_path(self.url) @property - def scheme(self): - # type: () -> str + def scheme(self) -> str: return self._parsed_url.scheme @property - def netloc(self): - # type: () -> str + def netloc(self) -> str: """ This can contain auth information. """ return self._parsed_url.netloc @property - def path(self): - # type: () -> str - return urllib_parse.unquote(self._parsed_url.path) + def path(self) -> str: + return urllib.parse.unquote(self._parsed_url.path) - def splitext(self): - # type: () -> Tuple[str, str] - return splitext(posixpath.basename(self.path.rstrip('/'))) + def splitext(self) -> Tuple[str, str]: + return splitext(posixpath.basename(self.path.rstrip("/"))) @property - def ext(self): - # type: () -> str + def ext(self) -> str: return self.splitext()[1] @property - def url_without_fragment(self): - # type: () -> str + def url_without_fragment(self) -> str: scheme, netloc, path, query, fragment = self._parsed_url - return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + return urllib.parse.urlunsplit((scheme, netloc, path, query, "")) - _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") @property - def egg_fragment(self): - # type: () -> Optional[str] + def egg_fragment(self) -> Optional[str]: match = self._egg_fragment_re.search(self._url) if not match: return None return match.group(1) - _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") @property - def subdirectory_fragment(self): - # type: () -> Optional[str] + def subdirectory_fragment(self) -> Optional[str]: match = self._subdirectory_fragment_re.search(self._url) if not match: return None return match.group(1) _hash_re = re.compile( - r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + r"({choices})=([a-f0-9]+)".format(choices="|".join(_SUPPORTED_HASHES)) ) @property - def hash(self): - # type: () -> Optional[str] + def hash(self) -> Optional[str]: match = self._hash_re.search(self._url) if match: return match.group(2) return None @property - def hash_name(self): - # type: () -> Optional[str] + def hash_name(self) -> Optional[str]: match = self._hash_re.search(self._url) if match: return match.group(1) return None @property - def show_url(self): - # type: () -> str - return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0]) + def show_url(self) -> str: + return posixpath.basename(self._url.split("#", 1)[0].split("?", 1)[0]) @property - def is_file(self): - # type: () -> bool - return self.scheme == 'file' + def is_file(self) -> bool: + return self.scheme == "file" - def is_existing_dir(self): - # type: () -> bool + def is_existing_dir(self) -> bool: return self.is_file and os.path.isdir(self.file_path) @property - def is_wheel(self): - # type: () -> bool + def is_wheel(self) -> bool: return self.ext == WHEEL_EXTENSION @property - def is_vcs(self): - # type: () -> bool + def is_vcs(self) -> bool: from pip._internal.vcs import vcs return self.scheme in vcs.all_schemes @property - def is_yanked(self): - # type: () -> bool + def is_yanked(self) -> bool: return self.yanked_reason is not None @property - def has_hash(self): - # type: () -> bool + def has_hash(self) -> bool: return self.hash_name is not None - def is_hash_allowed(self, hashes): - # type: (Optional[Hashes]) -> bool + def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: """ Return True if the link has a hash and it is allowed. """ @@ -234,3 +223,66 @@ class Link(KeyBasedCompareMixin): assert self.hash is not None return hashes.is_hash_allowed(self.hash_name, hex_digest=self.hash) + + +class _CleanResult(NamedTuple): + """Convert link for equivalency check. + + This is used in the resolver to check whether two URL-specified requirements + likely point to the same distribution and can be considered equivalent. This + equivalency logic avoids comparing URLs literally, which can be too strict + (e.g. "a=1&b=2" vs "b=2&a=1") and produce conflicts unexpecting to users. + + Currently this does three things: + + 1. Drop the basic auth part. This is technically wrong since a server can + serve different content based on auth, but if it does that, it is even + impossible to guarantee two URLs without auth are equivalent, since + the user can input different auth information when prompted. So the + practical solution is to assume the auth doesn't affect the response. + 2. Parse the query to avoid the ordering issue. Note that ordering under the + same key in the query are NOT cleaned; i.e. "a=1&a=2" and "a=2&a=1" are + still considered different. + 3. Explicitly drop most of the fragment part, except ``subdirectory=`` and + hash values, since it should have no impact the downloaded content. Note + that this drops the "egg=" part historically used to denote the requested + project (and extras), which is wrong in the strictest sense, but too many + people are supplying it inconsistently to cause superfluous resolution + conflicts, so we choose to also ignore them. + """ + + parsed: urllib.parse.SplitResult + query: Dict[str, List[str]] + subdirectory: str + hashes: Dict[str, str] + + +def _clean_link(link: Link) -> _CleanResult: + parsed = link._parsed_url + netloc = parsed.netloc.rsplit("@", 1)[-1] + # According to RFC 8089, an empty host in file: means localhost. + if parsed.scheme == "file" and not netloc: + netloc = "localhost" + fragment = urllib.parse.parse_qs(parsed.fragment) + if "egg" in fragment: + logger.debug("Ignoring egg= fragment in %s", link) + try: + # If there are multiple subdirectory values, use the first one. + # This matches the behavior of Link.subdirectory_fragment. + subdirectory = fragment["subdirectory"][0] + except (IndexError, KeyError): + subdirectory = "" + # If there are multiple hash values under the same algorithm, use the + # first one. This matches the behavior of Link.hash_value. + hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment} + return _CleanResult( + parsed=parsed._replace(netloc=netloc, query="", fragment=""), + query=urllib.parse.parse_qs(parsed.query), + subdirectory=subdirectory, + hashes=hashes, + ) + + +@functools.lru_cache(maxsize=None) +def links_equivalent(link1: Link, link2: Link) -> bool: + return _clean_link(link1) == _clean_link(link2) diff --git a/venv/Lib/site-packages/pip/_internal/models/scheme.py b/venv/Lib/site-packages/pip/_internal/models/scheme.py index af07b4078f997b5c6005c042ac178282c49fd5e7..f51190ac60354d90eb2aef4b04c484f8517275c2 100644 --- a/venv/Lib/site-packages/pip/_internal/models/scheme.py +++ b/venv/Lib/site-packages/pip/_internal/models/scheme.py @@ -6,18 +6,24 @@ https://docs.python.org/3/install/index.html#alternate-installation. """ -class Scheme(object): +SCHEME_KEYS = ["platlib", "purelib", "headers", "scripts", "data"] + + +class Scheme: """A Scheme holds paths which are used as the base directories for artifacts associated with a Python package. """ + + __slots__ = SCHEME_KEYS + def __init__( self, - platlib, # type: str - purelib, # type: str - headers, # type: str - scripts, # type: str - data, # type: str - ): + platlib: str, + purelib: str, + headers: str, + scripts: str, + data: str, + ) -> None: self.platlib = platlib self.purelib = purelib self.headers = headers diff --git a/venv/Lib/site-packages/pip/_internal/models/search_scope.py b/venv/Lib/site-packages/pip/_internal/models/search_scope.py index 7a0008e4825f69e0ed1631151d2770f4ac4947a9..e4e54c2f4c696407c6de380d44d790412b2d4ee5 100644 --- a/venv/Lib/site-packages/pip/_internal/models/search_scope.py +++ b/venv/Lib/site-packages/pip/_internal/models/search_scope.py @@ -2,35 +2,32 @@ import itertools import logging import os import posixpath +import urllib.parse +from typing import List from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._internal.models.index import PyPI from pip._internal.utils.compat import has_tls from pip._internal.utils.misc import normalize_path, redact_auth_from_url -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List - logger = logging.getLogger(__name__) -class SearchScope(object): +class SearchScope: """ Encapsulates the locations that pip is configured to search. """ + __slots__ = ["find_links", "index_urls"] + @classmethod def create( cls, - find_links, # type: List[str] - index_urls, # type: List[str] - ): - # type: (...) -> SearchScope + find_links: List[str], + index_urls: List[str], + ) -> "SearchScope": """ Create a SearchScope object after normalizing the `find_links`. """ @@ -39,9 +36,9 @@ class SearchScope(object): # it and if it exists, use the normalized version. # This is deliberately conservative - it might be fine just to # blindly normalize anything starting with a ~... - built_find_links = [] # type: List[str] + built_find_links: List[str] = [] for link in find_links: - if link.startswith('~'): + if link.startswith("~"): new_link = normalize_path(link) if os.path.exists(new_link): link = new_link @@ -51,12 +48,12 @@ class SearchScope(object): # relies on TLS. if not has_tls(): for link in itertools.chain(index_urls, built_find_links): - parsed = urllib_parse.urlparse(link) - if parsed.scheme == 'https': + parsed = urllib.parse.urlparse(link) + if parsed.scheme == "https": logger.warning( - 'pip is configured with locations that require ' - 'TLS/SSL, however the ssl module in Python is not ' - 'available.' + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." ) break @@ -67,15 +64,13 @@ class SearchScope(object): def __init__( self, - find_links, # type: List[str] - index_urls, # type: List[str] - ): - # type: (...) -> None + find_links: List[str], + index_urls: List[str], + ) -> None: self.find_links = find_links self.index_urls = index_urls - def get_formatted_locations(self): - # type: () -> str + def get_formatted_locations(self) -> str: lines = [] redacted_index_urls = [] if self.index_urls and self.index_urls != [PyPI.simple_url]: @@ -84,7 +79,7 @@ class SearchScope(object): redacted_index_url = redact_auth_from_url(url) # Parse the URL - purl = urllib_parse.urlsplit(redacted_index_url) + purl = urllib.parse.urlsplit(redacted_index_url) # URL is generally invalid if scheme and netloc is missing # there are issues with Python and URL parsing, so this test @@ -93,41 +88,42 @@ class SearchScope(object): # exceptions for malformed URLs if not purl.scheme and not purl.netloc: logger.warning( - 'The index url "{}" seems invalid, ' - 'please provide a scheme.'.format(redacted_index_url)) + 'The index url "%s" seems invalid, please provide a scheme.', + redacted_index_url, + ) redacted_index_urls.append(redacted_index_url) - lines.append('Looking in indexes: {}'.format( - ', '.join(redacted_index_urls))) + lines.append( + "Looking in indexes: {}".format(", ".join(redacted_index_urls)) + ) if self.find_links: lines.append( - 'Looking in links: {}'.format(', '.join( - redact_auth_from_url(url) for url in self.find_links)) + "Looking in links: {}".format( + ", ".join(redact_auth_from_url(url) for url in self.find_links) + ) ) - return '\n'.join(lines) + return "\n".join(lines) - def get_index_urls_locations(self, project_name): - # type: (str) -> List[str] + def get_index_urls_locations(self, project_name: str) -> List[str]: """Returns the locations found via self.index_urls Checks the url_name on the main (first in the list) index and use this url_name to produce all locations """ - def mkurl_pypi_url(url): - # type: (str) -> str + def mkurl_pypi_url(url: str) -> str: loc = posixpath.join( - url, - urllib_parse.quote(canonicalize_name(project_name))) + url, urllib.parse.quote(canonicalize_name(project_name)) + ) # For maximum compatibility with easy_install, ensure the path # ends in a trailing slash. Although this isn't in the spec # (and PyPI can handle it without the slash) some other index # implementations might break if they relied on easy_install's # behavior. - if not loc.endswith('/'): - loc = loc + '/' + if not loc.endswith("/"): + loc = loc + "/" return loc return [mkurl_pypi_url(url) for url in self.index_urls] diff --git a/venv/Lib/site-packages/pip/_internal/models/selection_prefs.py b/venv/Lib/site-packages/pip/_internal/models/selection_prefs.py index f58fdce9cdfcb9320c09f0652ff20a9dc52f3701..977bc4caa75c1e76156fa97e2841a01332f6fa47 100644 --- a/venv/Lib/site-packages/pip/_internal/models/selection_prefs.py +++ b/venv/Lib/site-packages/pip/_internal/models/selection_prefs.py @@ -1,30 +1,34 @@ -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from typing import Optional -if MYPY_CHECK_RUNNING: - from typing import Optional - from pip._internal.models.format_control import FormatControl +from pip._internal.models.format_control import FormatControl -class SelectionPreferences(object): - +class SelectionPreferences: """ Encapsulates the candidate selection preferences for downloading and installing files. """ + __slots__ = [ + "allow_yanked", + "allow_all_prereleases", + "format_control", + "prefer_binary", + "ignore_requires_python", + ] + # Don't include an allow_yanked default value to make sure each call # site considers whether yanked releases are allowed. This also causes # that decision to be made explicit in the calling code, which helps # people when reading the code. def __init__( self, - allow_yanked, # type: bool - allow_all_prereleases=False, # type: bool - format_control=None, # type: Optional[FormatControl] - prefer_binary=False, # type: bool - ignore_requires_python=None, # type: Optional[bool] - ): - # type: (...) -> None + allow_yanked: bool, + allow_all_prereleases: bool = False, + format_control: Optional[FormatControl] = None, + prefer_binary: bool = False, + ignore_requires_python: Optional[bool] = None, + ) -> None: """Create a SelectionPreferences object. :param allow_yanked: Whether files marked as yanked (in the sense diff --git a/venv/Lib/site-packages/pip/_internal/models/target_python.py b/venv/Lib/site-packages/pip/_internal/models/target_python.py index 84f1c209c66d36f90d8e468808ec3e1d529cf06d..744bd7ef58b4870406fcef8cb3b3667548a0ccea 100644 --- a/venv/Lib/site-packages/pip/_internal/models/target_python.py +++ b/venv/Lib/site-packages/pip/_internal/models/target_python.py @@ -1,44 +1,47 @@ import sys +from typing import List, Optional, Tuple -from pip._internal.utils.compatibility_tags import ( - get_supported, - version_info_to_nodot, -) -from pip._internal.utils.misc import normalize_version_info -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Tuple +from pip._vendor.packaging.tags import Tag - from pip._vendor.packaging.tags import Tag +from pip._internal.utils.compatibility_tags import get_supported, version_info_to_nodot +from pip._internal.utils.misc import normalize_version_info -class TargetPython(object): +class TargetPython: """ Encapsulates the properties of a Python interpreter one is targeting for a package install, download, etc. """ + __slots__ = [ + "_given_py_version_info", + "abis", + "implementation", + "platforms", + "py_version", + "py_version_info", + "_valid_tags", + ] + def __init__( self, - platform=None, # type: Optional[str] - py_version_info=None, # type: Optional[Tuple[int, ...]] - abi=None, # type: Optional[str] - implementation=None, # type: Optional[str] - ): - # type: (...) -> None + platforms: Optional[List[str]] = None, + py_version_info: Optional[Tuple[int, ...]] = None, + abis: Optional[List[str]] = None, + implementation: Optional[str] = None, + ) -> None: """ - :param platform: A string or None. If None, searches for packages - that are supported by the current system. Otherwise, will find - packages that can be built on the platform passed in. These + :param platforms: A list of strings or None. If None, searches for + packages that are supported by the current system. Otherwise, will + find packages that can be built on the platforms passed in. These packages will only be downloaded for distribution: they will not be built locally. :param py_version_info: An optional tuple of ints representing the Python version information to use (e.g. `sys.version_info[:3]`). This can have length 1, 2, or 3 when provided. - :param abi: A string or None. This is passed to compatibility_tags.py's - get_supported() function as is. + :param abis: A list of strings or None. This is passed to + compatibility_tags.py's get_supported() function as is. :param implementation: A string or None. This is passed to compatibility_tags.py's get_supported() function as is. """ @@ -50,41 +53,38 @@ class TargetPython(object): else: py_version_info = normalize_version_info(py_version_info) - py_version = '.'.join(map(str, py_version_info[:2])) + py_version = ".".join(map(str, py_version_info[:2])) - self.abi = abi + self.abis = abis self.implementation = implementation - self.platform = platform + self.platforms = platforms self.py_version = py_version self.py_version_info = py_version_info # This is used to cache the return value of get_tags(). - self._valid_tags = None # type: Optional[List[Tag]] + self._valid_tags: Optional[List[Tag]] = None - def format_given(self): - # type: () -> str + def format_given(self) -> str: """ Format the given, non-None attributes for display. """ display_version = None if self._given_py_version_info is not None: - display_version = '.'.join( + display_version = ".".join( str(part) for part in self._given_py_version_info ) key_values = [ - ('platform', self.platform), - ('version_info', display_version), - ('abi', self.abi), - ('implementation', self.implementation), + ("platforms", self.platforms), + ("version_info", display_version), + ("abis", self.abis), + ("implementation", self.implementation), ] - return ' '.join( - '{}={!r}'.format(key, value) for key, value in key_values - if value is not None + return " ".join( + f"{key}={value!r}" for key, value in key_values if value is not None ) - def get_tags(self): - # type: () -> List[Tag] + def get_tags(self) -> List[Tag]: """ Return the supported PEP 425 tags to check wheel candidates against. @@ -101,8 +101,8 @@ class TargetPython(object): tags = get_supported( version=version, - platform=self.platform, - abi=self.abi, + platforms=self.platforms, + abis=self.abis, impl=self.implementation, ) self._valid_tags = tags diff --git a/venv/Lib/site-packages/pip/_internal/models/wheel.py b/venv/Lib/site-packages/pip/_internal/models/wheel.py index 4d4068f3b737da1fb607812e6f170b5aa746baff..e09161227633c2a6108cf7f8c6b00f123f3f9984 100644 --- a/venv/Lib/site-packages/pip/_internal/models/wheel.py +++ b/venv/Lib/site-packages/pip/_internal/models/wheel.py @@ -2,59 +2,50 @@ name that have meaning. """ import re +from typing import Dict, Iterable, List from pip._vendor.packaging.tags import Tag from pip._internal.exceptions import InvalidWheelFilename -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import List - -class Wheel(object): +class Wheel: """A wheel file""" wheel_file_re = re.compile( r"""^(?P(?P.+?)-(?P.*?)) ((-(?P\d[^-]*?))?-(?P.+?)-(?P.+?)-(?P.+?) \.whl|\.dist-info)$""", - re.VERBOSE + re.VERBOSE, ) - def __init__(self, filename): - # type: (str) -> None + def __init__(self, filename: str) -> None: """ :raises InvalidWheelFilename: when the filename is invalid for a wheel """ wheel_info = self.wheel_file_re.match(filename) if not wheel_info: - raise InvalidWheelFilename( - "{} is not a valid wheel filename.".format(filename) - ) + raise InvalidWheelFilename(f"{filename} is not a valid wheel filename.") self.filename = filename - self.name = wheel_info.group('name').replace('_', '-') + self.name = wheel_info.group("name").replace("_", "-") # we'll assume "_" means "-" due to wheel naming scheme # (https://github.com/pypa/pip/issues/1150) - self.version = wheel_info.group('ver').replace('_', '-') - self.build_tag = wheel_info.group('build') - self.pyversions = wheel_info.group('pyver').split('.') - self.abis = wheel_info.group('abi').split('.') - self.plats = wheel_info.group('plat').split('.') + self.version = wheel_info.group("ver").replace("_", "-") + self.build_tag = wheel_info.group("build") + self.pyversions = wheel_info.group("pyver").split(".") + self.abis = wheel_info.group("abi").split(".") + self.plats = wheel_info.group("plat").split(".") # All the tag combinations from this file self.file_tags = { - Tag(x, y, z) for x in self.pyversions - for y in self.abis for z in self.plats + Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats } - def get_formatted_file_tags(self): - # type: () -> List[str] + def get_formatted_file_tags(self) -> List[str]: """Return the wheel's tags as a sorted list of strings.""" return sorted(str(tag) for tag in self.file_tags) - def support_index_min(self, tags): - # type: (List[Tag]) -> int + def support_index_min(self, tags: List[Tag]) -> int: """Return the lowest index that one of the wheel's file_tag combinations achieves in the given list of supported tags. @@ -69,8 +60,28 @@ class Wheel(object): """ return min(tags.index(tag) for tag in self.file_tags if tag in tags) - def supported(self, tags): - # type: (List[Tag]) -> bool + def find_most_preferred_tag( + self, tags: List[Tag], tag_to_priority: Dict[Tag, int] + ) -> int: + """Return the priority of the most preferred tag that one of the wheel's file + tag combinations achieves in the given list of supported tags using the given + tag_to_priority mapping, where lower priorities are more-preferred. + + This is used in place of support_index_min in some cases in order to avoid + an expensive linear scan of a large list of tags. + + :param tags: the PEP 425 tags to check the wheel against. + :param tag_to_priority: a mapping from tag to priority of that tag, where + lower is more preferred. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + return min( + tag_to_priority[tag] for tag in self.file_tags if tag in tag_to_priority + ) + + def supported(self, tags: Iterable[Tag]) -> bool: """Return whether the wheel is compatible with one of the given tags. :param tags: the PEP 425 tags to check the wheel against. diff --git a/venv/Lib/site-packages/pip/_internal/network/auth.py b/venv/Lib/site-packages/pip/_internal/network/auth.py index 94da3d46aaa0a7f3fd7111fb0662bd36d91a3e7a..ca42798bd952dfa10533e22a137e72dbd15250d4 100644 --- a/venv/Lib/site-packages/pip/_internal/network/auth.py +++ b/venv/Lib/site-packages/pip/_internal/network/auth.py @@ -4,15 +4,14 @@ Contains interface (MultiDomainBasicAuth) and associated glue code for providing credentials in the context of network requests. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -import logging +import urllib.parse +from typing import Any, Dict, List, Optional, Tuple from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.models import Request, Response from pip._vendor.requests.utils import get_netrc_auth -from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._internal.utils.logging import getLogger from pip._internal.utils.misc import ( ask, ask_input, @@ -20,31 +19,27 @@ from pip._internal.utils.misc import ( remove_auth_from_url, split_auth_netloc_from_url, ) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import Dict, Optional, Tuple +from pip._internal.vcs.versioncontrol import AuthInfo - from pip._internal.vcs.versioncontrol import AuthInfo +logger = getLogger(__name__) - Credentials = Tuple[str, str, str] - -logger = logging.getLogger(__name__) +Credentials = Tuple[str, str, str] try: - import keyring # noqa + import keyring except ImportError: - keyring = None + keyring = None # type: ignore[assignment] except Exception as exc: logger.warning( - "Keyring is skipped due to an exception: %s", str(exc), + "Keyring is skipped due to an exception: %s", + str(exc), ) - keyring = None + keyring = None # type: ignore[assignment] -def get_keyring_auth(url, username): +def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]: """Return the tuple auth for a given url from keyring.""" + global keyring if not url or not keyring: return None @@ -68,25 +63,28 @@ def get_keyring_auth(url, username): except Exception as exc: logger.warning( - "Keyring is skipped due to an exception: %s", str(exc), + "Keyring is skipped due to an exception: %s", + str(exc), ) + keyring = None # type: ignore[assignment] + return None class MultiDomainBasicAuth(AuthBase): - - def __init__(self, prompting=True, index_urls=None): - # type: (bool, Optional[Values]) -> None + def __init__( + self, prompting: bool = True, index_urls: Optional[List[str]] = None + ) -> None: self.prompting = prompting self.index_urls = index_urls - self.passwords = {} # type: Dict[str, AuthInfo] + self.passwords: Dict[str, AuthInfo] = {} # When the user is prompted to enter credentials and keyring is # available, we will offer to save them. If the user accepts, # this value is set to the credentials they entered. After the # request authenticates, the caller should call # ``save_credentials`` to save these. - self._credentials_to_save = None # type: Optional[Credentials] + self._credentials_to_save: Optional[Credentials] = None - def _get_index_url(self, url): + def _get_index_url(self, url: str) -> Optional[str]: """Return the original index URL matching the requested URL. Cached or dynamically generated credentials may work against @@ -106,9 +104,14 @@ class MultiDomainBasicAuth(AuthBase): prefix = remove_auth_from_url(u).rstrip("/") + "/" if url.startswith(prefix): return u + return None - def _get_new_credentials(self, original_url, allow_netrc=True, - allow_keyring=True): + def _get_new_credentials( + self, + original_url: str, + allow_netrc: bool = True, + allow_keyring: bool = False, + ) -> AuthInfo: """Find and return credentials for the specified URL.""" # Split the credentials and netloc from the url. url, netloc, url_user_password = split_auth_netloc_from_url( @@ -147,17 +150,21 @@ class MultiDomainBasicAuth(AuthBase): # If we don't have a password and keyring is available, use it. if allow_keyring: # The index url is more specific than the netloc, so try it first + # fmt: off kr_auth = ( get_keyring_auth(index_url, username) or get_keyring_auth(netloc, username) ) + # fmt: on if kr_auth: logger.debug("Found credentials in keyring for %s", netloc) return kr_auth return username, password - def _get_url_and_credentials(self, original_url): + def _get_url_and_credentials( + self, original_url: str + ) -> Tuple[str, Optional[str], Optional[str]]: """Return the credentials to use for the provided URL. If allowed, netrc and keyring may be used to obtain the @@ -169,13 +176,19 @@ class MultiDomainBasicAuth(AuthBase): """ url, netloc, _ = split_auth_netloc_from_url(original_url) - # Use any stored credentials that we have for this netloc - username, password = self.passwords.get(netloc, (None, None)) + # Try to get credentials from original url + username, password = self._get_new_credentials(original_url) - if username is None and password is None: - # No stored credentials. Acquire new credentials without prompting - # the user. (e.g. from netrc, keyring, or the URL itself) - username, password = self._get_new_credentials(original_url) + # If credentials not found, use any stored credentials for this netloc. + # Do this if either the username or the password is missing. + # This accounts for the situation in which the user has specified + # the username in the index url, but the password comes from keyring. + if (username is None or password is None) and netloc in self.passwords: + un, pw = self.passwords[netloc] + # It is possible that the cached credentials are for a different username, + # in which case the cache should be ignored. + if username is None or username == un: + username, password = un, pw if username is not None or password is not None: # Convert the username and password if they're None, so that @@ -190,14 +203,14 @@ class MultiDomainBasicAuth(AuthBase): assert ( # Credentials were found - (username is not None and password is not None) or + (username is not None and password is not None) # Credentials were not found - (username is None and password is None) - ), "Could not load credentials from url: {}".format(original_url) + or (username is None and password is None) + ), f"Could not load credentials from url: {original_url}" return url, username, password - def __call__(self, req): + def __call__(self, req: Request) -> Request: # Get credentials for this request url, username, password = self._get_url_and_credentials(req.url) @@ -214,23 +227,25 @@ class MultiDomainBasicAuth(AuthBase): return req # Factored out to allow for easy patching in tests - def _prompt_for_password(self, netloc): - username = ask_input("User for {}: ".format(netloc)) + def _prompt_for_password( + self, netloc: str + ) -> Tuple[Optional[str], Optional[str], bool]: + username = ask_input(f"User for {netloc}: ") if not username: - return None, None + return None, None, False auth = get_keyring_auth(netloc, username) - if auth: + if auth and auth[0] is not None and auth[1] is not None: return auth[0], auth[1], False password = ask_password("Password: ") return username, password, True # Factored out to allow for easy patching in tests - def _should_save_password_to_keyring(self): + def _should_save_password_to_keyring(self) -> bool: if not keyring: return False return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" - def handle_401(self, resp, **kwargs): + def handle_401(self, resp: Response, **kwargs: Any) -> Response: # We only care about 401 responses, anything else we want to just # pass through the actual response if resp.status_code != 401: @@ -240,10 +255,19 @@ class MultiDomainBasicAuth(AuthBase): if not self.prompting: return resp - parsed = urllib_parse.urlparse(resp.url) + parsed = urllib.parse.urlparse(resp.url) + + # Query the keyring for credentials: + username, password = self._get_new_credentials( + resp.url, + allow_netrc=False, + allow_keyring=True, + ) # Prompt the user for a new username and password - username, password, save = self._prompt_for_password(parsed.netloc) + save = False + if not username and not password: + username, password, save = self._prompt_for_password(parsed.netloc) # Store the new username and password to use for future requests self._credentials_to_save = None @@ -275,14 +299,15 @@ class MultiDomainBasicAuth(AuthBase): return new_resp - def warn_on_401(self, resp, **kwargs): + def warn_on_401(self, resp: Response, **kwargs: Any) -> None: """Response callback to warn about incorrect credentials.""" if resp.status_code == 401: logger.warning( - '401 Error, Credentials not correct for %s', resp.request.url, + "401 Error, Credentials not correct for %s", + resp.request.url, ) - def save_credentials(self, resp, **kwargs): + def save_credentials(self, resp: Response, **kwargs: Any) -> None: """Response callback to save credentials on success.""" assert keyring is not None, "should never reach here without keyring" if not keyring: @@ -292,7 +317,7 @@ class MultiDomainBasicAuth(AuthBase): self._credentials_to_save = None if creds and resp.status_code < 400: try: - logger.info('Saving credentials to keyring') + logger.info("Saving credentials to keyring") keyring.set_password(*creds) except Exception: - logger.exception('Failed to save credentials') + logger.exception("Failed to save credentials") diff --git a/venv/Lib/site-packages/pip/_internal/network/cache.py b/venv/Lib/site-packages/pip/_internal/network/cache.py index c9386e173600d58dacda2061f49d747de386a50a..2d915e6fcecbf920e3ab43f7a2eab667b6736ce5 100644 --- a/venv/Lib/site-packages/pip/_internal/network/cache.py +++ b/venv/Lib/site-packages/pip/_internal/network/cache.py @@ -1,11 +1,9 @@ """HTTP cache implementation. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import os from contextlib import contextmanager +from typing import Iterator, Optional from pip._vendor.cachecontrol.cache import BaseCache from pip._vendor.cachecontrol.caches import FileCache @@ -13,25 +11,20 @@ from pip._vendor.requests.models import Response from pip._internal.utils.filesystem import adjacent_tmp_file, replace from pip._internal.utils.misc import ensure_dir -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Optional -def is_from_cache(response): - # type: (Response) -> bool +def is_from_cache(response: Response) -> bool: return getattr(response, "from_cache", False) @contextmanager -def suppressed_cache_errors(): +def suppressed_cache_errors() -> Iterator[None]: """If we can't access the cache then we can just skip caching and process requests as if caching wasn't enabled. """ try: yield - except (OSError, IOError): + except OSError: pass @@ -41,14 +34,12 @@ class SafeFileCache(BaseCache): not be accessible or writable. """ - def __init__(self, directory): - # type: (str) -> None + def __init__(self, directory: str) -> None: assert directory is not None, "Cache directory must not be None." - super(SafeFileCache, self).__init__() + super().__init__() self.directory = directory - def _get_cache_path(self, name): - # type: (str) -> str + def _get_cache_path(self, name: str) -> str: # From cachecontrol.caches.file_cache.FileCache._fn, brought into our # class for backwards-compatibility and to avoid using a non-public # method. @@ -56,15 +47,13 @@ class SafeFileCache(BaseCache): parts = list(hashed[:5]) + [hashed] return os.path.join(self.directory, *parts) - def get(self, key): - # type: (str) -> Optional[bytes] + def get(self, key: str) -> Optional[bytes]: path = self._get_cache_path(key) with suppressed_cache_errors(): - with open(path, 'rb') as f: + with open(path, "rb") as f: return f.read() - def set(self, key, value): - # type: (str, bytes) -> None + def set(self, key: str, value: bytes) -> None: path = self._get_cache_path(key) with suppressed_cache_errors(): ensure_dir(os.path.dirname(path)) @@ -74,8 +63,7 @@ class SafeFileCache(BaseCache): replace(f.name, path) - def delete(self, key): - # type: (str) -> None + def delete(self, key: str) -> None: path = self._get_cache_path(key) with suppressed_cache_errors(): os.remove(path) diff --git a/venv/Lib/site-packages/pip/_internal/network/download.py b/venv/Lib/site-packages/pip/_internal/network/download.py index 2f3e08ae62ee8f051441e749c77a1b0e79cdb00a..47af547d6fcc6a947d7bde12be92daeb08075722 100644 --- a/venv/Lib/site-packages/pip/_internal/network/download.py +++ b/venv/Lib/site-packages/pip/_internal/network/download.py @@ -4,46 +4,34 @@ import cgi import logging import mimetypes import os +from typing import Iterable, Optional, Tuple -from pip._vendor import requests -from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response from pip._internal.cli.progress_bars import DownloadProgressProvider +from pip._internal.exceptions import NetworkConnectionError from pip._internal.models.index import PyPI +from pip._internal.models.link import Link from pip._internal.network.cache import is_from_cache -from pip._internal.network.utils import response_chunks -from pip._internal.utils.misc import ( - format_size, - redact_auth_from_url, - splitext, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Iterable, Optional - - from pip._vendor.requests.models import Response - - from pip._internal.models.link import Link - from pip._internal.network.session import PipSession +from pip._internal.network.session import PipSession +from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks +from pip._internal.utils.misc import format_size, redact_auth_from_url, splitext logger = logging.getLogger(__name__) -def _get_http_response_size(resp): - # type: (Response) -> Optional[int] +def _get_http_response_size(resp: Response) -> Optional[int]: try: - return int(resp.headers['content-length']) + return int(resp.headers["content-length"]) except (ValueError, KeyError, TypeError): return None def _prepare_download( - resp, # type: Response - link, # type: Link - progress_bar # type: str -): - # type: (...) -> Iterable[bytes] + resp: Response, + link: Link, + progress_bar: str, +) -> Iterable[bytes]: total_length = _get_http_response_size(resp) if link.netloc == PyPI.file_storage_domain: @@ -54,7 +42,7 @@ def _prepare_download( logged_url = redact_auth_from_url(url) if total_length: - logged_url = '{} ({})'.format(logged_url, format_size(total_length)) + logged_url = "{} ({})".format(logged_url, format_size(total_length)) if is_from_cache(resp): logger.info("Using cached %s", logged_url) @@ -77,27 +65,23 @@ def _prepare_download( if not show_progress: return chunks - return DownloadProgressProvider( - progress_bar, max=total_length - )(chunks) + return DownloadProgressProvider(progress_bar, max=total_length)(chunks) -def sanitize_content_filename(filename): - # type: (str) -> str +def sanitize_content_filename(filename: str) -> str: """ Sanitize the "filename" value from a Content-Disposition header. """ return os.path.basename(filename) -def parse_content_disposition(content_disposition, default_filename): - # type: (str, str) -> str +def parse_content_disposition(content_disposition: str, default_filename: str) -> str: """ Parse the "filename" value from a Content-Disposition header, and return the default filename if the result is empty. """ _type, params = cgi.parse_header(content_disposition) - filename = params.get('filename') + filename = params.get("filename") if filename: # We need to sanitize the filename to prevent directory traversal # in case the filename contains ".." path parts. @@ -105,21 +89,18 @@ def parse_content_disposition(content_disposition, default_filename): return filename or default_filename -def _get_http_response_filename(resp, link): - # type: (Response, Link) -> str +def _get_http_response_filename(resp: Response, link: Link) -> str: """Get an ideal filename from the given HTTP response, falling back to the link filename if not provided. """ filename = link.filename # fallback # Have a look at the Content-Disposition header for a better guess - content_disposition = resp.headers.get('content-disposition') + content_disposition = resp.headers.get("content-disposition") if content_disposition: filename = parse_content_disposition(content_disposition, filename) - ext = splitext(filename)[1] # type: Optional[str] + ext: Optional[str] = splitext(filename)[1] if not ext: - ext = mimetypes.guess_extension( - resp.headers.get('content-type', '') - ) + ext = mimetypes.guess_extension(resp.headers.get("content-type", "")) if ext: filename += ext if not ext and link.url != resp.url: @@ -129,72 +110,75 @@ def _get_http_response_filename(resp, link): return filename -def _http_get_download(session, link): - # type: (PipSession, Link) -> Response - target_url = link.url.split('#', 1)[0] - resp = session.get( - target_url, - # We use Accept-Encoding: identity here because requests - # defaults to accepting compressed responses. This breaks in - # a variety of ways depending on how the server is configured. - # - Some servers will notice that the file isn't a compressible - # file and will leave the file alone and with an empty - # Content-Encoding - # - Some servers will notice that the file is already - # compressed and will leave the file alone and will add a - # Content-Encoding: gzip header - # - Some servers won't notice anything at all and will take - # a file that's already been compressed and compress it again - # and set the Content-Encoding: gzip header - # By setting this to request only the identity encoding We're - # hoping to eliminate the third case. Hopefully there does not - # exist a server which when given a file will notice it is - # already compressed and that you're not asking for a - # compressed file and will then decompress it before sending - # because if that's the case I don't think it'll ever be - # possible to make this work. - headers={"Accept-Encoding": "identity"}, - stream=True, - ) - resp.raise_for_status() +def _http_get_download(session: PipSession, link: Link) -> Response: + target_url = link.url.split("#", 1)[0] + resp = session.get(target_url, headers=HEADERS, stream=True) + raise_for_status(resp) return resp -class Download(object): - def __init__( - self, - response, # type: Response - filename, # type: str - chunks, # type: Iterable[bytes] - ): - # type: (...) -> None - self.response = response - self.filename = filename - self.chunks = chunks - - -class Downloader(object): +class Downloader: def __init__( self, - session, # type: PipSession - progress_bar, # type: str - ): - # type: (...) -> None + session: PipSession, + progress_bar: str, + ) -> None: self._session = session self._progress_bar = progress_bar - def __call__(self, link): - # type: (Link) -> Download + def __call__(self, link: Link, location: str) -> Tuple[str, str]: + """Download the file given by link into location.""" try: resp = _http_get_download(self._session, link) - except requests.HTTPError as e: + except NetworkConnectionError as e: + assert e.response is not None logger.critical( "HTTP error %s while getting %s", e.response.status_code, link ) raise - return Download( - resp, - _get_http_response_filename(resp, link), - _prepare_download(resp, link, self._progress_bar), - ) + filename = _get_http_response_filename(resp, link) + filepath = os.path.join(location, filename) + + chunks = _prepare_download(resp, link, self._progress_bar) + with open(filepath, "wb") as content_file: + for chunk in chunks: + content_file.write(chunk) + content_type = resp.headers.get("Content-Type", "") + return filepath, content_type + + +class BatchDownloader: + def __init__( + self, + session: PipSession, + progress_bar: str, + ) -> None: + self._session = session + self._progress_bar = progress_bar + + def __call__( + self, links: Iterable[Link], location: str + ) -> Iterable[Tuple[Link, Tuple[str, str]]]: + """Download the files given by links into location.""" + for link in links: + try: + resp = _http_get_download(self._session, link) + except NetworkConnectionError as e: + assert e.response is not None + logger.critical( + "HTTP error %s while getting %s", + e.response.status_code, + link, + ) + raise + + filename = _get_http_response_filename(resp, link) + filepath = os.path.join(location, filename) + + chunks = _prepare_download(resp, link, self._progress_bar) + with open(filepath, "wb") as content_file: + for chunk in chunks: + content_file.write(chunk) + content_type = resp.headers.get("Content-Type", "") + yield link, (filepath, content_type) diff --git a/venv/Lib/site-packages/pip/_internal/network/session.py b/venv/Lib/site-packages/pip/_internal/network/session.py index 39a4a546edc8bc5c1a0cb427db4eae759de5fb52..cbe743ba6a1636f5ea7fb74c89d36dabb54b502b 100644 --- a/venv/Lib/site-packages/pip/_internal/network/session.py +++ b/venv/Lib/site-packages/pip/_internal/network/session.py @@ -2,58 +2,51 @@ network request configuration and behavior. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import email.utils +import io +import ipaddress import json import logging import mimetypes import os import platform +import shutil +import subprocess import sys +import urllib.parse import warnings +from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple, Union -from pip._vendor import requests, six, urllib3 +from pip._vendor import requests, urllib3 from pip._vendor.cachecontrol import CacheControlAdapter from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter -from pip._vendor.requests.models import Response +from pip._vendor.requests.models import PreparedRequest, Response from pip._vendor.requests.structures import CaseInsensitiveDict -from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.urllib3.connectionpool import ConnectionPool from pip._vendor.urllib3.exceptions import InsecureRequestWarning from pip import __version__ +from pip._internal.metadata import get_default_environment +from pip._internal.models.link import Link from pip._internal.network.auth import MultiDomainBasicAuth from pip._internal.network.cache import SafeFileCache + # Import ssl from compat so the initial import occurs in only one place. -from pip._internal.utils.compat import has_tls, ipaddress +from pip._internal.utils.compat import has_tls from pip._internal.utils.glibc import libc_ver -from pip._internal.utils.misc import ( - build_url_from_netloc, - get_installed_version, - parse_netloc, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.misc import build_url_from_netloc, parse_netloc from pip._internal.utils.urls import url_to_path -if MYPY_CHECK_RUNNING: - from typing import ( - Iterator, List, Optional, Tuple, Union, - ) - - from pip._internal.models.link import Link - - SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] - - logger = logging.getLogger(__name__) +SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] + # Ignore warning raised when using --trusted-host. warnings.filterwarnings("ignore", category=InsecureRequestWarning) -SECURE_ORIGINS = [ +SECURE_ORIGINS: List[SecureOrigin] = [ # protocol, hostname, port # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) ("https", "*", "*"), @@ -63,7 +56,7 @@ SECURE_ORIGINS = [ ("file", "*", None), # ssh is always secure. ("ssh", "*", "*"), -] # type: List[SecureOrigin] +] # These are environment variables present when running under various @@ -75,18 +68,17 @@ SECURE_ORIGINS = [ # For more background, see: https://github.com/pypa/pip/issues/5499 CI_ENVIRONMENT_VARIABLES = ( # Azure Pipelines - 'BUILD_BUILDID', + "BUILD_BUILDID", # Jenkins - 'BUILD_ID', + "BUILD_ID", # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI - 'CI', + "CI", # Explicit environment variable. - 'PIP_IS_CI', + "PIP_IS_CI", ) -def looks_like_ci(): - # type: () -> bool +def looks_like_ci() -> bool: """ Return whether it looks like pip is running under CI. """ @@ -96,11 +88,11 @@ def looks_like_ci(): return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES) -def user_agent(): +def user_agent() -> str: """ Return a string representing the user agent. """ - data = { + data: Dict[str, Any] = { "installer": {"name": "pip", "version": __version__}, "python": platform.python_version(), "implementation": { @@ -108,33 +100,38 @@ def user_agent(): }, } - if data["implementation"]["name"] == 'CPython': + if data["implementation"]["name"] == "CPython": data["implementation"]["version"] = platform.python_version() - elif data["implementation"]["name"] == 'PyPy': - if sys.pypy_version_info.releaselevel == 'final': - pypy_version_info = sys.pypy_version_info[:3] - else: - pypy_version_info = sys.pypy_version_info + elif data["implementation"]["name"] == "PyPy": + pypy_version_info = sys.pypy_version_info # type: ignore + if pypy_version_info.releaselevel == "final": + pypy_version_info = pypy_version_info[:3] data["implementation"]["version"] = ".".join( [str(x) for x in pypy_version_info] ) - elif data["implementation"]["name"] == 'Jython': + elif data["implementation"]["name"] == "Jython": # Complete Guess data["implementation"]["version"] = platform.python_version() - elif data["implementation"]["name"] == 'IronPython': + elif data["implementation"]["name"] == "IronPython": # Complete Guess data["implementation"]["version"] = platform.python_version() if sys.platform.startswith("linux"): from pip._vendor import distro - distro_infos = dict(filter( - lambda x: x[1], - zip(["name", "version", "id"], distro.linux_distribution()), - )) - libc = dict(filter( - lambda x: x[1], - zip(["lib", "version"], libc_ver()), - )) + + linux_distribution = distro.name(), distro.version(), distro.codename() + distro_infos: Dict[str, Any] = dict( + filter( + lambda x: x[1], + zip(["name", "version", "id"], linux_distribution), + ) + ) + libc = dict( + filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + ) + ) if libc: distro_infos["libc"] = libc if distro_infos: @@ -154,11 +151,27 @@ def user_agent(): if has_tls(): import _ssl as ssl + data["openssl_version"] = ssl.OPENSSL_VERSION - setuptools_version = get_installed_version("setuptools") - if setuptools_version is not None: - data["setuptools_version"] = setuptools_version + setuptools_dist = get_default_environment().get_distribution("setuptools") + if setuptools_dist is not None: + data["setuptools_version"] = str(setuptools_dist.version) + + if shutil.which("rustc") is not None: + # If for any reason `rustc --version` fails, silently ignore it + try: + rustc_output = subprocess.check_output( + ["rustc", "--version"], stderr=subprocess.STDOUT, timeout=0.5 + ) + except Exception: + pass + else: + if rustc_output.startswith(b"rustc "): + # The format of `rustc --version` is: + # `b'rustc 1.52.1 (9bc8c42bb 2021-05-09)\n'` + # We extract just the middle (1.52.1) part + data["rustc_version"] = rustc_output.split(b" ")[1].decode() # Use None rather than False so as not to give the impression that # pip knows it is not being run under CI. Rather, it is a null or @@ -177,9 +190,15 @@ def user_agent(): class LocalFSAdapter(BaseAdapter): - - def send(self, request, stream=None, timeout=None, verify=None, cert=None, - proxies=None): + def send( + self, + request: PreparedRequest, + stream: bool = False, + timeout: Optional[Union[float, Tuple[float, float]]] = None, + verify: Union[bool, str] = True, + cert: Optional[Union[str, Tuple[str, str]]] = None, + proxies: Optional[Mapping[str, str]] = None, + ) -> Response: pathname = url_to_path(request.url) resp = Response() @@ -189,61 +208,75 @@ class LocalFSAdapter(BaseAdapter): try: stats = os.stat(pathname) except OSError as exc: + # format the exception raised as a io.BytesIO object, + # to return a better error message: resp.status_code = 404 - resp.raw = exc + resp.reason = type(exc).__name__ + resp.raw = io.BytesIO(f"{resp.reason}: {exc}".encode("utf8")) else: modified = email.utils.formatdate(stats.st_mtime, usegmt=True) content_type = mimetypes.guess_type(pathname)[0] or "text/plain" - resp.headers = CaseInsensitiveDict({ - "Content-Type": content_type, - "Content-Length": stats.st_size, - "Last-Modified": modified, - }) + resp.headers = CaseInsensitiveDict( + { + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + } + ) resp.raw = open(pathname, "rb") resp.close = resp.raw.close return resp - def close(self): + def close(self) -> None: pass class InsecureHTTPAdapter(HTTPAdapter): - - def cert_verify(self, conn, url, verify, cert): - super(InsecureHTTPAdapter, self).cert_verify( - conn=conn, url=url, verify=False, cert=cert - ) + def cert_verify( + self, + conn: ConnectionPool, + url: str, + verify: Union[bool, str], + cert: Optional[Union[str, Tuple[str, str]]], + ) -> None: + super().cert_verify(conn=conn, url=url, verify=False, cert=cert) class InsecureCacheControlAdapter(CacheControlAdapter): - - def cert_verify(self, conn, url, verify, cert): - super(InsecureCacheControlAdapter, self).cert_verify( - conn=conn, url=url, verify=False, cert=cert - ) + def cert_verify( + self, + conn: ConnectionPool, + url: str, + verify: Union[bool, str], + cert: Optional[Union[str, Tuple[str, str]]], + ) -> None: + super().cert_verify(conn=conn, url=url, verify=False, cert=cert) class PipSession(requests.Session): - timeout = None # type: Optional[int] - - def __init__(self, *args, **kwargs): + timeout: Optional[int] = None + + def __init__( + self, + *args: Any, + retries: int = 0, + cache: Optional[str] = None, + trusted_hosts: Sequence[str] = (), + index_urls: Optional[List[str]] = None, + **kwargs: Any, + ) -> None: """ :param trusted_hosts: Domains not to emit warnings for when not using HTTPS. """ - retries = kwargs.pop("retries", 0) - cache = kwargs.pop("cache", None) - trusted_hosts = kwargs.pop("trusted_hosts", []) # type: List[str] - index_urls = kwargs.pop("index_urls", None) - - super(PipSession, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Namespace the attribute with "pip_" just in case to prevent # possible conflicts with the base class. - self.pip_trusted_origins = [] # type: List[Tuple[str, Optional[int]]] + self.pip_trusted_origins: List[Tuple[str, Optional[int]]] = [] # Attach our User Agent to the request self.headers["User-Agent"] = user_agent() @@ -257,7 +290,6 @@ class PipSession(requests.Session): # Set the total number of retries that a particular request can # have. total=retries, - # A 503 error from PyPI typically means that the Fastly -> Origin # connection got interrupted in some way. A 503 error in general # is typically considered a transient error so we'll go ahead and @@ -265,11 +297,10 @@ class PipSession(requests.Session): # A 500 may indicate transient error in Amazon S3 # A 520 or 527 - may indicate transient error in CloudFlare status_forcelist=[500, 503, 520, 527], - # Add a small amount of back off between failed requests in # order to prevent hammering the service. backoff_factor=0.25, - ) + ) # type: ignore # Our Insecure HTTPAdapter disables HTTPS validation. It does not # support caching so we'll use it for all http:// URLs. @@ -305,8 +336,16 @@ class PipSession(requests.Session): for host in trusted_hosts: self.add_trusted_host(host, suppress_logging=True) - def add_trusted_host(self, host, source=None, suppress_logging=False): - # type: (str, Optional[str], bool) -> None + def update_index_urls(self, new_index_urls: List[str]) -> None: + """ + :param new_index_urls: New index urls to update the authentication + handler with. + """ + self.auth.index_urls = new_index_urls + + def add_trusted_host( + self, host: str, source: Optional[str] = None, suppress_logging: bool = False + ) -> None: """ :param host: It is okay to provide a host that has previously been added. @@ -314,9 +353,9 @@ class PipSession(requests.Session): string came from. """ if not suppress_logging: - msg = 'adding trusted host: {!r}'.format(host) + msg = f"adding trusted host: {host!r}" if source is not None: - msg += ' (from {})'.format(source) + msg += f" (from {source})" logger.info(msg) host_port = parse_netloc(host) @@ -324,36 +363,36 @@ class PipSession(requests.Session): self.pip_trusted_origins.append(host_port) self.mount( - build_url_from_netloc(host) + '/', - self._trusted_host_adapter + build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter ) + self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter) if not host_port[1]: - # Mount wildcard ports for the same host. self.mount( - build_url_from_netloc(host) + ':', - self._trusted_host_adapter + build_url_from_netloc(host, scheme="http") + ":", + self._trusted_host_adapter, ) + # Mount wildcard ports for the same host. + self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter) - def iter_secure_origins(self): - # type: () -> Iterator[SecureOrigin] - for secure_origin in SECURE_ORIGINS: - yield secure_origin + def iter_secure_origins(self) -> Iterator[SecureOrigin]: + yield from SECURE_ORIGINS for host, port in self.pip_trusted_origins: - yield ('*', host, '*' if port is None else port) + yield ("*", host, "*" if port is None else port) - def is_secure_origin(self, location): - # type: (Link) -> bool + def is_secure_origin(self, location: Link) -> bool: # Determine if this url used a secure transport mechanism - parsed = urllib_parse.urlparse(str(location)) + parsed = urllib.parse.urlparse(str(location)) origin_protocol, origin_host, origin_port = ( - parsed.scheme, parsed.hostname, parsed.port, + parsed.scheme, + parsed.hostname, + parsed.port, ) # The protocol to use to see if the protocol matches. # Don't count the repository type as part of the protocol: in # cases such as "git+ssh", only use "ssh". (I.e., Only verify against # the last scheme.) - origin_protocol = origin_protocol.rsplit('+', 1)[-1] + origin_protocol = origin_protocol.rsplit("+", 1)[-1] # Determine if our origin is a secure origin by looking through our # hardcoded list of secure origins, as well as any additional ones @@ -364,21 +403,15 @@ class PipSession(requests.Session): continue try: - addr = ipaddress.ip_address( - None - if origin_host is None - else six.ensure_text(origin_host) - ) - network = ipaddress.ip_network( - six.ensure_text(secure_host) - ) + addr = ipaddress.ip_address(origin_host) + network = ipaddress.ip_network(secure_host) except ValueError: # We don't have both a valid address or a valid network, so # we'll check this origin against hostnames. if ( - origin_host and - origin_host.lower() != secure_host.lower() and - secure_host != "*" + origin_host + and origin_host.lower() != secure_host.lower() + and secure_host != "*" ): continue else: @@ -389,9 +422,9 @@ class PipSession(requests.Session): # Check to see if the port matches. if ( - origin_port != secure_port and - secure_port != "*" and - secure_port is not None + origin_port != secure_port + and secure_port != "*" + and secure_port is not None ): continue @@ -413,9 +446,9 @@ class PipSession(requests.Session): return False - def request(self, method, url, *args, **kwargs): + def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response: # Allow setting a default timeout on a session kwargs.setdefault("timeout", self.timeout) # Dispatch the actual request - return super(PipSession, self).request(method, url, *args, **kwargs) + return super().request(method, url, *args, **kwargs) diff --git a/venv/Lib/site-packages/pip/_internal/network/utils.py b/venv/Lib/site-packages/pip/_internal/network/utils.py index a19050b0f7082809f277bc74e516a9af8e537136..094cf1b4a97378c669f3440566e532fa8ef4535c 100644 --- a/venv/Lib/site-packages/pip/_internal/network/utils.py +++ b/venv/Lib/site-packages/pip/_internal/network/utils.py @@ -1,15 +1,63 @@ +from typing import Dict, Iterator + from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.exceptions import NetworkConnectionError + +# The following comments and HTTP headers were originally added by +# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03. +# +# We use Accept-Encoding: identity here because requests defaults to +# accepting compressed responses. This breaks in a variety of ways +# depending on how the server is configured. +# - Some servers will notice that the file isn't a compressible file +# and will leave the file alone and with an empty Content-Encoding +# - Some servers will notice that the file is already compressed and +# will leave the file alone, adding a Content-Encoding: gzip header +# - Some servers won't notice anything at all and will take a file +# that's already been compressed and compress it again, and set +# the Content-Encoding: gzip header +# By setting this to request only the identity encoding we're hoping +# to eliminate the third case. Hopefully there does not exist a server +# which when given a file will notice it is already compressed and that +# you're not asking for a compressed file and will then decompress it +# before sending because if that's the case I don't think it'll ever be +# possible to make this work. +HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"} + + +def raise_for_status(resp: Response) -> None: + http_error_msg = "" + if isinstance(resp.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. + try: + reason = resp.reason.decode("utf-8") + except UnicodeDecodeError: + reason = resp.reason.decode("iso-8859-1") + else: + reason = resp.reason + + if 400 <= resp.status_code < 500: + http_error_msg = ( + f"{resp.status_code} Client Error: {reason} for url: {resp.url}" + ) + + elif 500 <= resp.status_code < 600: + http_error_msg = ( + f"{resp.status_code} Server Error: {reason} for url: {resp.url}" + ) -if MYPY_CHECK_RUNNING: - from typing import Iterator + if http_error_msg: + raise NetworkConnectionError(http_error_msg, response=resp) -def response_chunks(response, chunk_size=CONTENT_CHUNK_SIZE): - # type: (Response, int) -> Iterator[bytes] - """Given a requests Response, provide the data chunks. - """ +def response_chunks( + response: Response, chunk_size: int = CONTENT_CHUNK_SIZE +) -> Iterator[bytes]: + """Given a requests Response, provide the data chunks.""" try: # Special case for urllib3. for chunk in response.raw.stream( diff --git a/venv/Lib/site-packages/pip/_internal/network/xmlrpc.py b/venv/Lib/site-packages/pip/_internal/network/xmlrpc.py index 121edd93056f57c7717e6e48e2d7432cfc18ada4..4a7d55d0e50cb8b892caa021695522e5ddd54a17 100644 --- a/venv/Lib/site-packages/pip/_internal/network/xmlrpc.py +++ b/venv/Lib/site-packages/pip/_internal/network/xmlrpc.py @@ -1,44 +1,60 @@ """xmlrpclib.Transport implementation """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - import logging +import urllib.parse +import xmlrpc.client +from typing import TYPE_CHECKING, Tuple + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status -from pip._vendor import requests -# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is -# why we ignore the type on this import -from pip._vendor.six.moves import xmlrpc_client # type: ignore -from pip._vendor.six.moves.urllib import parse as urllib_parse +if TYPE_CHECKING: + from xmlrpc.client import _HostType, _Marshallable logger = logging.getLogger(__name__) -class PipXmlrpcTransport(xmlrpc_client.Transport): +class PipXmlrpcTransport(xmlrpc.client.Transport): """Provide a `xmlrpclib.Transport` implementation via a `PipSession` object. """ - def __init__(self, index_url, session, use_datetime=False): - xmlrpc_client.Transport.__init__(self, use_datetime) - index_parts = urllib_parse.urlparse(index_url) + def __init__( + self, index_url: str, session: PipSession, use_datetime: bool = False + ) -> None: + super().__init__(use_datetime) + index_parts = urllib.parse.urlparse(index_url) self._scheme = index_parts.scheme self._session = session - def request(self, host, handler, request_body, verbose=False): + def request( + self, + host: "_HostType", + handler: str, + request_body: bytes, + verbose: bool = False, + ) -> Tuple["_Marshallable", ...]: + assert isinstance(host, str) parts = (self._scheme, host, handler, None, None, None) - url = urllib_parse.urlunparse(parts) + url = urllib.parse.urlunparse(parts) try: - headers = {'Content-Type': 'text/xml'} - response = self._session.post(url, data=request_body, - headers=headers, stream=True) - response.raise_for_status() + headers = {"Content-Type": "text/xml"} + response = self._session.post( + url, + data=request_body, + headers=headers, + stream=True, + ) + raise_for_status(response) self.verbose = verbose return self.parse_response(response.raw) - except requests.HTTPError as exc: + except NetworkConnectionError as exc: + assert exc.response logger.critical( "HTTP error %s while getting %s", - exc.response.status_code, url, + exc.response.status_code, + url, ) raise diff --git a/venv/Lib/site-packages/pip/_internal/operations/build/metadata.py b/venv/Lib/site-packages/pip/_internal/operations/build/metadata.py index b13fbdef93357da3d1b3b0303b49a28990736256..7d12438d6ed08c527a4a9ef06bcce1bf8c2a840c 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/build/metadata.py +++ b/venv/Lib/site-packages/pip/_internal/operations/build/metadata.py @@ -1,29 +1,21 @@ """Metadata generation logic for source distributions. """ -import logging import os +from pip._vendor.pep517.wrappers import Pep517HookCaller + +from pip._internal.build_env import BuildEnvironment from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from pip._internal.build_env import BuildEnvironment - from pip._vendor.pep517.wrappers import Pep517HookCaller - -logger = logging.getLogger(__name__) -def generate_metadata(build_env, backend): - # type: (BuildEnvironment, Pep517HookCaller) -> str +def generate_metadata(build_env: BuildEnvironment, backend: Pep517HookCaller) -> str: """Generate metadata using mechanisms described in PEP 517. Returns the generated metadata directory. """ - metadata_tmpdir = TempDirectory( - kind="modern-metadata", globally_managed=True - ) + metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True) metadata_dir = metadata_tmpdir.path @@ -31,10 +23,8 @@ def generate_metadata(build_env, backend): # Note that Pep517HookCaller implements a fallback for # prepare_metadata_for_build_wheel, so we don't have to # consider the possibility that this hook doesn't exist. - runner = runner_with_spinner_message("Preparing wheel metadata") + runner = runner_with_spinner_message("Preparing metadata (pyproject.toml)") with backend.subprocess_runner(runner): - distinfo_dir = backend.prepare_metadata_for_build_wheel( - metadata_dir - ) + distinfo_dir = backend.prepare_metadata_for_build_wheel(metadata_dir) return os.path.join(metadata_dir, distinfo_dir) diff --git a/venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py b/venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py index 14762aef3c0037ed0cd5cd8cc9331bbccfaf7c97..ff52de9c4cfcb65aa204104b658b86bf142606d3 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py +++ b/venv/Lib/site-packages/pip/_internal/operations/build/metadata_legacy.py @@ -4,61 +4,49 @@ import logging import os +from pip._internal.build_env import BuildEnvironment +from pip._internal.cli.spinners import open_spinner from pip._internal.exceptions import InstallationError from pip._internal.utils.setuptools_build import make_setuptools_egg_info_args from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from pip._internal.build_env import BuildEnvironment logger = logging.getLogger(__name__) -def _find_egg_info(directory): - # type: (str) -> str - """Find an .egg-info subdirectory in `directory`. - """ - filenames = [ - f for f in os.listdir(directory) if f.endswith(".egg-info") - ] +def _find_egg_info(directory: str) -> str: + """Find an .egg-info subdirectory in `directory`.""" + filenames = [f for f in os.listdir(directory) if f.endswith(".egg-info")] if not filenames: - raise InstallationError( - "No .egg-info directory found in {}".format(directory) - ) + raise InstallationError(f"No .egg-info directory found in {directory}") if len(filenames) > 1: raise InstallationError( - "More than one .egg-info directory found in {}".format( - directory - ) + "More than one .egg-info directory found in {}".format(directory) ) return os.path.join(directory, filenames[0]) def generate_metadata( - build_env, # type: BuildEnvironment - setup_py_path, # type: str - source_dir, # type: str - isolated, # type: bool - details, # type: str -): - # type: (...) -> str + build_env: BuildEnvironment, + setup_py_path: str, + source_dir: str, + isolated: bool, + details: str, +) -> str: """Generate metadata using setup.py-based defacto mechanisms. Returns the generated metadata directory. """ logger.debug( - 'Running setup.py (path:%s) egg_info for package %s', - setup_py_path, details, + "Running setup.py (path:%s) egg_info for package %s", + setup_py_path, + details, ) - egg_info_dir = TempDirectory( - kind="pip-egg-info", globally_managed=True - ).path + egg_info_dir = TempDirectory(kind="pip-egg-info", globally_managed=True).path args = make_setuptools_egg_info_args( setup_py_path, @@ -67,11 +55,13 @@ def generate_metadata( ) with build_env: - call_subprocess( - args, - cwd=source_dir, - command_desc='python setup.py egg_info', - ) + with open_spinner("Preparing metadata (setup.py)") as spinner: + call_subprocess( + args, + cwd=source_dir, + command_desc="python setup.py egg_info", + spinner=spinner, + ) # Return the .egg-info directory. return _find_egg_info(egg_info_dir) diff --git a/venv/Lib/site-packages/pip/_internal/operations/build/wheel.py b/venv/Lib/site-packages/pip/_internal/operations/build/wheel.py index 1266ce05c6f4fddeec7f40a00ad4d2d85f531552..b0d2fc9eadb9349c0b8e69b58351648f3e54dfb5 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/build/wheel.py +++ b/venv/Lib/site-packages/pip/_internal/operations/build/wheel.py @@ -1,39 +1,30 @@ import logging import os +from typing import Optional -from pip._internal.utils.subprocess import runner_with_spinner_message -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._vendor.pep517.wrappers import Pep517HookCaller -if MYPY_CHECK_RUNNING: - from typing import List, Optional - from pip._vendor.pep517.wrappers import Pep517HookCaller +from pip._internal.utils.subprocess import runner_with_spinner_message logger = logging.getLogger(__name__) def build_wheel_pep517( - name, # type: str - backend, # type: Pep517HookCaller - metadata_directory, # type: str - build_options, # type: List[str] - tempd, # type: str -): - # type: (...) -> Optional[str] + name: str, + backend: Pep517HookCaller, + metadata_directory: str, + tempd: str, +) -> Optional[str]: """Build one InstallRequirement using the PEP 517 build process. Returns path to wheel if successfully built. Otherwise, returns None. """ assert metadata_directory is not None - if build_options: - # PEP 517 does not support --build-options - logger.error('Cannot build wheel for %s using PEP 517 when ' - '--build-option is present' % (name,)) - return None try: - logger.debug('Destination directory: %s', tempd) + logger.debug("Destination directory: %s", tempd) runner = runner_with_spinner_message( - 'Building wheel for {} (PEP 517)'.format(name) + f"Building wheel for {name} (pyproject.toml)" ) with backend.subprocess_runner(runner): wheel_name = backend.build_wheel( @@ -41,6 +32,6 @@ def build_wheel_pep517( metadata_directory=metadata_directory, ) except Exception: - logger.error('Failed building wheel for %s', name) + logger.error("Failed building wheel for %s", name) return None return os.path.join(tempd, wheel_name) diff --git a/venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py b/venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py index 37dc876acbd82a14231c16ae502b660b82df699f..2d5cb264ee6a0cc25e8a893db6fec277fce65c1f 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py +++ b/venv/Lib/site-packages/pip/_internal/operations/build/wheel_legacy.py @@ -1,67 +1,58 @@ import logging import os.path +from typing import List, Optional from pip._internal.cli.spinners import open_spinner -from pip._internal.utils.setuptools_build import ( - make_setuptools_bdist_wheel_args, -) +from pip._internal.utils.setuptools_build import make_setuptools_bdist_wheel_args from pip._internal.utils.subprocess import ( LOG_DIVIDER, call_subprocess, format_command_args, ) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Text logger = logging.getLogger(__name__) def format_command_result( - command_args, # type: List[str] - command_output, # type: Text -): - # type: (...) -> str + command_args: List[str], + command_output: str, +) -> str: """Format command information for logging.""" command_desc = format_command_args(command_args) - text = 'Command arguments: {}\n'.format(command_desc) + text = f"Command arguments: {command_desc}\n" if not command_output: - text += 'Command output: None' + text += "Command output: None" elif logger.getEffectiveLevel() > logging.DEBUG: - text += 'Command output: [use --verbose to show]' + text += "Command output: [use --verbose to show]" else: - if not command_output.endswith('\n'): - command_output += '\n' - text += 'Command output:\n{}{}'.format(command_output, LOG_DIVIDER) + if not command_output.endswith("\n"): + command_output += "\n" + text += f"Command output:\n{command_output}{LOG_DIVIDER}" return text def get_legacy_build_wheel_path( - names, # type: List[str] - temp_dir, # type: str - name, # type: str - command_args, # type: List[str] - command_output, # type: Text -): - # type: (...) -> Optional[str] + names: List[str], + temp_dir: str, + name: str, + command_args: List[str], + command_output: str, +) -> Optional[str]: """Return the path to the wheel in the temporary build directory.""" # Sort for determinism. names = sorted(names) if not names: - msg = ( - 'Legacy build of wheel for {!r} created no files.\n' - ).format(name) + msg = ("Legacy build of wheel for {!r} created no files.\n").format(name) msg += format_command_result(command_args, command_output) logger.warning(msg) return None if len(names) > 1: msg = ( - 'Legacy build of wheel for {!r} created more than one file.\n' - 'Filenames (choosing first): {}\n' + "Legacy build of wheel for {!r} created more than one file.\n" + "Filenames (choosing first): {}\n" ).format(name, names) msg += format_command_result(command_args, command_output) logger.warning(msg) @@ -70,14 +61,13 @@ def get_legacy_build_wheel_path( def build_wheel_legacy( - name, # type: str - setup_py_path, # type: str - source_dir, # type: str - global_options, # type: List[str] - build_options, # type: List[str] - tempd, # type: str -): - # type: (...) -> Optional[str] + name: str, + setup_py_path: str, + source_dir: str, + global_options: List[str], + build_options: List[str], + tempd: str, +) -> Optional[str]: """Build one unpacked package using the "legacy" build process. Returns path to wheel if successfully built. Otherwise, returns None. @@ -89,9 +79,9 @@ def build_wheel_legacy( destination_dir=tempd, ) - spin_message = 'Building wheel for {} (setup.py)'.format(name) + spin_message = f"Building wheel for {name} (setup.py)" with open_spinner(spin_message) as spinner: - logger.debug('Destination directory: %s', tempd) + logger.debug("Destination directory: %s", tempd) try: output = call_subprocess( @@ -101,7 +91,7 @@ def build_wheel_legacy( ) except Exception: spinner.finish("error") - logger.error('Failed building wheel for %s', name) + logger.error("Failed building wheel for %s", name) return None names = os.listdir(tempd) diff --git a/venv/Lib/site-packages/pip/_internal/operations/check.py b/venv/Lib/site-packages/pip/_internal/operations/check.py index b85a12306a4f9008ae072b5f2c88df5b9d1d3db3..fb3ac8b9c9ea57ec1bb667cb8e904a8b5b2f9df2 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/check.py +++ b/venv/Lib/site-packages/pip/_internal/operations/check.py @@ -1,87 +1,75 @@ """Validation of dependencies of packages """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - import logging -from collections import namedtuple +from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple -from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.pkg_resources import RequirementParseError +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name -from pip._internal.distributions import ( - make_distribution_for_install_requirement, -) -from pip._internal.utils.misc import get_installed_distributions -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.distributions import make_distribution_for_install_requirement +from pip._internal.metadata import get_default_environment +from pip._internal.metadata.base import DistributionVersion +from pip._internal.req.req_install import InstallRequirement logger = logging.getLogger(__name__) -if MYPY_CHECK_RUNNING: - from pip._internal.req.req_install import InstallRequirement - from typing import ( - Any, Callable, Dict, Optional, Set, Tuple, List - ) - # Shorthands - PackageSet = Dict[str, 'PackageDetails'] - Missing = Tuple[str, Any] - Conflicting = Tuple[str, str, Any] +class PackageDetails(NamedTuple): + version: DistributionVersion + dependencies: List[Requirement] - MissingDict = Dict[str, List[Missing]] - ConflictingDict = Dict[str, List[Conflicting]] - CheckResult = Tuple[MissingDict, ConflictingDict] -PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) +# Shorthands +PackageSet = Dict[NormalizedName, PackageDetails] +Missing = Tuple[NormalizedName, Requirement] +Conflicting = Tuple[NormalizedName, DistributionVersion, Requirement] +MissingDict = Dict[NormalizedName, List[Missing]] +ConflictingDict = Dict[NormalizedName, List[Conflicting]] +CheckResult = Tuple[MissingDict, ConflictingDict] +ConflictDetails = Tuple[PackageSet, CheckResult] -def create_package_set_from_installed(**kwargs): - # type: (**Any) -> Tuple[PackageSet, bool] - """Converts a list of distributions into a PackageSet. - """ - # Default to using all packages installed on the system - if kwargs == {}: - kwargs = {"local_only": False, "skip": ()} +def create_package_set_from_installed() -> Tuple[PackageSet, bool]: + """Converts a list of distributions into a PackageSet.""" package_set = {} problems = False - for dist in get_installed_distributions(**kwargs): - name = canonicalize_name(dist.project_name) + env = get_default_environment() + for dist in env.iter_installed_distributions(local_only=False, skip=()): + name = dist.canonical_name try: - package_set[name] = PackageDetails(dist.version, dist.requires()) - except RequirementParseError as e: - # Don't crash on broken metadata + dependencies = list(dist.iter_dependencies()) + package_set[name] = PackageDetails(dist.version, dependencies) + except (OSError, ValueError) as e: + # Don't crash on unreadable or broken metadata. logger.warning("Error parsing requirements for %s: %s", name, e) problems = True return package_set, problems -def check_package_set(package_set, should_ignore=None): - # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult +def check_package_set( + package_set: PackageSet, should_ignore: Optional[Callable[[str], bool]] = None +) -> CheckResult: """Check if a package set is consistent If should_ignore is passed, it should be a callable that takes a package name and returns a boolean. """ - if should_ignore is None: - def should_ignore(name): - return False missing = {} conflicting = {} - for package_name in package_set: + for package_name, package_detail in package_set.items(): # Info about dependencies of package_name - missing_deps = set() # type: Set[Missing] - conflicting_deps = set() # type: Set[Conflicting] + missing_deps: Set[Missing] = set() + conflicting_deps: Set[Conflicting] = set() - if should_ignore(package_name): + if should_ignore and should_ignore(package_name): continue - for req in package_set[package_name].requires: - name = canonicalize_name(req.project_name) # type: str + for req in package_detail.dependencies: + name = canonicalize_name(req.name) # Check if it's missing if name not in package_set: @@ -93,7 +81,7 @@ def check_package_set(package_set, should_ignore=None): continue # Check if there's a conflict - version = package_set[name].version # type: str + version = package_set[name].version if not req.specifier.contains(version, prereleases=True): conflicting_deps.add((name, version, req)) @@ -105,8 +93,7 @@ def check_package_set(package_set, should_ignore=None): return missing, conflicting -def check_install_conflicts(to_install): - # type: (List[InstallRequirement]) -> Tuple[PackageSet, CheckResult] +def check_install_conflicts(to_install: List[InstallRequirement]) -> ConflictDetails: """For checking if the dependency graph would be consistent after \ installing given requirements """ @@ -122,40 +109,39 @@ def check_install_conflicts(to_install): package_set, check_package_set( package_set, should_ignore=lambda name: name not in whitelist - ) + ), ) -def _simulate_installation_of(to_install, package_set): - # type: (List[InstallRequirement], PackageSet) -> Set[str] - """Computes the version of packages after installing to_install. - """ - +def _simulate_installation_of( + to_install: List[InstallRequirement], package_set: PackageSet +) -> Set[NormalizedName]: + """Computes the version of packages after installing to_install.""" # Keep track of packages that were installed installed = set() # Modify it as installing requirement_set would (assuming no errors) for inst_req in to_install: abstract_dist = make_distribution_for_install_requirement(inst_req) - dist = abstract_dist.get_pkg_resources_distribution() - - name = canonicalize_name(dist.key) - package_set[name] = PackageDetails(dist.version, dist.requires()) + dist = abstract_dist.get_metadata_distribution() + name = dist.canonical_name + package_set[name] = PackageDetails(dist.version, list(dist.iter_dependencies())) installed.add(name) return installed -def _create_whitelist(would_be_installed, package_set): - # type: (Set[str], PackageSet) -> Set[str] +def _create_whitelist( + would_be_installed: Set[NormalizedName], package_set: PackageSet +) -> Set[NormalizedName]: packages_affected = set(would_be_installed) for package_name in package_set: if package_name in packages_affected: continue - for req in package_set[package_name].requires: + for req in package_set[package_name].dependencies: if canonicalize_name(req.name) in packages_affected: packages_affected.add(package_name) break diff --git a/venv/Lib/site-packages/pip/_internal/operations/freeze.py b/venv/Lib/site-packages/pip/_internal/operations/freeze.py index aa6b052b6aa35cb0e6ba88293ff32d74c115d472..456554085df1bca271a261ee5e0b05ff413edafb 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/freeze.py +++ b/venv/Lib/site-packages/pip/_internal/operations/freeze.py @@ -1,81 +1,46 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import collections import logging import os +from typing import Container, Dict, Iterable, Iterator, List, NamedTuple, Optional, Set -from pip._vendor import six from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.pkg_resources import RequirementParseError +from pip._vendor.packaging.version import Version from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.metadata import BaseDistribution, get_environment from pip._internal.req.constructors import ( install_req_from_editable, install_req_from_line, ) from pip._internal.req.req_file import COMMENT_RE -from pip._internal.utils.direct_url_helpers import ( - direct_url_as_pep440_direct_reference, - dist_get_direct_url, -) -from pip._internal.utils.misc import ( - dist_is_editable, - get_installed_distributions, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import ( - Iterator, Optional, List, Container, Set, Dict, Tuple, Iterable, Union - ) - from pip._internal.cache import WheelCache - from pip._vendor.pkg_resources import ( - Distribution, Requirement - ) +from pip._internal.utils.direct_url_helpers import direct_url_as_pep440_direct_reference - RequirementInfo = Tuple[Optional[Union[str, Requirement]], bool, List[str]] +logger = logging.getLogger(__name__) -logger = logging.getLogger(__name__) +class _EditableInfo(NamedTuple): + requirement: str + comments: List[str] def freeze( - requirement=None, # type: Optional[List[str]] - find_links=None, # type: Optional[List[str]] - local_only=None, # type: Optional[bool] - user_only=None, # type: Optional[bool] - paths=None, # type: Optional[List[str]] - isolated=False, # type: bool - wheel_cache=None, # type: Optional[WheelCache] - exclude_editable=False, # type: bool - skip=() # type: Container[str] -): - # type: (...) -> Iterator[str] - find_links = find_links or [] - - for link in find_links: - yield '-f {}'.format(link) - installations = {} # type: Dict[str, FrozenRequirement] - for dist in get_installed_distributions(local_only=local_only, - skip=(), - user_only=user_only, - paths=paths): - try: - req = FrozenRequirement.from_dist(dist) - except RequirementParseError as exc: - # We include dist rather than dist.project_name because the - # dist string includes more information, like the version and - # location. We also include the exception message to aid - # troubleshooting. - logger.warning( - 'Could not generate requirement for distribution %r: %s', - dist, exc - ) - continue + requirement: Optional[List[str]] = None, + local_only: bool = False, + user_only: bool = False, + paths: Optional[List[str]] = None, + isolated: bool = False, + exclude_editable: bool = False, + skip: Container[str] = (), +) -> Iterator[str]: + installations: Dict[str, FrozenRequirement] = {} + + dists = get_environment(paths).iter_installed_distributions( + local_only=local_only, + skip=(), + user_only=user_only, + ) + for dist in dists: + req = FrozenRequirement.from_dist(dist) if exclude_editable and req.editable: continue installations[req.canonical_name] = req @@ -85,42 +50,50 @@ def freeze( # should only be emitted once, even if the same option is in multiple # requirements files, so we need to keep track of what has been emitted # so that we don't emit it again if it's seen again - emitted_options = set() # type: Set[str] + emitted_options: Set[str] = set() # keep track of which files a requirement is in so that we can # give an accurate warning if a requirement appears multiple times. - req_files = collections.defaultdict(list) # type: Dict[str, List[str]] + req_files: Dict[str, List[str]] = collections.defaultdict(list) for req_file_path in requirement: with open(req_file_path) as req_file: for line in req_file: - if (not line.strip() or - line.strip().startswith('#') or - line.startswith(( - '-r', '--requirement', - '-Z', '--always-unzip', - '-f', '--find-links', - '-i', '--index-url', - '--pre', - '--trusted-host', - '--process-dependency-links', - '--extra-index-url'))): + if ( + not line.strip() + or line.strip().startswith("#") + or line.startswith( + ( + "-r", + "--requirement", + "-f", + "--find-links", + "-i", + "--index-url", + "--pre", + "--trusted-host", + "--process-dependency-links", + "--extra-index-url", + "--use-feature", + ) + ) + ): line = line.rstrip() if line not in emitted_options: emitted_options.add(line) yield line continue - if line.startswith('-e') or line.startswith('--editable'): - if line.startswith('-e'): + if line.startswith("-e") or line.startswith("--editable"): + if line.startswith("-e"): line = line[2:].strip() else: - line = line[len('--editable'):].strip().lstrip('=') + line = line[len("--editable") :].strip().lstrip("=") line_req = install_req_from_editable( line, isolated=isolated, ) else: line_req = install_req_from_line( - COMMENT_RE.sub('', line).strip(), + COMMENT_RE.sub("", line).strip(), isolated=isolated, ) @@ -128,15 +101,15 @@ def freeze( logger.info( "Skipping line in requirement file [%s] because " "it's not clear what it would install: %s", - req_file_path, line.strip(), + req_file_path, + line.strip(), ) logger.info( " (add #egg=PackageName to the URL to avoid" " this warning)" ) else: - line_req_canonical_name = canonicalize_name( - line_req.name) + line_req_canonical_name = canonicalize_name(line_req.name) if line_req_canonical_name not in installations: # either it's not installed, or it is installed # but has been processed already @@ -145,99 +118,112 @@ def freeze( "Requirement file [%s] contains %s, but " "package %r is not installed", req_file_path, - COMMENT_RE.sub('', line).strip(), - line_req.name + COMMENT_RE.sub("", line).strip(), + line_req.name, ) else: req_files[line_req.name].append(req_file_path) else: - yield str(installations[ - line_req_canonical_name]).rstrip() + yield str(installations[line_req_canonical_name]).rstrip() del installations[line_req_canonical_name] req_files[line_req.name].append(req_file_path) # Warn about requirements that were included multiple times (in a # single requirements file or in different requirements files). - for name, files in six.iteritems(req_files): + for name, files in req_files.items(): if len(files) > 1: - logger.warning("Requirement %s included multiple times [%s]", - name, ', '.join(sorted(set(files)))) + logger.warning( + "Requirement %s included multiple times [%s]", + name, + ", ".join(sorted(set(files))), + ) - yield( - '## The following requirements were added by ' - 'pip freeze:' - ) - for installation in sorted( - installations.values(), key=lambda x: x.name.lower()): + yield ("## The following requirements were added by pip freeze:") + for installation in sorted(installations.values(), key=lambda x: x.name.lower()): if installation.canonical_name not in skip: yield str(installation).rstrip() -def get_requirement_info(dist): - # type: (Distribution) -> RequirementInfo +def _format_as_name_version(dist: BaseDistribution) -> str: + if isinstance(dist.version, Version): + return f"{dist.raw_name}=={dist.version}" + return f"{dist.raw_name}==={dist.version}" + + +def _get_editable_info(dist: BaseDistribution) -> _EditableInfo: """ - Compute and return values (req, editable, comments) for use in + Compute and return values (req, comments) for use in FrozenRequirement.from_dist(). """ - if not dist_is_editable(dist): - return (None, False, []) + editable_project_location = dist.editable_project_location + assert editable_project_location + location = os.path.normcase(os.path.abspath(editable_project_location)) - location = os.path.normcase(os.path.abspath(dist.location)) + from pip._internal.vcs import RemoteNotFoundError, RemoteNotValidError, vcs - from pip._internal.vcs import vcs, RemoteNotFoundError vcs_backend = vcs.get_backend_for_dir(location) if vcs_backend is None: - req = dist.as_requirement() + display = _format_as_name_version(dist) logger.debug( - 'No VCS found for editable requirement "%s" in: %r', req, + 'No VCS found for editable requirement "%s" in: %r', + display, location, ) - comments = [ - '# Editable install with no version control ({})'.format(req) - ] - return (location, True, comments) + return _EditableInfo( + requirement=location, + comments=[f"# Editable install with no version control ({display})"], + ) + + vcs_name = type(vcs_backend).__name__ try: - req = vcs_backend.get_src_requirement(location, dist.project_name) + req = vcs_backend.get_src_requirement(location, dist.raw_name) except RemoteNotFoundError: - req = dist.as_requirement() - comments = [ - '# Editable {} install with no remote ({})'.format( - type(vcs_backend).__name__, req, - ) - ] - return (location, True, comments) - + display = _format_as_name_version(dist) + return _EditableInfo( + requirement=location, + comments=[f"# Editable {vcs_name} install with no remote ({display})"], + ) + except RemoteNotValidError as ex: + display = _format_as_name_version(dist) + return _EditableInfo( + requirement=location, + comments=[ + f"# Editable {vcs_name} install ({display}) with either a deleted " + f"local remote or invalid URI:", + f"# '{ex.url}'", + ], + ) except BadCommand: logger.warning( - 'cannot determine version of editable source in %s ' - '(%s command not found in path)', + "cannot determine version of editable source in %s " + "(%s command not found in path)", location, vcs_backend.name, ) - return (None, True, []) - + return _EditableInfo(requirement=location, comments=[]) except InstallationError as exc: - logger.warning( - "Error when trying to get requirement for VCS system %s, " - "falling back to uneditable format", exc - ) + logger.warning("Error when trying to get requirement for VCS system %s", exc) else: - if req is not None: - return (req, True, []) + return _EditableInfo(requirement=req, comments=[]) - logger.warning( - 'Could not determine repository location of %s', location - ) - comments = ['## !! Could not determine repository location'] + logger.warning("Could not determine repository location of %s", location) - return (None, False, comments) + return _EditableInfo( + requirement=location, + comments=["## !! Could not determine repository location"], + ) -class FrozenRequirement(object): - def __init__(self, name, req, editable, comments=()): - # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None +class FrozenRequirement: + def __init__( + self, + name: str, + req: str, + editable: bool, + comments: Iterable[str] = (), + ) -> None: self.name = name self.canonical_name = canonicalize_name(name) self.req = req @@ -245,28 +231,24 @@ class FrozenRequirement(object): self.comments = comments @classmethod - def from_dist(cls, dist): - # type: (Distribution) -> FrozenRequirement - # TODO `get_requirement_info` is taking care of editable requirements. - # TODO This should be refactored when we will add detection of - # editable that provide .dist-info metadata. - req, editable, comments = get_requirement_info(dist) - if req is None and not editable: - # if PEP 610 metadata is present, attempt to use it - direct_url = dist_get_direct_url(dist) + def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement": + editable = dist.editable + if editable: + req, comments = _get_editable_info(dist) + else: + comments = [] + direct_url = dist.direct_url if direct_url: - req = direct_url_as_pep440_direct_reference( - direct_url, dist.project_name - ) - comments = [] - if req is None: - # name==version requirement - req = dist.as_requirement() + # if PEP 610 metadata is present, use it + req = direct_url_as_pep440_direct_reference(direct_url, dist.raw_name) + else: + # name==version requirement + req = _format_as_name_version(dist) - return cls(dist.project_name, req, editable, comments=comments) + return cls(dist.raw_name, req, editable, comments=comments) - def __str__(self): + def __str__(self) -> str: req = self.req if self.editable: - req = '-e {}'.format(req) - return '\n'.join(list(self.comments) + [str(req)]) + '\n' + req = f"-e {req}" + return "\n".join(list(self.comments) + [str(req)]) + "\n" diff --git a/venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py b/venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py index a668a61dc60f50963186b5a358e1e581bb6bbf09..5bd72ca198d299f8ba752e326f0c610d37cc4fd0 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py +++ b/venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py @@ -1,38 +1,32 @@ """Legacy editable installation process, i.e. `setup.py develop`. """ import logging +from typing import List, Optional, Sequence +from pip._internal.build_env import BuildEnvironment from pip._internal.utils.logging import indent_log from pip._internal.utils.setuptools_build import make_setuptools_develop_args from pip._internal.utils.subprocess import call_subprocess -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Sequence - - from pip._internal.build_env import BuildEnvironment - logger = logging.getLogger(__name__) def install_editable( - install_options, # type: List[str] - global_options, # type: Sequence[str] - prefix, # type: Optional[str] - home, # type: Optional[str] - use_user_site, # type: bool - name, # type: str - setup_py_path, # type: str - isolated, # type: bool - build_env, # type: BuildEnvironment - unpacked_source_directory, # type: str -): - # type: (...) -> None + install_options: List[str], + global_options: Sequence[str], + prefix: Optional[str], + home: Optional[str], + use_user_site: bool, + name: str, + setup_py_path: str, + isolated: bool, + build_env: BuildEnvironment, + unpacked_source_directory: str, +) -> None: """Install a package in editable mode. Most arguments are pass-through to setuptools. """ - logger.info('Running setup.py develop for %s', name) + logger.info("Running setup.py develop for %s", name) args = make_setuptools_develop_args( setup_py_path, diff --git a/venv/Lib/site-packages/pip/_internal/operations/install/legacy.py b/venv/Lib/site-packages/pip/_internal/operations/install/legacy.py index 0fac90573dbd77c7eacb941e09db9144045e62bd..2206c9309136bcc4439888a0622820a4a8c9cfa7 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/install/legacy.py +++ b/venv/Lib/site-packages/pip/_internal/operations/install/legacy.py @@ -3,56 +3,84 @@ import logging import os -import sys from distutils.util import change_root +from typing import List, Optional, Sequence -from pip._internal.utils.deprecation import deprecated +from pip._internal.build_env import BuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.models.scheme import Scheme from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ensure_dir from pip._internal.utils.setuptools_build import make_setuptools_install_args from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Sequence +logger = logging.getLogger(__name__) - from pip._internal.build_env import BuildEnvironment - from pip._internal.models.scheme import Scheme +class LegacyInstallFailure(Exception): + pass -logger = logging.getLogger(__name__) +def write_installed_files_from_setuptools_record( + record_lines: List[str], + root: Optional[str], + req_description: str, +) -> None: + def prepend_root(path: str) -> str: + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) -class LegacyInstallFailure(Exception): - def __init__(self): - # type: () -> None - self.parent = sys.exc_info() + for line in record_lines: + directory = os.path.dirname(line) + if directory.endswith(".egg-info"): + egg_info_dir = prepend_root(directory) + break + else: + message = ( + "{} did not indicate that it installed an " + ".egg-info directory. Only setup.py projects " + "generating .egg-info directories are supported." + ).format(req_description) + raise InstallationError(message) + + new_lines = [] + for line in record_lines: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir)) + new_lines.sort() + ensure_dir(egg_info_dir) + inst_files_path = os.path.join(egg_info_dir, "installed-files.txt") + with open(inst_files_path, "w") as f: + f.write("\n".join(new_lines) + "\n") def install( - install_options, # type: List[str] - global_options, # type: Sequence[str] - root, # type: Optional[str] - home, # type: Optional[str] - prefix, # type: Optional[str] - use_user_site, # type: bool - pycompile, # type: bool - scheme, # type: Scheme - setup_py_path, # type: str - isolated, # type: bool - req_name, # type: str - build_env, # type: BuildEnvironment - unpacked_source_directory, # type: str - req_description, # type: str -): - # type: (...) -> bool + install_options: List[str], + global_options: Sequence[str], + root: Optional[str], + home: Optional[str], + prefix: Optional[str], + use_user_site: bool, + pycompile: bool, + scheme: Scheme, + setup_py_path: str, + isolated: bool, + req_name: str, + build_env: BuildEnvironment, + unpacked_source_directory: str, + req_description: str, +) -> bool: header_dir = scheme.headers with TempDirectory(kind="record") as temp_dir: try: - record_filename = os.path.join(temp_dir.path, 'install-record.txt') + record_filename = os.path.join(temp_dir.path, "install-record.txt") install_args = make_setuptools_install_args( setup_py_path, global_options=global_options, @@ -68,7 +96,7 @@ def install( ) runner = runner_with_spinner_message( - "Running setup.py install for {}".format(req_name) + f"Running setup.py install for {req_name}" ) with indent_log(), build_env: runner( @@ -77,13 +105,13 @@ def install( ) if not os.path.exists(record_filename): - logger.debug('Record file %s not found', record_filename) + logger.debug("Record file %s not found", record_filename) # Signal to the caller that we didn't install the new package return False - except Exception: + except Exception as e: # Signal to the caller that we didn't install the new package - raise LegacyInstallFailure + raise LegacyInstallFailure from e # At this point, we have successfully installed the requirement. @@ -93,50 +121,5 @@ def install( with open(record_filename) as f: record_lines = f.read().splitlines() - def prepend_root(path): - # type: (str) -> str - if root is None or not os.path.isabs(path): - return path - else: - return change_root(root, path) - - for line in record_lines: - directory = os.path.dirname(line) - if directory.endswith('.egg-info'): - egg_info_dir = prepend_root(directory) - break - else: - deprecated( - reason=( - "{} did not indicate that it installed an " - ".egg-info directory. Only setup.py projects " - "generating .egg-info directories are supported." - ).format(req_description), - replacement=( - "for maintainers: updating the setup.py of {0}. " - "For users: contact the maintainers of {0} to let " - "them know to update their setup.py.".format( - req_name - ) - ), - gone_in="20.2", - issue=6998, - ) - # FIXME: put the record somewhere - return True - - new_lines = [] - for line in record_lines: - filename = line.strip() - if os.path.isdir(filename): - filename += os.path.sep - new_lines.append( - os.path.relpath(prepend_root(filename), egg_info_dir) - ) - new_lines.sort() - ensure_dir(egg_info_dir) - inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') - with open(inst_files_path, 'w') as f: - f.write('\n'.join(new_lines) + '\n') - + write_installed_files_from_setuptools_record(record_lines, root, req_description) return True diff --git a/venv/Lib/site-packages/pip/_internal/operations/install/wheel.py b/venv/Lib/site-packages/pip/_internal/operations/install/wheel.py index 2fb86b866db994d8b37fef48b36ba806aea3e2b0..e191b13431d67acfb80471421990cc0499e34e2f 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/install/wheel.py +++ b/venv/Lib/site-packages/pip/_internal/operations/install/wheel.py @@ -1,147 +1,131 @@ """Support for installing and building the "wheel" binary package format. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - import collections import compileall import contextlib import csv +import importlib import logging import os.path import re import shutil -import stat import sys import warnings from base64 import urlsafe_b64encode -from itertools import starmap -from zipfile import ZipFile +from email.message import Message +from itertools import chain, filterfalse, starmap +from typing import ( + IO, + TYPE_CHECKING, + Any, + BinaryIO, + Callable, + Dict, + Iterable, + Iterator, + List, + NewType, + Optional, + Sequence, + Set, + Tuple, + Union, + cast, +) +from zipfile import ZipFile, ZipInfo -from pip._vendor import pkg_resources from pip._vendor.distlib.scripts import ScriptMaker from pip._vendor.distlib.util import get_export_entry -from pip._vendor.six import StringIO +from pip._vendor.packaging.utils import canonicalize_name from pip._internal.exceptions import InstallationError from pip._internal.locations import get_major_minor_version +from pip._internal.metadata import ( + BaseDistribution, + FilesystemWheel, + get_wheel_distribution, +) from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from pip._internal.models.scheme import SCHEME_KEYS, Scheme from pip._internal.utils.filesystem import adjacent_tmp_file, replace -from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file -from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.utils.unpacking import current_umask, unpack_file +from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file, partition +from pip._internal.utils.unpacking import ( + current_umask, + is_within_directory, + set_extracted_file_to_default_mode_plus_executable, + zip_item_is_executable, +) from pip._internal.utils.wheel import parse_wheel -if MYPY_CHECK_RUNNING: - from email.message import Message - from typing import ( - Dict, List, Optional, Sequence, Tuple, Any, - Iterable, Iterator, Callable, Set, - ) +if TYPE_CHECKING: + from typing import Protocol - from pip._internal.models.scheme import Scheme - from pip._internal.utils.filesystem import NamedTemporaryFileResult + class File(Protocol): + src_record_path: "RecordPath" + dest_path: str + changed: bool - InstalledCSVRow = Tuple[str, ...] + def save(self) -> None: + pass logger = logging.getLogger(__name__) - -def normpath(src, p): - # type: (str, str) -> str - return os.path.relpath(src, p).replace(os.path.sep, '/') +RecordPath = NewType("RecordPath", str) +InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] -def rehash(path, blocksize=1 << 20): - # type: (str, int) -> Tuple[str, str] +def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]: """Return (encoded_digest, length) for path using hashlib.sha256()""" h, length = hash_file(path, blocksize) - digest = 'sha256=' + urlsafe_b64encode( - h.digest() - ).decode('latin1').rstrip('=') - # unicode/str python2 issues - return (digest, str(length)) # type: ignore + digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=") + return (digest, str(length)) -def csv_io_kwargs(mode): - # type: (str) -> Dict[str, Any] +def csv_io_kwargs(mode: str) -> Dict[str, Any]: """Return keyword arguments to properly open a CSV file in the given mode. """ - if sys.version_info.major < 3: - return {'mode': '{}b'.format(mode)} - else: - return {'mode': mode, 'newline': ''} + return {"mode": mode, "newline": "", "encoding": "utf-8"} -def fix_script(path): - # type: (str) -> Optional[bool] +def fix_script(path: str) -> bool: """Replace #!python with #!/path/to/python Return True if file was changed. """ # XXX RECORD hashes will need to be updated - if os.path.isfile(path): - with open(path, 'rb') as script: - firstline = script.readline() - if not firstline.startswith(b'#!python'): - return False - exename = sys.executable.encode(sys.getfilesystemencoding()) - firstline = b'#!' + exename + os.linesep.encode("ascii") - rest = script.read() - with open(path, 'wb') as script: - script.write(firstline) - script.write(rest) - return True - return None - - -def wheel_root_is_purelib(metadata): - # type: (Message) -> bool + assert os.path.isfile(path) + + with open(path, "rb") as script: + firstline = script.readline() + if not firstline.startswith(b"#!python"): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b"#!" + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, "wb") as script: + script.write(firstline) + script.write(rest) + return True + + +def wheel_root_is_purelib(metadata: Message) -> bool: return metadata.get("Root-Is-Purelib", "").lower() == "true" -def get_entrypoints(filename): - # type: (str) -> Tuple[Dict[str, str], Dict[str, str]] - if not os.path.exists(filename): - return {}, {} - - # This is done because you can pass a string to entry_points wrappers which - # means that they may or may not be valid INI files. The attempt here is to - # strip leading and trailing whitespace in order to make them valid INI - # files. - with open(filename) as fp: - data = StringIO() - for line in fp: - data.write(line.strip()) - data.write("\n") - data.seek(0) - - # get the entry points and then the script names - entry_points = pkg_resources.EntryPoint.parse_map(data) - console = entry_points.get('console_scripts', {}) - gui = entry_points.get('gui_scripts', {}) - - def _split_ep(s): - # type: (pkg_resources.EntryPoint) -> Tuple[str, str] - """get the string representation of EntryPoint, - remove space and split on '=' - """ - split_parts = str(s).replace(" ", "").split("=") - return split_parts[0], split_parts[1] - - # convert the EntryPoint objects into strings with module:function - console = dict(_split_ep(v) for v in console.values()) - gui = dict(_split_ep(v) for v in gui.values()) - return console, gui - - -def message_about_scripts_not_on_PATH(scripts): - # type: (Sequence[str]) -> Optional[str] +def get_entrypoints(dist: BaseDistribution) -> Tuple[Dict[str, str], Dict[str, str]]: + console_scripts = {} + gui_scripts = {} + for entry_point in dist.iter_entry_points(): + if entry_point.group == "console_scripts": + console_scripts[entry_point.name] = entry_point.value + elif entry_point.group == "gui_scripts": + gui_scripts[entry_point.name] = entry_point.value + return console_scripts, gui_scripts + + +def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]: """Determine if any scripts are not on PATH and format a warning. Returns a warning message if one or more scripts are not on PATH, otherwise None. @@ -150,7 +134,7 @@ def message_about_scripts_not_on_PATH(scripts): return None # Group scripts by the path they were installed in - grouped_by_dir = collections.defaultdict(set) # type: Dict[str, Set[str]] + grouped_by_dir: Dict[str, Set[str]] = collections.defaultdict(set) for destfile in scripts: parent_dir = os.path.dirname(destfile) script_name = os.path.basename(destfile) @@ -158,23 +142,24 @@ def message_about_scripts_not_on_PATH(scripts): # We don't want to warn for directories that are on PATH. not_warn_dirs = [ - os.path.normcase(i).rstrip(os.sep) for i in - os.environ.get("PATH", "").split(os.pathsep) + os.path.normcase(i).rstrip(os.sep) + for i in os.environ.get("PATH", "").split(os.pathsep) ] # If an executable sits with sys.executable, we don't warn for it. # This covers the case of venv invocations without activating the venv. not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) - warn_for = { - parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() + warn_for: Dict[str, Set[str]] = { + parent_dir: scripts + for parent_dir, scripts in grouped_by_dir.items() if os.path.normcase(parent_dir) not in not_warn_dirs - } # type: Dict[str, Set[str]] + } if not warn_for: return None # Format a message msg_lines = [] for parent_dir, dir_scripts in warn_for.items(): - sorted_scripts = sorted(dir_scripts) # type: List[str] + sorted_scripts: List[str] = sorted(dir_scripts) if len(sorted_scripts) == 1: start_text = "script {} is".format(sorted_scripts[0]) else: @@ -183,8 +168,9 @@ def message_about_scripts_not_on_PATH(scripts): ) msg_lines.append( - "The {} installed in '{}' which is not on PATH." - .format(start_text, parent_dir) + "The {} installed in '{}' which is not on PATH.".format( + start_text, parent_dir + ) ) last_line_fmt = ( @@ -211,9 +197,13 @@ def message_about_scripts_not_on_PATH(scripts): return "\n".join(msg_lines) -def sorted_outrows(outrows): - # type: (Iterable[InstalledCSVRow]) -> List[InstalledCSVRow] - """Return the given rows of a RECORD file in sorted order. +def _normalized_outrows( + outrows: Iterable[InstalledCSVRow], +) -> List[Tuple[str, str, str]]: + """Normalize the given rows of a RECORD file. + + Items in each row are converted into str. Rows are then sorted to make + the value more predictable for tests. Each row is a 3-tuple (path, hash, size) and corresponds to a record of a RECORD file (see PEP 376 and PEP 427 for details). For the rows @@ -228,78 +218,228 @@ def sorted_outrows(outrows): # coerce each element to a string to avoid a TypeError in this case. # For additional background, see-- # https://github.com/pypa/pip/issues/5868 - return sorted(outrows, key=lambda row: tuple(str(x) for x in row)) + return sorted( + (record_path, hash_, str(size)) for record_path, hash_, size in outrows + ) + + +def _record_to_fs_path(record_path: RecordPath) -> str: + return record_path + + +def _fs_to_record_path(path: str, relative_to: Optional[str] = None) -> RecordPath: + if relative_to is not None: + # On Windows, do not handle relative paths if they belong to different + # logical disks + if ( + os.path.splitdrive(path)[0].lower() + == os.path.splitdrive(relative_to)[0].lower() + ): + path = os.path.relpath(path, relative_to) + path = path.replace(os.path.sep, "/") + return cast("RecordPath", path) def get_csv_rows_for_installed( - old_csv_rows, # type: Iterable[List[str]] - installed, # type: Dict[str, str] - changed, # type: Set[str] - generated, # type: List[str] - lib_dir, # type: str -): - # type: (...) -> List[InstalledCSVRow] + old_csv_rows: List[List[str]], + installed: Dict[RecordPath, RecordPath], + changed: Set[RecordPath], + generated: List[str], + lib_dir: str, +) -> List[InstalledCSVRow]: """ :param installed: A map from archive RECORD path to installation RECORD path. """ - installed_rows = [] # type: List[InstalledCSVRow] + installed_rows: List[InstalledCSVRow] = [] for row in old_csv_rows: if len(row) > 3: - logger.warning( - 'RECORD line has more than three elements: {}'.format(row) - ) - # Make a copy because we are mutating the row. - row = list(row) - old_path = row[0] - new_path = installed.pop(old_path, old_path) - row[0] = new_path - if new_path in changed: - digest, length = rehash(new_path) - row[1] = digest - row[2] = length - installed_rows.append(tuple(row)) + logger.warning("RECORD line has more than three elements: %s", row) + old_record_path = cast("RecordPath", row[0]) + new_record_path = installed.pop(old_record_path, old_record_path) + if new_record_path in changed: + digest, length = rehash(_record_to_fs_path(new_record_path)) + else: + digest = row[1] if len(row) > 1 else "" + length = row[2] if len(row) > 2 else "" + installed_rows.append((new_record_path, digest, length)) for f in generated: + path = _fs_to_record_path(f, lib_dir) digest, length = rehash(f) - installed_rows.append((normpath(f, lib_dir), digest, str(length))) - for f in installed: - installed_rows.append((installed[f], '', '')) + installed_rows.append((path, digest, length)) + for installed_record_path in installed.values(): + installed_rows.append((installed_record_path, "", "")) return installed_rows -class MissingCallableSuffix(Exception): - pass +def get_console_script_specs(console: Dict[str, str]) -> List[str]: + """ + Given the mapping from entrypoint name to callable, return the relevant + console script specs. + """ + # Don't mutate caller's version + console = console.copy() + scripts_to_generate = [] -def _raise_for_invalid_entrypoint(specification): - # type: (str) -> None + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop("pip", None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append("pip = " + pip_script) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + scripts_to_generate.append( + "pip{} = {}".format(sys.version_info[0], pip_script) + ) + + scripts_to_generate.append(f"pip{get_major_minor_version()} = {pip_script}") + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r"pip(\d(\.\d)?)?$", k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop("easy_install", None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append("easy_install = " + easy_install_script) + + scripts_to_generate.append( + "easy_install-{} = {}".format( + get_major_minor_version(), easy_install_script + ) + ) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r"easy_install(-\d\.\d)?$", k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console entry points specified in the wheel + scripts_to_generate.extend(starmap("{} = {}".format, console.items())) + + return scripts_to_generate + + +class ZipBackedFile: + def __init__( + self, src_record_path: RecordPath, dest_path: str, zip_file: ZipFile + ) -> None: + self.src_record_path = src_record_path + self.dest_path = dest_path + self._zip_file = zip_file + self.changed = False + + def _getinfo(self) -> ZipInfo: + return self._zip_file.getinfo(self.src_record_path) + + def save(self) -> None: + # directory creation is lazy and after file filtering + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + parent_dir = os.path.dirname(self.dest_path) + ensure_dir(parent_dir) + + # When we open the output file below, any existing file is truncated + # before we start writing the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(self.dest_path): + os.unlink(self.dest_path) + + zipinfo = self._getinfo() + + with self._zip_file.open(zipinfo) as f: + with open(self.dest_path, "wb") as dest: + shutil.copyfileobj(f, dest) + + if zip_item_is_executable(zipinfo): + set_extracted_file_to_default_mode_plus_executable(self.dest_path) + + +class ScriptFile: + def __init__(self, file: "File") -> None: + self._file = file + self.src_record_path = self._file.src_record_path + self.dest_path = self._file.dest_path + self.changed = False + + def save(self) -> None: + self._file.save() + self.changed = fix_script(self.dest_path) + + +class MissingCallableSuffix(InstallationError): + def __init__(self, entry_point: str) -> None: + super().__init__( + "Invalid script entry point: {} - A callable " + "suffix is required. Cf https://packaging.python.org/" + "specifications/entry-points/#use-for-scripts for more " + "information.".format(entry_point) + ) + + +def _raise_for_invalid_entrypoint(specification: str) -> None: entry = get_export_entry(specification) if entry is not None and entry.suffix is None: raise MissingCallableSuffix(str(entry)) class PipScriptMaker(ScriptMaker): - def make(self, specification, options=None): - # type: (str, Dict[str, Any]) -> List[str] + def make(self, specification: str, options: Dict[str, Any] = None) -> List[str]: _raise_for_invalid_entrypoint(specification) - return super(PipScriptMaker, self).make(specification, options) - - -def install_unpacked_wheel( - name, # type: str - wheeldir, # type: str - wheel_zip, # type: ZipFile - scheme, # type: Scheme - req_description, # type: str - pycompile=True, # type: bool - warn_script_location=True, # type: bool - direct_url=None, # type: Optional[DirectUrl] -): - # type: (...) -> None + return super().make(specification, options) + + +def _install_wheel( + name: str, + wheel_zip: ZipFile, + wheel_path: str, + scheme: Scheme, + pycompile: bool = True, + warn_script_location: bool = True, + direct_url: Optional[DirectUrl] = None, + requested: bool = False, +) -> None: """Install a wheel. :param name: Name of the project to install - :param wheeldir: Base directory of the unpacked wheel :param wheel_zip: open ZipFile for wheel being installed :param scheme: Distutils scheme dictating the install directories :param req_description: String used in place of the requirement, for @@ -312,12 +452,6 @@ def install_unpacked_wheel( Wheel-Version * when the .dist-info dir does not match the wheel """ - # TODO: Investigate and break this up. - # TODO: Look into moving this into a dedicated class for representing an - # installation. - - source = wheeldir.rstrip(os.path.sep) + os.path.sep - info_dir, metadata = parse_wheel(wheel_zip, name) if wheel_root_is_purelib(metadata): @@ -325,132 +459,168 @@ def install_unpacked_wheel( else: lib_dir = scheme.platlib - subdirs = os.listdir(source) - data_dirs = [s for s in subdirs if s.endswith('.data')] - # Record details of the files moved # installed = files copied from the wheel to the destination # changed = files changed while installing (scripts #! line typically) # generated = files newly generated during the install (script wrappers) - installed = {} # type: Dict[str, str] - changed = set() - generated = [] # type: List[str] + installed: Dict[RecordPath, RecordPath] = {} + changed: Set[RecordPath] = set() + generated: List[str] = [] - # Compile all of the pyc files that we're going to be installing - if pycompile: - with captured_stdout() as stdout: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore') - compileall.compile_dir(source, force=True, quiet=True) - logger.debug(stdout.getvalue()) - - def record_installed(srcfile, destfile, modified=False): - # type: (str, str, bool) -> None + def record_installed( + srcfile: RecordPath, destfile: str, modified: bool = False + ) -> None: """Map archive RECORD paths to installation RECORD paths.""" - oldpath = normpath(srcfile, wheeldir) - newpath = normpath(destfile, lib_dir) - installed[oldpath] = newpath + newpath = _fs_to_record_path(destfile, lib_dir) + installed[srcfile] = newpath if modified: - changed.add(destfile) - - def clobber( - source, # type: str - dest, # type: str - is_base, # type: bool - fixer=None, # type: Optional[Callable[[str], Any]] - filter=None # type: Optional[Callable[[str], bool]] - ): - # type: (...) -> None - ensure_dir(dest) # common for the 'include' path - - for dir, subdirs, files in os.walk(source): - basedir = dir[len(source):].lstrip(os.path.sep) - destdir = os.path.join(dest, basedir) - if is_base and basedir == '': - subdirs[:] = [s for s in subdirs if not s.endswith('.data')] - for f in files: - # Skip unwanted files - if filter and filter(f): - continue - srcfile = os.path.join(dir, f) - destfile = os.path.join(dest, basedir, f) - # directory creation is lazy and after the file filtering above - # to ensure we don't install empty dirs; empty dirs can't be - # uninstalled. - ensure_dir(destdir) - - # copyfile (called below) truncates the destination if it - # exists and then writes the new contents. This is fine in most - # cases, but can cause a segfault if pip has loaded a shared - # object (e.g. from pyopenssl through its vendored urllib3) - # Since the shared object is mmap'd an attempt to call a - # symbol in it will then cause a segfault. Unlinking the file - # allows writing of new contents while allowing the process to - # continue to use the old copy. - if os.path.exists(destfile): - os.unlink(destfile) - - # We use copyfile (not move, copy, or copy2) to be extra sure - # that we are not moving directories over (copyfile fails for - # directories) as well as to ensure that we are not copying - # over any metadata because we want more control over what - # metadata we actually copy over. - shutil.copyfile(srcfile, destfile) - - # Copy over the metadata for the file, currently this only - # includes the atime and mtime. - st = os.stat(srcfile) - if hasattr(os, "utime"): - os.utime(destfile, (st.st_atime, st.st_mtime)) - - # If our file is executable, then make our destination file - # executable. - if os.access(srcfile, os.X_OK): - st = os.stat(srcfile) - permissions = ( - st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH - ) - os.chmod(destfile, permissions) - - changed = False - if fixer: - changed = fixer(destfile) - record_installed(srcfile, destfile, changed) - - clobber(source, lib_dir, True) + changed.add(_fs_to_record_path(destfile)) - dest_info_dir = os.path.join(lib_dir, info_dir) + def is_dir_path(path: RecordPath) -> bool: + return path.endswith("/") + + def assert_no_path_traversal(dest_dir_path: str, target_path: str) -> None: + if not is_within_directory(dest_dir_path, target_path): + message = ( + "The wheel {!r} has a file {!r} trying to install" + " outside the target directory {!r}" + ) + raise InstallationError( + message.format(wheel_path, target_path, dest_dir_path) + ) + + def root_scheme_file_maker( + zip_file: ZipFile, dest: str + ) -> Callable[[RecordPath], "File"]: + def make_root_scheme_file(record_path: RecordPath) -> "File": + normed_path = os.path.normpath(record_path) + dest_path = os.path.join(dest, normed_path) + assert_no_path_traversal(dest, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_root_scheme_file + + def data_scheme_file_maker( + zip_file: ZipFile, scheme: Scheme + ) -> Callable[[RecordPath], "File"]: + scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS} + + def make_data_scheme_file(record_path: RecordPath) -> "File": + normed_path = os.path.normpath(record_path) + try: + _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2) + except ValueError: + message = ( + "Unexpected file in {}: {!r}. .data directory contents" + " should be named like: '/'." + ).format(wheel_path, record_path) + raise InstallationError(message) + + try: + scheme_path = scheme_paths[scheme_key] + except KeyError: + valid_scheme_keys = ", ".join(sorted(scheme_paths)) + message = ( + "Unknown scheme key used in {}: {} (for file {!r}). .data" + " directory contents should be in subdirectories named" + " with a valid scheme key ({})" + ).format(wheel_path, scheme_key, record_path, valid_scheme_keys) + raise InstallationError(message) + + dest_path = os.path.join(scheme_path, dest_subpath) + assert_no_path_traversal(scheme_path, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_data_scheme_file + + def is_data_scheme_path(path: RecordPath) -> bool: + return path.split("/", 1)[0].endswith(".data") + + paths = cast(List[RecordPath], wheel_zip.namelist()) + file_paths = filterfalse(is_dir_path, paths) + root_scheme_paths, data_scheme_paths = partition(is_data_scheme_path, file_paths) + + make_root_scheme_file = root_scheme_file_maker(wheel_zip, lib_dir) + files: Iterator[File] = map(make_root_scheme_file, root_scheme_paths) + + def is_script_scheme_path(path: RecordPath) -> bool: + parts = path.split("/", 2) + return len(parts) > 2 and parts[0].endswith(".data") and parts[1] == "scripts" + + other_scheme_paths, script_scheme_paths = partition( + is_script_scheme_path, data_scheme_paths + ) + + make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme) + other_scheme_files = map(make_data_scheme_file, other_scheme_paths) + files = chain(files, other_scheme_files) # Get the defined entry points - ep_file = os.path.join(dest_info_dir, 'entry_points.txt') - console, gui = get_entrypoints(ep_file) + distribution = get_wheel_distribution( + FilesystemWheel(wheel_path), + canonicalize_name(name), + ) + console, gui = get_entrypoints(distribution) - def is_entrypoint_wrapper(name): - # type: (str) -> bool + def is_entrypoint_wrapper(file: "File") -> bool: # EP, EP.exe and EP-script.py are scripts generated for # entry point EP by setuptools - if name.lower().endswith('.exe'): + path = file.dest_path + name = os.path.basename(path) + if name.lower().endswith(".exe"): matchname = name[:-4] - elif name.lower().endswith('-script.py'): + elif name.lower().endswith("-script.py"): matchname = name[:-10] elif name.lower().endswith(".pya"): matchname = name[:-4] else: matchname = name # Ignore setuptools-generated scripts - return (matchname in console or matchname in gui) - - for datadir in data_dirs: - fixer = None - filter = None - for subdir in os.listdir(os.path.join(wheeldir, datadir)): - fixer = None - if subdir == 'scripts': - fixer = fix_script - filter = is_entrypoint_wrapper - source = os.path.join(wheeldir, datadir, subdir) - dest = getattr(scheme, subdir) - clobber(source, dest, False, fixer=fixer, filter=filter) + return matchname in console or matchname in gui + + script_scheme_files: Iterator[File] = map( + make_data_scheme_file, script_scheme_paths + ) + script_scheme_files = filterfalse(is_entrypoint_wrapper, script_scheme_files) + script_scheme_files = map(ScriptFile, script_scheme_files) + files = chain(files, script_scheme_files) + + for file in files: + file.save() + record_installed(file.src_record_path, file.dest_path, file.changed) + + def pyc_source_file_paths() -> Iterator[str]: + # We de-duplicate installation paths, since there can be overlap (e.g. + # file in .data maps to same location as file in wheel root). + # Sorting installation paths makes it easier to reproduce and debug + # issues related to permissions on existing files. + for installed_path in sorted(set(installed.values())): + full_installed_path = os.path.join(lib_dir, installed_path) + if not os.path.isfile(full_installed_path): + continue + if not full_installed_path.endswith(".py"): + continue + yield full_installed_path + + def pyc_output_path(path: str) -> str: + """Return the path the pyc file would have been written to.""" + return importlib.util.cache_from_source(path) + + # Compile all of the pyc files for the installed files + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + for path in pyc_source_file_paths(): + success = compileall.compile_file(path, force=True, quiet=True) + if success: + pyc_path = pyc_output_path(path) + assert os.path.exists(pyc_path) + pyc_record_path = cast( + "RecordPath", pyc_path.replace(os.path.sep, "/") + ) + record_installed(pyc_record_path, pyc_path) + logger.debug(stdout.getvalue()) maker = PipScriptMaker(None, scheme.scripts) @@ -461,106 +631,22 @@ def install_unpacked_wheel( # Ensure we don't generate any variants for scripts because this is almost # never what somebody wants. # See https://bitbucket.org/pypa/distlib/issue/35/ - maker.variants = {''} + maker.variants = {""} # This is required because otherwise distlib creates scripts that are not # executable. # See https://bitbucket.org/pypa/distlib/issue/32/ maker.set_mode = True - scripts_to_generate = [] - - # Special case pip and setuptools to generate versioned wrappers - # - # The issue is that some projects (specifically, pip and setuptools) use - # code in setup.py to create "versioned" entry points - pip2.7 on Python - # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into - # the wheel metadata at build time, and so if the wheel is installed with - # a *different* version of Python the entry points will be wrong. The - # correct fix for this is to enhance the metadata to be able to describe - # such versioned entry points, but that won't happen till Metadata 2.0 is - # available. - # In the meantime, projects using versioned entry points will either have - # incorrect versioned entry points, or they will not be able to distribute - # "universal" wheels (i.e., they will need a wheel per Python version). - # - # Because setuptools and pip are bundled with _ensurepip and virtualenv, - # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we - # override the versioned entry points in the wheel and generate the - # correct ones. This code is purely a short-term measure until Metadata 2.0 - # is available. - # - # To add the level of hack in this section of code, in order to support - # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment - # variable which will control which version scripts get installed. - # - # ENSUREPIP_OPTIONS=altinstall - # - Only pipX.Y and easy_install-X.Y will be generated and installed - # ENSUREPIP_OPTIONS=install - # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note - # that this option is technically if ENSUREPIP_OPTIONS is set and is - # not altinstall - # DEFAULT - # - The default behavior is to install pip, pipX, pipX.Y, easy_install - # and easy_install-X.Y. - pip_script = console.pop('pip', None) - if pip_script: - if "ENSUREPIP_OPTIONS" not in os.environ: - scripts_to_generate.append('pip = ' + pip_script) - - if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": - scripts_to_generate.append( - 'pip{} = {}'.format(sys.version_info[0], pip_script) - ) - - scripts_to_generate.append( - 'pip{} = {}'.format(get_major_minor_version(), pip_script) - ) - # Delete any other versioned pip entry points - pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] - for k in pip_ep: - del console[k] - easy_install_script = console.pop('easy_install', None) - if easy_install_script: - if "ENSUREPIP_OPTIONS" not in os.environ: - scripts_to_generate.append( - 'easy_install = ' + easy_install_script - ) - - scripts_to_generate.append( - 'easy_install-{} = {}'.format( - get_major_minor_version(), easy_install_script - ) - ) - # Delete any other versioned easy_install entry points - easy_install_ep = [ - k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) - ] - for k in easy_install_ep: - del console[k] - # Generate the console and GUI entry points specified in the wheel - scripts_to_generate.extend(starmap('{} = {}'.format, console.items())) - - gui_scripts_to_generate = list(starmap('{} = {}'.format, gui.items())) + scripts_to_generate = get_console_script_specs(console) - generated_console_scripts = [] # type: List[str] + gui_scripts_to_generate = list(starmap("{} = {}".format, gui.items())) - try: - generated_console_scripts = maker.make_multiple(scripts_to_generate) - generated.extend(generated_console_scripts) + generated_console_scripts = maker.make_multiple(scripts_to_generate) + generated.extend(generated_console_scripts) - generated.extend( - maker.make_multiple(gui_scripts_to_generate, {'gui': True}) - ) - except MissingCallableSuffix as e: - entry = e.args[0] - raise InstallationError( - "Invalid script entry point: {} for req: {} - A callable " - "suffix is required. Cf https://packaging.python.org/" - "specifications/entry-points/#use-for-scripts for more " - "information.".format(entry, req_description) - ) + generated.extend(maker.make_multiple(gui_scripts_to_generate, {"gui": True})) if warn_script_location: msg = message_about_scripts_not_on_PATH(generated_console_scripts) @@ -570,17 +656,18 @@ def install_unpacked_wheel( generated_file_mode = 0o666 & ~current_umask() @contextlib.contextmanager - def _generate_file(path, **kwargs): - # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + def _generate_file(path: str, **kwargs: Any) -> Iterator[BinaryIO]: with adjacent_tmp_file(path, **kwargs) as f: yield f os.chmod(f.name, generated_file_mode) replace(f.name, path) + dest_info_dir = os.path.join(lib_dir, info_dir) + # Record pip as the installer - installer_path = os.path.join(dest_info_dir, 'INSTALLER') + installer_path = os.path.join(dest_info_dir, "INSTALLER") with _generate_file(installer_path) as installer_file: - installer_file.write(b'pip\n') + installer_file.write(b"pip\n") generated.append(installer_path) # Record the PEP 610 direct URL reference @@ -590,42 +677,62 @@ def install_unpacked_wheel( direct_url_file.write(direct_url.to_json().encode("utf-8")) generated.append(direct_url_path) + # Record the REQUESTED file + if requested: + requested_path = os.path.join(dest_info_dir, "REQUESTED") + with open(requested_path, "wb"): + pass + generated.append(requested_path) + + record_text = distribution.read_text("RECORD") + record_rows = list(csv.reader(record_text.splitlines())) + + rows = get_csv_rows_for_installed( + record_rows, + installed=installed, + changed=changed, + generated=generated, + lib_dir=lib_dir, + ) + # Record details of all files installed - record_path = os.path.join(dest_info_dir, 'RECORD') - with open(record_path, **csv_io_kwargs('r')) as record_file: - rows = get_csv_rows_for_installed( - csv.reader(record_file), - installed=installed, - changed=changed, - generated=generated, - lib_dir=lib_dir) - with _generate_file(record_path, **csv_io_kwargs('w')) as record_file: - writer = csv.writer(record_file) - writer.writerows(sorted_outrows(rows)) # sort to simplify testing + record_path = os.path.join(dest_info_dir, "RECORD") + + with _generate_file(record_path, **csv_io_kwargs("w")) as record_file: + # Explicitly cast to typing.IO[str] as a workaround for the mypy error: + # "writer" has incompatible type "BinaryIO"; expected "_Writer" + writer = csv.writer(cast("IO[str]", record_file)) + writer.writerows(_normalized_outrows(rows)) + + +@contextlib.contextmanager +def req_error_context(req_description: str) -> Iterator[None]: + try: + yield + except InstallationError as e: + message = "For req: {}. {}".format(req_description, e.args[0]) + raise InstallationError(message) from e def install_wheel( - name, # type: str - wheel_path, # type: str - scheme, # type: Scheme - req_description, # type: str - pycompile=True, # type: bool - warn_script_location=True, # type: bool - _temp_dir_for_testing=None, # type: Optional[str] - direct_url=None, # type: Optional[DirectUrl] -): - # type: (...) -> None - with TempDirectory( - path=_temp_dir_for_testing, kind="unpacked-wheel" - ) as unpacked_dir, ZipFile(wheel_path, allowZip64=True) as z: - unpack_file(wheel_path, unpacked_dir.path) - install_unpacked_wheel( - name=name, - wheeldir=unpacked_dir.path, - wheel_zip=z, - scheme=scheme, - req_description=req_description, - pycompile=pycompile, - warn_script_location=warn_script_location, - direct_url=direct_url, - ) + name: str, + wheel_path: str, + scheme: Scheme, + req_description: str, + pycompile: bool = True, + warn_script_location: bool = True, + direct_url: Optional[DirectUrl] = None, + requested: bool = False, +) -> None: + with ZipFile(wheel_path, allowZip64=True) as z: + with req_error_context(req_description): + _install_wheel( + name=name, + wheel_zip=z, + wheel_path=wheel_path, + scheme=scheme, + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=requested, + ) diff --git a/venv/Lib/site-packages/pip/_internal/operations/prepare.py b/venv/Lib/site-packages/pip/_internal/operations/prepare.py index 1fcbb775ece6b00a75b0c966a00c9072fdb3e231..34cf9a51b6ee68365e55c239431f937aff95b8f7 100644 --- a/venv/Lib/site-packages/pip/_internal/operations/prepare.py +++ b/venv/Lib/site-packages/pip/_internal/operations/prepare.py @@ -8,133 +8,97 @@ import logging import mimetypes import os import shutil +from typing import Dict, Iterable, List, Optional -from pip._vendor import requests -from pip._vendor.six import PY2 +from pip._vendor.packaging.utils import canonicalize_name -from pip._internal.distributions import ( - make_distribution_for_install_requirement, -) +from pip._internal.distributions import make_distribution_for_install_requirement from pip._internal.distributions.installed import InstalledDistribution from pip._internal.exceptions import ( DirectoryUrlHashUnsupported, HashMismatch, HashUnpinned, InstallationError, + NetworkConnectionError, PreviousBuildDirError, VcsHashUnsupported, ) +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.network.download import BatchDownloader, Downloader +from pip._internal.network.lazy_wheel import ( + HTTPRangeRequestUnsupported, + dist_from_wheel_url, +) +from pip._internal.network.session import PipSession +from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_tracker import RequirementTracker from pip._internal.utils.filesystem import copy2_fixed -from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.hashes import Hashes, MissingHashes from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import ( - display_path, - hide_url, - path_to_display, - rmtree, -) +from pip._internal.utils.misc import display_path, hide_url, is_installable_dir, rmtree from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.unpacking import unpack_file from pip._internal.vcs import vcs -if MYPY_CHECK_RUNNING: - from typing import ( - Callable, List, Optional, Tuple, - ) - - from mypy_extensions import TypedDict - - from pip._internal.distributions import AbstractDistribution - from pip._internal.index.package_finder import PackageFinder - from pip._internal.models.link import Link - from pip._internal.network.download import Downloader - from pip._internal.req.req_install import InstallRequirement - from pip._internal.req.req_tracker import RequirementTracker - from pip._internal.utils.hashes import Hashes - - if PY2: - CopytreeKwargs = TypedDict( - 'CopytreeKwargs', - { - 'ignore': Callable[[str, List[str]], List[str]], - 'symlinks': bool, - }, - total=False, - ) - else: - CopytreeKwargs = TypedDict( - 'CopytreeKwargs', - { - 'copy_function': Callable[[str, str], None], - 'ignore': Callable[[str, List[str]], List[str]], - 'ignore_dangling_symlinks': bool, - 'symlinks': bool, - }, - total=False, - ) - logger = logging.getLogger(__name__) def _get_prepared_distribution( - req, # type: InstallRequirement - req_tracker, # type: RequirementTracker - finder, # type: PackageFinder - build_isolation # type: bool -): - # type: (...) -> AbstractDistribution - """Prepare a distribution for installation. - """ + req: InstallRequirement, + req_tracker: RequirementTracker, + finder: PackageFinder, + build_isolation: bool, +) -> BaseDistribution: + """Prepare a distribution for installation.""" abstract_dist = make_distribution_for_install_requirement(req) with req_tracker.track(req): abstract_dist.prepare_distribution_metadata(finder, build_isolation) - return abstract_dist + return abstract_dist.get_metadata_distribution() -def unpack_vcs_link(link, location): - # type: (Link, str) -> None +def unpack_vcs_link(link: Link, location: str) -> None: vcs_backend = vcs.get_backend_for_scheme(link.scheme) assert vcs_backend is not None vcs_backend.unpack(location, url=hide_url(link.url)) -class File(object): - def __init__(self, path, content_type): - # type: (str, str) -> None +class File: + def __init__(self, path: str, content_type: Optional[str]) -> None: self.path = path - self.content_type = content_type + if content_type is None: + self.content_type = mimetypes.guess_type(path)[0] + else: + self.content_type = content_type def get_http_url( - link, # type: Link - downloader, # type: Downloader - download_dir=None, # type: Optional[str] - hashes=None, # type: Optional[Hashes] -): - # type: (...) -> File + link: Link, + download: Downloader, + download_dir: Optional[str] = None, + hashes: Optional[Hashes] = None, +) -> File: temp_dir = TempDirectory(kind="unpack", globally_managed=True) # If a download dir is specified, is the file already downloaded there? already_downloaded_path = None if download_dir: - already_downloaded_path = _check_download_dir( - link, download_dir, hashes - ) + already_downloaded_path = _check_download_dir(link, download_dir, hashes) if already_downloaded_path: from_path = already_downloaded_path - content_type = mimetypes.guess_type(from_path)[0] + content_type = None else: # let's download to a tmp dir - from_path, content_type = _download_http_url( - link, downloader, temp_dir.path, hashes - ) + from_path, content_type = download(link, temp_dir.path) + if hashes: + hashes.check_against_path(from_path) return File(from_path, content_type) -def _copy2_ignoring_special_files(src, dest): - # type: (str, str) -> None +def _copy2_ignoring_special_files(src: str, dest: str) -> None: """Copying special files is not supported, but as a convenience to users we skip errors copying them. This supports tools that may create e.g. socket files in the project source directory. @@ -149,26 +113,24 @@ def _copy2_ignoring_special_files(src, dest): logger.warning( "Ignoring special file error '%s' encountered copying %s to %s.", str(e), - path_to_display(src), - path_to_display(dest), + src, + dest, ) -def _copy_source_tree(source, target): - # type: (str, str) -> None +def _copy_source_tree(source: str, target: str) -> None: target_abspath = os.path.abspath(target) target_basename = os.path.basename(target_abspath) target_dirname = os.path.dirname(target_abspath) - def ignore(d, names): - # type: (str, List[str]) -> List[str] - skipped = [] # type: List[str] + def ignore(d: str, names: List[str]) -> List[str]: + skipped: List[str] = [] if d == source: # Pulling in those directories can potentially be very slow, # exclude the following directories if they appear in the top # level dir (and only it). # See discussion at https://github.com/pypa/pip/pull/6770 - skipped += ['.tox', '.nox'] + skipped += [".tox", ".nox"] if os.path.abspath(d) == target_dirname: # Prevent an infinite recursion if the target is in source. # This can happen when TMPDIR is set to ${PWD}/... @@ -176,30 +138,23 @@ def _copy_source_tree(source, target): skipped += [target_basename] return skipped - kwargs = dict(ignore=ignore, symlinks=True) # type: CopytreeKwargs - - if not PY2: - # Python 2 does not support copy_function, so we only ignore - # errors on special file copy in Python 3. - kwargs['copy_function'] = _copy2_ignoring_special_files - - shutil.copytree(source, target, **kwargs) + shutil.copytree( + source, + target, + ignore=ignore, + symlinks=True, + copy_function=_copy2_ignoring_special_files, + ) def get_file_url( - link, # type: Link - download_dir=None, # type: Optional[str] - hashes=None # type: Optional[Hashes] -): - # type: (...) -> File - """Get file and optionally check its hash. - """ + link: Link, download_dir: Optional[str] = None, hashes: Optional[Hashes] = None +) -> File: + """Get file and optionally check its hash.""" # If a download dir is specified, is the file already there and valid? already_downloaded_path = None if download_dir: - already_downloaded_path = _check_download_dir( - link, download_dir, hashes - ) + already_downloaded_path = _check_download_dir(link, download_dir, hashes) if already_downloaded_path: from_path = already_downloaded_path @@ -213,20 +168,16 @@ def get_file_url( # one; no internet-sourced hash will be in `hashes`. if hashes: hashes.check_against_path(from_path) - - content_type = mimetypes.guess_type(from_path)[0] - - return File(from_path, content_type) + return File(from_path, None) def unpack_url( - link, # type: Link - location, # type: str - downloader, # type: Downloader - download_dir=None, # type: Optional[str] - hashes=None, # type: Optional[Hashes] -): - # type: (...) -> Optional[File] + link: Link, + location: str, + download: Downloader, + download_dir: Optional[str] = None, + hashes: Optional[Hashes] = None, +) -> Optional[File]: """Unpack link into location, downloading if required. :param hashes: A Hashes object, one of whose embedded hashes must match, @@ -239,7 +190,14 @@ def unpack_url( unpack_vcs_link(link, location) return None - # If it's a url to a local directory + # Once out-of-tree-builds are no longer supported, could potentially + # replace the below condition with `assert not link.is_existing_dir` + # - unpack_url does not need to be called for in-tree-builds. + # + # As further cleanup, _copy_source_tree and accompanying tests can + # be removed. + # + # TODO when use-deprecated=out-of-tree-build is removed if link.is_existing_dir(): if os.path.isdir(location): rmtree(location) @@ -254,43 +212,24 @@ def unpack_url( else: file = get_http_url( link, - downloader, + download, download_dir, hashes=hashes, ) # unpack the archive to the build dir location. even when only downloading - # archives, they have to be unpacked to parse dependencies - unpack_file(file.path, location, file.content_type) + # archives, they have to be unpacked to parse dependencies, except wheels + if not link.is_wheel: + unpack_file(file.path, location, file.content_type) return file -def _download_http_url( - link, # type: Link - downloader, # type: Downloader - temp_dir, # type: str - hashes, # type: Optional[Hashes] -): - # type: (...) -> Tuple[str, str] - """Download link url into temp_dir using provided session""" - download = downloader(link) - - file_path = os.path.join(temp_dir, download.filename) - with open(file_path, 'wb') as content_file: - for chunk in download.chunks: - content_file.write(chunk) - - if hashes: - hashes.check_against_path(file_path) - - return file_path, download.response.headers.get('content-type', '') - - -def _check_download_dir(link, download_dir, hashes): - # type: (Link, str, Optional[Hashes]) -> Optional[str] - """ Check download_dir for previously downloaded file with correct hash - If a correct file is found return its path else None +def _check_download_dir( + link: Link, download_dir: str, hashes: Optional[Hashes] +) -> Optional[str]: + """Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None """ download_path = os.path.join(download_dir, link.filename) @@ -298,61 +237,52 @@ def _check_download_dir(link, download_dir, hashes): return None # If already downloaded, does its hash match? - logger.info('File was already downloaded %s', download_path) + logger.info("File was already downloaded %s", download_path) if hashes: try: hashes.check_against_path(download_path) except HashMismatch: logger.warning( - 'Previously-downloaded file %s has bad hash. ' - 'Re-downloading.', - download_path + "Previously-downloaded file %s has bad hash. Re-downloading.", + download_path, ) os.unlink(download_path) return None return download_path -class RequirementPreparer(object): - """Prepares a Requirement - """ +class RequirementPreparer: + """Prepares a Requirement""" def __init__( self, - build_dir, # type: str - download_dir, # type: Optional[str] - src_dir, # type: str - wheel_download_dir, # type: Optional[str] - build_isolation, # type: bool - req_tracker, # type: RequirementTracker - downloader, # type: Downloader - finder, # type: PackageFinder - require_hashes, # type: bool - use_user_site, # type: bool - ): - # type: (...) -> None - super(RequirementPreparer, self).__init__() + build_dir: str, + download_dir: Optional[str], + src_dir: str, + build_isolation: bool, + req_tracker: RequirementTracker, + session: PipSession, + progress_bar: str, + finder: PackageFinder, + require_hashes: bool, + use_user_site: bool, + lazy_wheel: bool, + in_tree_build: bool, + ) -> None: + super().__init__() self.src_dir = src_dir self.build_dir = build_dir self.req_tracker = req_tracker - self.downloader = downloader + self._session = session + self._download = Downloader(session, progress_bar) + self._batch_download = BatchDownloader(session, progress_bar) self.finder = finder # Where still-packed archives should be written to. If None, they are # not saved, and are deleted immediately after unpacking. self.download_dir = download_dir - # Where still-packed .whl files should be written to. If None, they are - # written to the download_dir parameter. Separate to download_dir to - # permit only keeping wheel archives for pip wheel. - self.wheel_download_dir = wheel_download_dir - - # NOTE - # download_dir and wheel_download_dir overlap semantically and may - # be combined if we're willing to have non-wheel archives present in - # the wheelhouse output by 'pip wheel'. - # Is build isolation allowed? self.build_isolation = build_isolation @@ -362,207 +292,341 @@ class RequirementPreparer(object): # Should install in user site-packages? self.use_user_site = use_user_site - @property - def _download_should_save(self): - # type: () -> bool - if not self.download_dir: - return False + # Should wheels be downloaded lazily? + self.use_lazy_wheel = lazy_wheel - if os.path.exists(self.download_dir): - return True + # Should in-tree builds be used for local paths? + self.in_tree_build = in_tree_build - logger.critical('Could not find download directory') - raise InstallationError( - "Could not find or access download directory '{}'" - .format(self.download_dir)) + # Memoized downloaded files, as mapping of url: path. + self._downloaded: Dict[str, str] = {} - def prepare_linked_requirement( + # Previous "header" printed for a link-based InstallRequirement + self._previous_requirement_header = ("", "") + + def _log_preparing_link(self, req: InstallRequirement) -> None: + """Provide context for the requirement being prepared.""" + if req.link.is_file and not req.original_link_is_in_wheel_cache: + message = "Processing %s" + information = str(display_path(req.link.file_path)) + else: + message = "Collecting %s" + information = str(req.req or req) + + if (message, information) != self._previous_requirement_header: + self._previous_requirement_header = (message, information) + logger.info(message, information) + + if req.original_link_is_in_wheel_cache: + with indent_log(): + logger.info("Using cached %s", req.link.filename) + + def _ensure_link_req_src_dir( + self, req: InstallRequirement, parallel_builds: bool + ) -> None: + """Ensure source_dir of a linked InstallRequirement.""" + # Since source_dir is only set for editable requirements. + if req.link.is_wheel: + # We don't need to unpack wheels, so no need for a source + # directory. + return + assert req.source_dir is None + if req.link.is_existing_dir() and self.in_tree_build: + # build local directories in-tree + req.source_dir = req.link.file_path + return + + # We always delete unpacked sdists after pip runs. + req.ensure_has_source_dir( + self.build_dir, + autodelete=True, + parallel_builds=parallel_builds, + ) + + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req.source_dir` + # TODO: this check is now probably dead code + if is_installable_dir(req.source_dir): + raise PreviousBuildDirError( + "pip can't proceed with requirements '{}' due to a" + "pre-existing build directory ({}). This is likely " + "due to a previous installation that failed . pip is " + "being responsible and not assuming it can delete this. " + "Please delete it and try again.".format(req, req.source_dir) + ) + + def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes: + # By the time this is called, the requirement's link should have + # been checked so we can tell what kind of requirements req is + # and raise some more informative errors than otherwise. + # (For example, we can raise VcsHashUnsupported for a VCS URL + # rather than HashMissing.) + if not self.require_hashes: + return req.hashes(trust_internet=True) + + # We could check these first 2 conditions inside unpack_url + # and save repetition of conditions, but then we would + # report less-useful error messages for unhashable + # requirements, complaining that there's no hash provided. + if req.link.is_vcs: + raise VcsHashUnsupported() + if req.link.is_existing_dir(): + raise DirectoryUrlHashUnsupported() + + # Unpinned packages are asking for trouble when a new version + # is uploaded. This isn't a security check, but it saves users + # a surprising hash mismatch in the future. + # file:/// URLs aren't pinnable, so don't complain about them + # not being pinned. + if req.original_link is None and not req.is_pinned: + raise HashUnpinned() + + # If known-good hashes are missing for this requirement, + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + return req.hashes(trust_internet=False) or MissingHashes() + + def _fetch_metadata_using_lazy_wheel( + self, + link: Link, + ) -> Optional[BaseDistribution]: + """Fetch metadata using lazy wheel, if possible.""" + if not self.use_lazy_wheel: + return None + if self.require_hashes: + logger.debug("Lazy wheel is not used as hash checking is required") + return None + if link.is_file or not link.is_wheel: + logger.debug( + "Lazy wheel is not used as %r does not points to a remote wheel", + link, + ) + return None + + wheel = Wheel(link.filename) + name = canonicalize_name(wheel.name) + logger.info( + "Obtaining dependency information from %s %s", + name, + wheel.version, + ) + url = link.url.split("#", 1)[0] + try: + return dist_from_wheel_url(name, url, self._session) + except HTTPRangeRequestUnsupported: + logger.debug("%s does not support range requests", url) + return None + + def _complete_partial_requirements( self, - req, # type: InstallRequirement - ): - # type: (...) -> AbstractDistribution - """Prepare a requirement that would be obtained from req.link - """ + partially_downloaded_reqs: Iterable[InstallRequirement], + parallel_builds: bool = False, + ) -> None: + """Download any requirements which were only fetched by metadata.""" + # Download to a temporary directory. These will be copied over as + # needed for downstream 'download', 'wheel', and 'install' commands. + temp_dir = TempDirectory(kind="unpack", globally_managed=True).path + + # Map each link to the requirement that owns it. This allows us to set + # `req.local_file_path` on the appropriate requirement after passing + # all the links at once into BatchDownloader. + links_to_fully_download: Dict[Link, InstallRequirement] = {} + for req in partially_downloaded_reqs: + assert req.link + links_to_fully_download[req.link] = req + + batch_download = self._batch_download( + links_to_fully_download.keys(), + temp_dir, + ) + for link, (filepath, _) in batch_download: + logger.debug("Downloading link %s to %s", link, filepath) + req = links_to_fully_download[link] + req.local_file_path = filepath + + # This step is necessary to ensure all lazy wheels are processed + # successfully by the 'download', 'wheel', and 'install' commands. + for req in partially_downloaded_reqs: + self._prepare_linked_requirement(req, parallel_builds) + + def prepare_linked_requirement( + self, req: InstallRequirement, parallel_builds: bool = False + ) -> BaseDistribution: + """Prepare a requirement to be obtained from req.link.""" assert req.link link = req.link - - # TODO: Breakup into smaller functions - if link.scheme == 'file': - path = link.file_path - logger.info('Processing %s', display_path(path)) - else: - logger.info('Collecting %s', req.req or req) - - download_dir = self.download_dir - if link.is_wheel and self.wheel_download_dir: - # when doing 'pip wheel` we download wheels to a - # dedicated dir. - download_dir = self.wheel_download_dir - - if link.is_wheel: - if download_dir: - # When downloading, we only unpack wheels to get - # metadata. - autodelete_unpacked = True + self._log_preparing_link(req) + with indent_log(): + # Check if the relevant file is already available + # in the download directory + file_path = None + if self.download_dir is not None and link.is_wheel: + hashes = self._get_linked_req_hashes(req) + file_path = _check_download_dir(req.link, self.download_dir, hashes) + + if file_path is not None: + # The file is already available, so mark it as downloaded + self._downloaded[req.link.url] = file_path else: - # When installing a wheel, we use the unpacked - # wheel. - autodelete_unpacked = False - else: - # We always delete unpacked sdists after pip runs. - autodelete_unpacked = True + # The file is not available, attempt to fetch only metadata + wheel_dist = self._fetch_metadata_using_lazy_wheel(link) + if wheel_dist is not None: + req.needs_more_preparation = True + return wheel_dist + + # None of the optimizations worked, fully prepare the requirement + return self._prepare_linked_requirement(req, parallel_builds) + + def prepare_linked_requirements_more( + self, reqs: Iterable[InstallRequirement], parallel_builds: bool = False + ) -> None: + """Prepare linked requirements more, if needed.""" + reqs = [req for req in reqs if req.needs_more_preparation] + for req in reqs: + # Determine if any of these requirements were already downloaded. + if self.download_dir is not None and req.link.is_wheel: + hashes = self._get_linked_req_hashes(req) + file_path = _check_download_dir(req.link, self.download_dir, hashes) + if file_path is not None: + self._downloaded[req.link.url] = file_path + req.needs_more_preparation = False + + # Prepare requirements we found were already downloaded for some + # reason. The other downloads will be completed separately. + partially_downloaded_reqs: List[InstallRequirement] = [] + for req in reqs: + if req.needs_more_preparation: + partially_downloaded_reqs.append(req) + else: + self._prepare_linked_requirement(req, parallel_builds) - with indent_log(): - # Since source_dir is only set for editable requirements. - assert req.source_dir is None - req.ensure_has_source_dir(self.build_dir, autodelete_unpacked) - # If a checkout exists, it's unwise to keep going. version - # inconsistencies are logged later, but do not fail the - # installation. - # FIXME: this won't upgrade when there's an existing - # package unpacked in `req.source_dir` - if os.path.exists(os.path.join(req.source_dir, 'setup.py')): - raise PreviousBuildDirError( - "pip can't proceed with requirements '{}' due to a" - " pre-existing build directory ({}). This is " - "likely due to a previous installation that failed" - ". pip is being responsible and not assuming it " - "can delete this. Please delete it and try again." - .format(req, req.source_dir) - ) + # TODO: separate this part out from RequirementPreparer when the v1 + # resolver can be removed! + self._complete_partial_requirements( + partially_downloaded_reqs, + parallel_builds=parallel_builds, + ) - # Now that we have the real link, we can tell what kind of - # requirements we have and raise some more informative errors - # than otherwise. (For example, we can raise VcsHashUnsupported - # for a VCS URL rather than HashMissing.) - if self.require_hashes: - # We could check these first 2 conditions inside - # unpack_url and save repetition of conditions, but then - # we would report less-useful error messages for - # unhashable requirements, complaining that there's no - # hash provided. - if link.is_vcs: - raise VcsHashUnsupported() - elif link.is_existing_dir(): - raise DirectoryUrlHashUnsupported() - if not req.original_link and not req.is_pinned: - # Unpinned packages are asking for trouble when a new - # version is uploaded. This isn't a security check, but - # it saves users a surprising hash mismatch in the - # future. - # - # file:/// URLs aren't pinnable, so don't complain - # about them not being pinned. - raise HashUnpinned() - - hashes = req.hashes(trust_internet=not self.require_hashes) - if self.require_hashes and not hashes: - # Known-good hashes are missing for this requirement, so - # shim it with a facade object that will provoke hash - # computation and then raise a HashMissing exception - # showing the user what the hash should be. - hashes = MissingHashes() + def _prepare_linked_requirement( + self, req: InstallRequirement, parallel_builds: bool + ) -> BaseDistribution: + assert req.link + link = req.link + + self._ensure_link_req_src_dir(req, parallel_builds) + hashes = self._get_linked_req_hashes(req) + if link.is_existing_dir() and self.in_tree_build: + local_file = None + elif link.url not in self._downloaded: try: local_file = unpack_url( - link, req.source_dir, self.downloader, download_dir, - hashes=hashes, - ) - except requests.HTTPError as exc: - logger.critical( - 'Could not install requirement %s because of error %s', - req, - exc, + link, req.source_dir, self._download, self.download_dir, hashes ) + except NetworkConnectionError as exc: raise InstallationError( - 'Could not install requirement {} because of HTTP ' - 'error {} for URL {}'.format(req, exc, link) + "Could not install requirement {} because of HTTP " + "error {} for URL {}".format(req, exc, link) ) + else: + file_path = self._downloaded[link.url] + if hashes: + hashes.check_against_path(file_path) + local_file = File(file_path, content_type=None) + + # For use in later processing, + # preserve the file path on the requirement. + if local_file: + req.local_file_path = local_file.path + + dist = _get_prepared_distribution( + req, + self.req_tracker, + self.finder, + self.build_isolation, + ) + return dist - # For use in later processing, preserve the file path on the - # requirement. - if local_file: - req.local_file_path = local_file.path - - abstract_dist = _get_prepared_distribution( - req, self.req_tracker, self.finder, self.build_isolation, + def save_linked_requirement(self, req: InstallRequirement) -> None: + assert self.download_dir is not None + assert req.link is not None + link = req.link + if link.is_vcs or (link.is_existing_dir() and req.editable): + # Make a .zip of the source_dir we already created. + req.archive(self.download_dir) + return + + if link.is_existing_dir(): + logger.debug( + "Not copying link to destination directory " + "since it is a directory: %s", + link, ) + return + if req.local_file_path is None: + # No distribution was downloaded for this requirement. + return - if download_dir: - if link.is_existing_dir(): - logger.info('Link is a directory, ignoring download_dir') - elif local_file: - download_location = os.path.join( - download_dir, link.filename - ) - if not os.path.exists(download_location): - shutil.copy(local_file.path, download_location) - logger.info( - 'Saved %s', display_path(download_location) - ) - - if self._download_should_save: - # Make a .zip of the source_dir we already created. - if link.is_vcs: - req.archive(self.download_dir) - return abstract_dist + download_location = os.path.join(self.download_dir, link.filename) + if not os.path.exists(download_location): + shutil.copy(req.local_file_path, download_location) + download_path = display_path(download_location) + logger.info("Saved %s", download_path) def prepare_editable_requirement( self, - req, # type: InstallRequirement - ): - # type: (...) -> AbstractDistribution - """Prepare an editable requirement - """ + req: InstallRequirement, + ) -> BaseDistribution: + """Prepare an editable requirement.""" assert req.editable, "cannot prepare a non-editable req as editable" - logger.info('Obtaining %s', req) + logger.info("Obtaining %s", req) with indent_log(): if self.require_hashes: raise InstallationError( - 'The editable requirement {} cannot be installed when ' - 'requiring hashes, because there is no single file to ' - 'hash.'.format(req) + "The editable requirement {} cannot be installed when " + "requiring hashes, because there is no single file to " + "hash.".format(req) ) req.ensure_has_source_dir(self.src_dir) - req.update_editable(not self._download_should_save) + req.update_editable() - abstract_dist = _get_prepared_distribution( - req, self.req_tracker, self.finder, self.build_isolation, + dist = _get_prepared_distribution( + req, + self.req_tracker, + self.finder, + self.build_isolation, ) - if self._download_should_save: - req.archive(self.download_dir) req.check_if_exists(self.use_user_site) - return abstract_dist + return dist def prepare_installed_requirement( self, - req, # type: InstallRequirement - skip_reason # type: str - ): - # type: (...) -> AbstractDistribution - """Prepare an already-installed requirement - """ + req: InstallRequirement, + skip_reason: str, + ) -> BaseDistribution: + """Prepare an already-installed requirement.""" assert req.satisfied_by, "req should have been satisfied but isn't" assert skip_reason is not None, ( "did not get skip reason skipped but req.satisfied_by " "is set to {}".format(req.satisfied_by) ) logger.info( - 'Requirement %s: %s (%s)', - skip_reason, req, req.satisfied_by.version + "Requirement %s: %s (%s)", skip_reason, req, req.satisfied_by.version ) with indent_log(): if self.require_hashes: logger.debug( - 'Since it is already installed, we are trusting this ' - 'package without checking its hash. To ensure a ' - 'completely repeatable environment, install into an ' - 'empty virtualenv.' + "Since it is already installed, we are trusting this " + "package without checking its hash. To ensure a " + "completely repeatable environment, install into an " + "empty virtualenv." ) - abstract_dist = InstalledDistribution(req) - - return abstract_dist + return InstalledDistribution(req).get_metadata_distribution() diff --git a/venv/Lib/site-packages/pip/_internal/pyproject.py b/venv/Lib/site-packages/pip/_internal/pyproject.py index 6b4faf7a7527cecf3fdd1cb32f8193d358f3c8fe..31534a3a9d307c220dc6a68d9183f40b3e5fd8e5 100644 --- a/venv/Lib/site-packages/pip/_internal/pyproject.py +++ b/venv/Lib/site-packages/pip/_internal/pyproject.py @@ -1,51 +1,29 @@ -from __future__ import absolute_import - -import io import os -import sys from collections import namedtuple +from typing import Any, List, Optional -from pip._vendor import six, toml +from pip._vendor import tomli from pip._vendor.packaging.requirements import InvalidRequirement, Requirement from pip._internal.exceptions import InstallationError -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Any, Optional, List -def _is_list_of_str(obj): - # type: (Any) -> bool - return ( - isinstance(obj, list) and - all(isinstance(item, six.string_types) for item in obj) - ) - +def _is_list_of_str(obj: Any) -> bool: + return isinstance(obj, list) and all(isinstance(item, str) for item in obj) -def make_pyproject_path(unpacked_source_directory): - # type: (str) -> str - path = os.path.join(unpacked_source_directory, 'pyproject.toml') - # Python2 __file__ should not be unicode - if six.PY2 and isinstance(path, six.text_type): - path = path.encode(sys.getfilesystemencoding()) +def make_pyproject_path(unpacked_source_directory: str) -> str: + return os.path.join(unpacked_source_directory, "pyproject.toml") - return path - -BuildSystemDetails = namedtuple('BuildSystemDetails', [ - 'requires', 'backend', 'check', 'backend_path' -]) +BuildSystemDetails = namedtuple( + "BuildSystemDetails", ["requires", "backend", "check", "backend_path"] +) def load_pyproject_toml( - use_pep517, # type: Optional[bool] - pyproject_toml, # type: str - setup_py, # type: str - req_name # type: str -): - # type: (...) -> Optional[BuildSystemDetails] + use_pep517: Optional[bool], pyproject_toml: str, setup_py: str, req_name: str +) -> Optional[BuildSystemDetails]: """Load the pyproject.toml file. Parameters: @@ -70,9 +48,15 @@ def load_pyproject_toml( has_pyproject = os.path.isfile(pyproject_toml) has_setup = os.path.isfile(setup_py) + if not has_pyproject and not has_setup: + raise InstallationError( + f"{req_name} does not appear to be a Python project: " + f"neither 'setup.py' nor 'pyproject.toml' found." + ) + if has_pyproject: - with io.open(pyproject_toml, encoding="utf-8") as f: - pp_toml = toml.load(f) + with open(pyproject_toml, encoding="utf-8") as f: + pp_toml = tomli.load(f) build_system = pp_toml.get("build-system") else: build_system = None @@ -95,9 +79,7 @@ def load_pyproject_toml( raise InstallationError( "Disabling PEP 517 processing is invalid: " "project specifies a build backend of {} " - "in pyproject.toml".format( - build_system["build-backend"] - ) + "in pyproject.toml".format(build_system["build-backend"]) ) use_pep517 = True @@ -145,19 +127,24 @@ def load_pyproject_toml( # Specifying the build-system table but not the requires key is invalid if "requires" not in build_system: raise InstallationError( - error_template.format(package=req_name, reason=( - "it has a 'build-system' table but not " - "'build-system.requires' which is mandatory in the table" - )) + error_template.format( + package=req_name, + reason=( + "it has a 'build-system' table but not " + "'build-system.requires' which is mandatory in the table" + ), + ) ) # Error out if requires is not a list of strings requires = build_system["requires"] if not _is_list_of_str(requires): - raise InstallationError(error_template.format( - package=req_name, - reason="'build-system.requires' is not a list of strings.", - )) + raise InstallationError( + error_template.format( + package=req_name, + reason="'build-system.requires' is not a list of strings.", + ) + ) # Each requirement must be valid as per PEP 508 for requirement in requires: @@ -176,7 +163,7 @@ def load_pyproject_toml( backend = build_system.get("build-backend") backend_path = build_system.get("backend-path", []) - check = [] # type: List[str] + check: List[str] = [] if backend is None: # If the user didn't specify a backend, we assume they want to use # the setuptools backend. But we can't be sure they have included diff --git a/venv/Lib/site-packages/pip/_internal/req/__init__.py b/venv/Lib/site-packages/pip/_internal/req/__init__.py index d2d027adeec4dbedaf62f95b070d7fd9f1fbbe60..70dea27a6a8a181e62516fea5211c95a2ce911b9 100644 --- a/venv/Lib/site-packages/pip/_internal/req/__init__.py +++ b/venv/Lib/site-packages/pip/_internal/req/__init__.py @@ -1,92 +1,94 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - +import collections import logging +from typing import Iterator, List, Optional, Sequence, Tuple from pip._internal.utils.logging import indent_log -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from .req_file import parse_requirements from .req_install import InstallRequirement from .req_set import RequirementSet -if MYPY_CHECK_RUNNING: - from typing import Any, List, Sequence - __all__ = [ - "RequirementSet", "InstallRequirement", - "parse_requirements", "install_given_reqs", + "RequirementSet", + "InstallRequirement", + "parse_requirements", + "install_given_reqs", ] logger = logging.getLogger(__name__) -class InstallationResult(object): - def __init__(self, name): - # type: (str) -> None +class InstallationResult: + def __init__(self, name: str) -> None: self.name = name - def __repr__(self): - # type: () -> str - return "InstallationResult(name={!r})".format(self.name) + def __repr__(self) -> str: + return f"InstallationResult(name={self.name!r})" + + +def _validate_requirements( + requirements: List[InstallRequirement], +) -> Iterator[Tuple[str, InstallRequirement]]: + for req in requirements: + assert req.name, f"invalid to-be-installed requirement: {req}" + yield req.name, req def install_given_reqs( - to_install, # type: List[InstallRequirement] - install_options, # type: List[str] - global_options=(), # type: Sequence[str] - *args, # type: Any - **kwargs # type: Any -): - # type: (...) -> List[InstallationResult] + requirements: List[InstallRequirement], + install_options: List[str], + global_options: Sequence[str], + root: Optional[str], + home: Optional[str], + prefix: Optional[str], + warn_script_location: bool, + use_user_site: bool, + pycompile: bool, +) -> List[InstallationResult]: """ Install everything in the given list. (to be called after having downloaded and unpacked the packages) """ + to_install = collections.OrderedDict(_validate_requirements(requirements)) if to_install: logger.info( - 'Installing collected packages: %s', - ', '.join([req.name for req in to_install]), + "Installing collected packages: %s", + ", ".join(to_install.keys()), ) installed = [] with indent_log(): - for requirement in to_install: + for req_name, requirement in to_install.items(): if requirement.should_reinstall: - logger.info('Attempting uninstall: %s', requirement.name) + logger.info("Attempting uninstall: %s", req_name) with indent_log(): - uninstalled_pathset = requirement.uninstall( - auto_confirm=True - ) + uninstalled_pathset = requirement.uninstall(auto_confirm=True) + else: + uninstalled_pathset = None + try: requirement.install( install_options, global_options, - *args, - **kwargs + root=root, + home=home, + prefix=prefix, + warn_script_location=warn_script_location, + use_user_site=use_user_site, + pycompile=pycompile, ) except Exception: - should_rollback = ( - requirement.should_reinstall and - not requirement.install_succeeded - ) # if install did not succeed, rollback previous uninstall - if should_rollback: + if uninstalled_pathset and not requirement.install_succeeded: uninstalled_pathset.rollback() raise else: - should_commit = ( - requirement.should_reinstall and - requirement.install_succeeded - ) - if should_commit: + if uninstalled_pathset and requirement.install_succeeded: uninstalled_pathset.commit() - installed.append(InstallationResult(requirement.name)) + installed.append(InstallationResult(req_name)) return installed diff --git a/venv/Lib/site-packages/pip/_internal/req/constructors.py b/venv/Lib/site-packages/pip/_internal/req/constructors.py index c9f1fe713967519345d9c6fcfc8cb77f8e89571c..4a594037fd12960dbed00148b7b805f5883b5939 100644 --- a/venv/Lib/site-packages/pip/_internal/req/constructors.py +++ b/venv/Lib/site-packages/pip/_internal/req/constructors.py @@ -8,12 +8,10 @@ These are meant to be used elsewhere within pip to create instances of InstallRequirement. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import logging import os import re +from typing import Any, Dict, Optional, Set, Tuple, Union from pip._vendor.packaging.markers import Marker from pip._vendor.packaging.requirements import InvalidRequirement, Requirement @@ -24,42 +22,26 @@ from pip._internal.exceptions import InstallationError from pip._internal.models.index import PyPI, TestPyPI from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel -from pip._internal.pyproject import make_pyproject_path +from pip._internal.req.req_file import ParsedRequirement from pip._internal.req.req_install import InstallRequirement -from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS -from pip._internal.utils.misc import is_installable_dir, splitext -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.filetypes import is_archive_file +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.packaging import get_requirement from pip._internal.utils.urls import path_to_url from pip._internal.vcs import is_url, vcs -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Dict, Optional, Set, Tuple, Union, - ) - from pip._internal.req.req_file import ParsedRequirement - - __all__ = [ - "install_req_from_editable", "install_req_from_line", - "parse_editable" + "install_req_from_editable", + "install_req_from_line", + "parse_editable", ] logger = logging.getLogger(__name__) operators = Specifier._operators.keys() -def is_archive_file(name): - # type: (str) -> bool - """Return True if `name` is a considered as an archive file.""" - ext = splitext(name)[1].lower() - if ext in ARCHIVE_EXTENSIONS: - return True - return False - - -def _strip_extras(path): - # type: (str) -> Tuple[str, Optional[str]] - m = re.match(r'^(.+)(\[[^\]]+\])$', path) +def _strip_extras(path: str) -> Tuple[str, Optional[str]]: + m = re.match(r"^(.+)(\[[^\]]+\])$", path) extras = None if m: path_no_extras = m.group(1) @@ -70,15 +52,13 @@ def _strip_extras(path): return path_no_extras, extras -def convert_extras(extras): - # type: (Optional[str]) -> Set[str] +def convert_extras(extras: Optional[str]) -> Set[str]: if not extras: return set() - return Requirement("placeholder" + extras.lower()).extras + return get_requirement("placeholder" + extras.lower()).extras -def parse_editable(editable_req): - # type: (str) -> Tuple[Optional[str], str, Optional[Set[str]]] +def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]: """Parses an editable requirement into: - a requirement name - an URL @@ -95,65 +75,45 @@ def parse_editable(editable_req): url_no_extras, extras = _strip_extras(url) if os.path.isdir(url_no_extras): - if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): - msg = ( - 'File "setup.py" not found. Directory cannot be installed ' - 'in editable mode: {}'.format(os.path.abspath(url_no_extras)) - ) - pyproject_path = make_pyproject_path(url_no_extras) - if os.path.isfile(pyproject_path): - msg += ( - '\n(A "pyproject.toml" file was found, but editable ' - 'mode currently requires a setup.py based build.)' - ) - raise InstallationError(msg) - # Treating it as code that has already been checked out url_no_extras = path_to_url(url_no_extras) - if url_no_extras.lower().startswith('file:'): + if url_no_extras.lower().startswith("file:"): package_name = Link(url_no_extras).egg_fragment if extras: return ( package_name, url_no_extras, - Requirement("placeholder" + extras.lower()).extras, + get_requirement("placeholder" + extras.lower()).extras, ) else: - return package_name, url_no_extras, None + return package_name, url_no_extras, set() for version_control in vcs: - if url.lower().startswith('{}:'.format(version_control)): - url = '{}+{}'.format(version_control, url) + if url.lower().startswith(f"{version_control}:"): + url = f"{version_control}+{url}" break - if '+' not in url: + link = Link(url) + + if not link.is_vcs: + backends = ", ".join(vcs.all_schemes) raise InstallationError( - '{} is not a valid editable requirement. ' - 'It should either be a path to a local project or a VCS URL ' - '(beginning with svn+, git+, hg+, or bzr+).'.format(editable_req) + f"{editable_req} is not a valid editable requirement. " + f"It should either be a path to a local project or a VCS URL " + f"(beginning with {backends})." ) - vc_type = url.split('+', 1)[0].lower() - - if not vcs.get_backend(vc_type): - backends = ", ".join([bends.name + '+URL' for bends in vcs.backends]) - error_message = "For --editable={}, " \ - "only {} are currently supported".format( - editable_req, backends) - raise InstallationError(error_message) - - package_name = Link(url).egg_fragment + package_name = link.egg_fragment if not package_name: raise InstallationError( "Could not detect requirement name for '{}', please specify one " "with #egg=your_package_name".format(editable_req) ) - return package_name, url, None + return package_name, url, set() -def deduce_helpful_msg(req): - # type: (str) -> str +def deduce_helpful_msg(req: str) -> str: """Returns helpful msg in case requirements file does not exist, or cannot be parsed. @@ -161,10 +121,10 @@ def deduce_helpful_msg(req): """ msg = "" if os.path.exists(req): - msg = " It does exist." + msg = " The path does exist. " # Try to parse and check if it is a requirements file. try: - with open(req, 'r') as fp: + with open(req) as fp: # parse first line only next(parse_requirements(fp.read())) msg += ( @@ -175,20 +135,19 @@ def deduce_helpful_msg(req): " the packages specified within it." ).format(req) except RequirementParseError: - logger.debug("Cannot parse '{}' as requirements \ - file".format(req), exc_info=True) + logger.debug("Cannot parse '%s' as requirements file", req, exc_info=True) else: - msg += " File '{}' does not exist.".format(req) + msg += f" File '{req}' does not exist." return msg -class RequirementParts(object): +class RequirementParts: def __init__( - self, - requirement, # type: Optional[Requirement] - link, # type: Optional[Link] - markers, # type: Optional[Marker] - extras, # type: Set[str] + self, + requirement: Optional[Requirement], + link: Optional[Link], + markers: Optional[Marker], + extras: Set[str], ): self.requirement = requirement self.link = link @@ -196,15 +155,14 @@ class RequirementParts(object): self.extras = extras -def parse_req_from_editable(editable_req): - # type: (str) -> RequirementParts +def parse_req_from_editable(editable_req: str) -> RequirementParts: name, url, extras_override = parse_editable(editable_req) if name is not None: try: - req = Requirement(name) + req: Optional[Requirement] = Requirement(name) except InvalidRequirement: - raise InstallationError("Invalid requirement: '{}'".format(name)) + raise InstallationError(f"Invalid requirement: '{name}'") else: req = None @@ -217,21 +175,24 @@ def parse_req_from_editable(editable_req): def install_req_from_editable( - editable_req, # type: str - comes_from=None, # type: Optional[Union[InstallRequirement, str]] - use_pep517=None, # type: Optional[bool] - isolated=False, # type: bool - options=None, # type: Optional[Dict[str, Any]] - constraint=False # type: bool -): - # type: (...) -> InstallRequirement + editable_req: str, + comes_from: Optional[Union[InstallRequirement, str]] = None, + use_pep517: Optional[bool] = None, + isolated: bool = False, + options: Optional[Dict[str, Any]] = None, + constraint: bool = False, + user_supplied: bool = False, + permit_editable_wheels: bool = False, +) -> InstallRequirement: parts = parse_req_from_editable(editable_req) return InstallRequirement( parts.requirement, comes_from=comes_from, + user_supplied=user_supplied, editable=True, + permit_editable_wheels=permit_editable_wheels, link=parts.link, constraint=constraint, use_pep517=use_pep517, @@ -243,8 +204,7 @@ def install_req_from_editable( ) -def _looks_like_path(name): - # type: (str) -> bool +def _looks_like_path(name: str) -> bool: """Checks whether the string "looks like" a path on the filesystem. This does not check whether the target actually exists, only judge from the @@ -263,11 +223,10 @@ def _looks_like_path(name): return False -def _get_url_from_path(path, name): - # type: (str, str) -> str +def _get_url_from_path(path: str, name: str) -> Optional[str]: """ - First, it checks whether a provided path is an installable directory - (e.g. it has a setup.py). If it is, returns the path. + First, it checks whether a provided path is an installable directory. If it + is, returns the path. If false, check if the path is an archive file (such as a .whl). The function checks if the path is a file. If false, if the path has @@ -276,33 +235,33 @@ def _get_url_from_path(path, name): if _looks_like_path(name) and os.path.isdir(path): if is_installable_dir(path): return path_to_url(path) + # TODO: The is_installable_dir test here might not be necessary + # now that it is done in load_pyproject_toml too. raise InstallationError( - "Directory {name!r} is not installable. Neither 'setup.py' " - "nor 'pyproject.toml' found.".format(**locals()) + f"Directory {name!r} is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found." ) if not is_archive_file(path): return None if os.path.isfile(path): return path_to_url(path) - urlreq_parts = name.split('@', 1) + urlreq_parts = name.split("@", 1) if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]): # If the path contains '@' and the part before it does not look # like a path, try to treat it as a PEP 440 URL req instead. return None logger.warning( - 'Requirement %r looks like a filename, but the ' - 'file does not exist', - name + "Requirement %r looks like a filename, but the file does not exist", + name, ) return path_to_url(path) -def parse_req_from_line(name, line_source): - # type: (str, Optional[str]) -> RequirementParts +def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementParts: if is_url(name): - marker_sep = '; ' + marker_sep = "; " else: - marker_sep = ';' + marker_sep = ";" if marker_sep in name: name, markers_as_string = name.split(marker_sep, 1) markers_as_string = markers_as_string.strip() @@ -329,13 +288,12 @@ def parse_req_from_line(name, line_source): # it's a local file, dir, or url if link: # Handle relative file URLs - if link.scheme == 'file' and re.search(r'\.\./', link.url): - link = Link( - path_to_url(os.path.normpath(os.path.abspath(link.path)))) + if link.scheme == "file" and re.search(r"\.\./", link.url): + link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path)))) # wheel file if link.is_wheel: wheel = Wheel(link.filename) # can raise InvalidWheelFilename - req_as_string = "{wheel.name}=={wheel.version}".format(**locals()) + req_as_string = f"{wheel.name}=={wheel.version}" else: # set the req to the egg fragment. when it's not there, this # will become an 'unnamed' requirement @@ -347,30 +305,42 @@ def parse_req_from_line(name, line_source): extras = convert_extras(extras_as_string) - def with_source(text): - # type: (str) -> str + def with_source(text: str) -> str: if not line_source: return text - return '{} (from {})'.format(text, line_source) + return f"{text} (from {line_source})" - if req_as_string is not None: + def _parse_req_string(req_as_string: str) -> Requirement: try: - req = Requirement(req_as_string) + req = get_requirement(req_as_string) except InvalidRequirement: if os.path.sep in req_as_string: add_msg = "It looks like a path." add_msg += deduce_helpful_msg(req_as_string) - elif ('=' in req_as_string and - not any(op in req_as_string for op in operators)): + elif "=" in req_as_string and not any( + op in req_as_string for op in operators + ): add_msg = "= is not a valid operator. Did you mean == ?" else: - add_msg = '' - msg = with_source( - 'Invalid requirement: {!r}'.format(req_as_string) - ) + add_msg = "" + msg = with_source(f"Invalid requirement: {req_as_string!r}") if add_msg: - msg += '\nHint: {}'.format(add_msg) + msg += f"\nHint: {add_msg}" raise InstallationError(msg) + else: + # Deprecate extras after specifiers: "name>=1.0[extras]" + # This currently works by accident because _strip_extras() parses + # any extras in the end of the string and those are saved in + # RequirementParts + for spec in req.specifier: + spec_str = str(spec) + if spec_str.endswith("]"): + msg = f"Extras after version '{spec_str}'." + raise InstallationError(msg) + return req + + if req_as_string is not None: + req: Optional[Requirement] = _parse_req_string(req_as_string) else: req = None @@ -378,15 +348,15 @@ def parse_req_from_line(name, line_source): def install_req_from_line( - name, # type: str - comes_from=None, # type: Optional[Union[str, InstallRequirement]] - use_pep517=None, # type: Optional[bool] - isolated=False, # type: bool - options=None, # type: Optional[Dict[str, Any]] - constraint=False, # type: bool - line_source=None, # type: Optional[str] -): - # type: (...) -> InstallRequirement + name: str, + comes_from: Optional[Union[str, InstallRequirement]] = None, + use_pep517: Optional[bool] = None, + isolated: bool = False, + options: Optional[Dict[str, Any]] = None, + constraint: bool = False, + line_source: Optional[str] = None, + user_supplied: bool = False, +) -> InstallRequirement: """Creates an InstallRequirement from a name, which might be a requirement, directory containing 'setup.py', filename, or URL. @@ -396,34 +366,43 @@ def install_req_from_line( parts = parse_req_from_line(name, line_source) return InstallRequirement( - parts.requirement, comes_from, link=parts.link, markers=parts.markers, - use_pep517=use_pep517, isolated=isolated, + parts.requirement, + comes_from, + link=parts.link, + markers=parts.markers, + use_pep517=use_pep517, + isolated=isolated, install_options=options.get("install_options", []) if options else [], global_options=options.get("global_options", []) if options else [], hash_options=options.get("hashes", {}) if options else {}, constraint=constraint, extras=parts.extras, + user_supplied=user_supplied, ) def install_req_from_req_string( - req_string, # type: str - comes_from=None, # type: Optional[InstallRequirement] - isolated=False, # type: bool - use_pep517=None # type: Optional[bool] -): - # type: (...) -> InstallRequirement + req_string: str, + comes_from: Optional[InstallRequirement] = None, + isolated: bool = False, + use_pep517: Optional[bool] = None, + user_supplied: bool = False, +) -> InstallRequirement: try: - req = Requirement(req_string) + req = get_requirement(req_string) except InvalidRequirement: - raise InstallationError("Invalid requirement: '{}'".format(req_string)) + raise InstallationError(f"Invalid requirement: '{req_string}'") domains_not_allowed = [ PyPI.file_storage_domain, TestPyPI.file_storage_domain, ] - if (req.url and comes_from and comes_from.link and - comes_from.link.netloc in domains_not_allowed): + if ( + req.url + and comes_from + and comes_from.link + and comes_from.link.netloc in domains_not_allowed + ): # Explicitly disallow pypi packages that depend on external urls raise InstallationError( "Packages installed from PyPI cannot depend on packages " @@ -432,16 +411,20 @@ def install_req_from_req_string( ) return InstallRequirement( - req, comes_from, isolated=isolated, use_pep517=use_pep517 + req, + comes_from, + isolated=isolated, + use_pep517=use_pep517, + user_supplied=user_supplied, ) def install_req_from_parsed_requirement( - parsed_req, # type: ParsedRequirement - isolated=False, # type: bool - use_pep517=None # type: Optional[bool] -): - # type: (...) -> InstallRequirement + parsed_req: ParsedRequirement, + isolated: bool = False, + use_pep517: Optional[bool] = None, + user_supplied: bool = False, +) -> InstallRequirement: if parsed_req.is_editable: req = install_req_from_editable( parsed_req.requirement, @@ -449,6 +432,7 @@ def install_req_from_parsed_requirement( use_pep517=use_pep517, constraint=parsed_req.constraint, isolated=isolated, + user_supplied=user_supplied, ) else: @@ -460,5 +444,23 @@ def install_req_from_parsed_requirement( options=parsed_req.options, constraint=parsed_req.constraint, line_source=parsed_req.line_source, + user_supplied=user_supplied, ) return req + + +def install_req_from_link_and_ireq( + link: Link, ireq: InstallRequirement +) -> InstallRequirement: + return InstallRequirement( + req=ireq.req, + comes_from=ireq.comes_from, + editable=ireq.editable, + link=link, + markers=ireq.markers, + use_pep517=ireq.use_pep517, + isolated=ireq.isolated, + install_options=ireq.install_options, + global_options=ireq.global_options, + hash_options=ireq.hash_options, + ) diff --git a/venv/Lib/site-packages/pip/_internal/req/req_file.py b/venv/Lib/site-packages/pip/_internal/req/req_file.py index 63cab76f6f267c17851e1286c93fa179ad885dcc..03ae50492c585028425beed7a047905dd2f60299 100644 --- a/venv/Lib/site-packages/pip/_internal/req/req_file.py +++ b/venv/Lib/site-packages/pip/_internal/req/req_file.py @@ -2,55 +2,55 @@ Requirements file parsing """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - import optparse import os import re import shlex -import sys - -from pip._vendor.six.moves.urllib import parse as urllib_parse +import urllib.parse +from optparse import Values +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Tuple, +) from pip._internal.cli import cmdoptions -from pip._internal.exceptions import ( - InstallationError, - RequirementsFileParseError, -) +from pip._internal.exceptions import InstallationError, RequirementsFileParseError from pip._internal.models.search_scope import SearchScope +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status from pip._internal.utils.encoding import auto_decode -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import get_url_scheme -if MYPY_CHECK_RUNNING: - from optparse import Values - from typing import ( - Any, Callable, Dict, Iterator, List, NoReturn, Optional, Text, Tuple, - ) +if TYPE_CHECKING: + # NoReturn introduced in 3.6.2; imported only for type checking to maintain + # pip compatibility with older patch versions of Python 3.6 + from typing import NoReturn from pip._internal.index.package_finder import PackageFinder - from pip._internal.network.session import PipSession - - ReqFileLines = Iterator[Tuple[int, Text]] - LineParser = Callable[[Text], Tuple[str, Values]] +__all__ = ["parse_requirements"] +ReqFileLines = Iterable[Tuple[int, str]] -__all__ = ['parse_requirements'] +LineParser = Callable[[str], Tuple[str, Values]] -SCHEME_RE = re.compile(r'^(http|https|file):', re.I) -COMMENT_RE = re.compile(r'(^|\s+)#.*$') +SCHEME_RE = re.compile(r"^(http|https|file):", re.I) +COMMENT_RE = re.compile(r"(^|\s+)#.*$") # Matches environment variable-style values in '${MY_VARIABLE_1}' with the # variable name consisting of only uppercase letters, digits or the '_' # (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, # 2013 Edition. -ENV_VAR_RE = re.compile(r'(?P\$\{(?P[A-Z0-9_]+)\})') +ENV_VAR_RE = re.compile(r"(?P\$\{(?P[A-Z0-9_]+)\})") -SUPPORTED_OPTIONS = [ +SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [ cmdoptions.index_url, cmdoptions.extra_index_url, cmdoptions.no_index, @@ -60,34 +60,34 @@ SUPPORTED_OPTIONS = [ cmdoptions.find_links, cmdoptions.no_binary, cmdoptions.only_binary, + cmdoptions.prefer_binary, cmdoptions.require_hashes, cmdoptions.pre, cmdoptions.trusted_host, - cmdoptions.always_unzip, # Deprecated -] # type: List[Callable[..., optparse.Option]] + cmdoptions.use_new_feature, +] # options to be passed to requirements -SUPPORTED_OPTIONS_REQ = [ +SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [ cmdoptions.install_options, cmdoptions.global_options, cmdoptions.hash, -] # type: List[Callable[..., optparse.Option]] +] # the 'dest' string values SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] -class ParsedRequirement(object): +class ParsedRequirement: def __init__( self, - requirement, # type:str - is_editable, # type: bool - comes_from, # type: str - constraint, # type: bool - options=None, # type: Optional[Dict[str, Any]] - line_source=None, # type: Optional[str] - ): - # type: (...) -> None + requirement: str, + is_editable: bool, + comes_from: str, + constraint: bool, + options: Optional[Dict[str, Any]] = None, + line_source: Optional[str] = None, + ) -> None: self.requirement = requirement self.is_editable = is_editable self.comes_from = comes_from @@ -96,20 +96,17 @@ class ParsedRequirement(object): self.line_source = line_source -class ParsedLine(object): +class ParsedLine: def __init__( self, - filename, # type: str - lineno, # type: int - comes_from, # type: str - args, # type: str - opts, # type: Values - constraint, # type: bool - ): - # type: (...) -> None + filename: str, + lineno: int, + args: str, + opts: Values, + constraint: bool, + ) -> None: self.filename = filename self.lineno = lineno - self.comes_from = comes_from self.opts = opts self.constraint = constraint @@ -127,45 +124,38 @@ class ParsedLine(object): def parse_requirements( - filename, # type: str - session, # type: PipSession - finder=None, # type: Optional[PackageFinder] - comes_from=None, # type: Optional[str] - options=None, # type: Optional[optparse.Values] - constraint=False, # type: bool -): - # type: (...) -> Iterator[ParsedRequirement] - """Parse a requirements file and yield InstallRequirement instances. + filename: str, + session: PipSession, + finder: Optional["PackageFinder"] = None, + options: Optional[optparse.Values] = None, + constraint: bool = False, +) -> Iterator[ParsedRequirement]: + """Parse a requirements file and yield ParsedRequirement instances. :param filename: Path or url of requirements file. :param session: PipSession instance. :param finder: Instance of pip.index.PackageFinder. - :param comes_from: Origin description of requirements. :param options: cli options. :param constraint: If true, parsing a constraint file rather than requirements file. """ line_parser = get_line_parser(finder) - parser = RequirementsFileParser(session, line_parser, comes_from) + parser = RequirementsFileParser(session, line_parser) for parsed_line in parser.parse(filename, constraint): parsed_req = handle_line( - parsed_line, - options=options, - finder=finder, - session=session + parsed_line, options=options, finder=finder, session=session ) if parsed_req is not None: yield parsed_req -def preprocess(content): - # type: (Text) -> ReqFileLines +def preprocess(content: str) -> ReqFileLines: """Split, filter, and join lines, and return a line iterator :param content: the content of the requirements file """ - lines_enum = enumerate(content.splitlines(), start=1) # type: ReqFileLines + lines_enum: ReqFileLines = enumerate(content.splitlines(), start=1) lines_enum = join_lines(lines_enum) lines_enum = ignore_comments(lines_enum) lines_enum = expand_env_variables(lines_enum) @@ -173,14 +163,15 @@ def preprocess(content): def handle_requirement_line( - line, # type: ParsedLine - options=None, # type: Optional[optparse.Values] -): - # type: (...) -> ParsedRequirement + line: ParsedLine, + options: Optional[optparse.Values] = None, +) -> ParsedRequirement: # preserve for the nested code path - line_comes_from = '{} {} (line {})'.format( - '-c' if line.constraint else '-r', line.filename, line.lineno, + line_comes_from = "{} {} (line {})".format( + "-c" if line.constraint else "-r", + line.filename, + line.lineno, ) assert line.is_requirement @@ -205,7 +196,7 @@ def handle_requirement_line( if dest in line.opts.__dict__ and line.opts.__dict__[dest]: req_options[dest] = line.opts.__dict__[dest] - line_source = 'line {} of {}'.format(line.lineno, line.filename) + line_source = f"line {line.lineno} of {line.filename}" return ParsedRequirement( requirement=line.requirement, is_editable=line.is_editable, @@ -217,21 +208,25 @@ def handle_requirement_line( def handle_option_line( - opts, # type: Values - filename, # type: str - lineno, # type: int - finder=None, # type: Optional[PackageFinder] - options=None, # type: Optional[optparse.Values] - session=None, # type: Optional[PipSession] -): - # type: (...) -> None - - # percolate hash-checking option upward - if opts.require_hashes: - options.require_hashes = opts.require_hashes + opts: Values, + filename: str, + lineno: int, + finder: Optional["PackageFinder"] = None, + options: Optional[optparse.Values] = None, + session: Optional[PipSession] = None, +) -> None: + + if options: + # percolate options upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + if opts.features_enabled: + options.features_enabled.extend( + f for f in opts.features_enabled if f not in options.features_enabled + ) # set finder options - elif finder: + if finder: find_links = finder.find_links index_urls = finder.index_urls if opts.index_url: @@ -251,6 +246,10 @@ def handle_option_line( value = relative_to_reqs_file find_links.append(value) + if session: + # We need to update the auth urls in session + session.update_index_urls(index_urls) + search_scope = SearchScope( find_links=find_links, index_urls=index_urls, @@ -260,19 +259,21 @@ def handle_option_line( if opts.pre: finder.set_allow_all_prereleases() + if opts.prefer_binary: + finder.set_prefer_binary() + if session: for host in opts.trusted_hosts or []: - source = 'line {} of {}'.format(lineno, filename) + source = f"line {lineno} of {filename}" session.add_trusted_host(host, source=source) def handle_line( - line, # type: ParsedLine - options=None, # type: Optional[optparse.Values] - finder=None, # type: Optional[PackageFinder] - session=None, # type: Optional[PipSession] -): - # type: (...) -> Optional[ParsedRequirement] + line: ParsedLine, + options: Optional[optparse.Values] = None, + finder: Optional["PackageFinder"] = None, + session: Optional[PipSession] = None, +) -> Optional[ParsedRequirement]: """Handle a single parsed requirements line; This can result in creating/yielding requirements, or updating the finder. @@ -311,31 +312,25 @@ def handle_line( return None -class RequirementsFileParser(object): +class RequirementsFileParser: def __init__( self, - session, # type: PipSession - line_parser, # type: LineParser - comes_from, # type: str - ): - # type: (...) -> None + session: PipSession, + line_parser: LineParser, + ) -> None: self._session = session self._line_parser = line_parser - self._comes_from = comes_from - def parse(self, filename, constraint): - # type: (str, bool) -> Iterator[ParsedLine] - """Parse a given file, yielding parsed lines. - """ - for line in self._parse_and_recurse(filename, constraint): - yield line + def parse(self, filename: str, constraint: bool) -> Iterator[ParsedLine]: + """Parse a given file, yielding parsed lines.""" + yield from self._parse_and_recurse(filename, constraint) - def _parse_and_recurse(self, filename, constraint): - # type: (str, bool) -> Iterator[ParsedLine] + def _parse_and_recurse( + self, filename: str, constraint: bool + ) -> Iterator[ParsedLine]: for line in self._parse_file(filename, constraint): - if ( - not line.is_requirement and - (line.opts.requirements or line.opts.constraints) + if not line.is_requirement and ( + line.opts.requirements or line.opts.constraints ): # parse a nested requirements file if line.opts.requirements: @@ -348,26 +343,21 @@ class RequirementsFileParser(object): # original file is over http if SCHEME_RE.search(filename): # do a url join so relative paths work - req_path = urllib_parse.urljoin(filename, req_path) + req_path = urllib.parse.urljoin(filename, req_path) # original file and nested file are paths elif not SCHEME_RE.search(req_path): # do a join so relative paths work req_path = os.path.join( - os.path.dirname(filename), req_path, + os.path.dirname(filename), + req_path, ) - for inner_line in self._parse_and_recurse( - req_path, nested_constraint, - ): - yield inner_line + yield from self._parse_and_recurse(req_path, nested_constraint) else: yield line - def _parse_file(self, filename, constraint): - # type: (str, bool) -> Iterator[ParsedLine] - _, content = get_file_content( - filename, self._session, comes_from=self._comes_from - ) + def _parse_file(self, filename: str, constraint: bool) -> Iterator[ParsedLine]: + _, content = get_file_content(filename, self._session) lines_enum = preprocess(content) @@ -376,23 +366,20 @@ class RequirementsFileParser(object): args_str, opts = self._line_parser(line) except OptionParsingError as e: # add offending line - msg = 'Invalid requirement: {}\n{}'.format(line, e.msg) + msg = f"Invalid requirement: {line}\n{e.msg}" raise RequirementsFileParseError(msg) yield ParsedLine( filename, line_number, - self._comes_from, args_str, opts, constraint, ) -def get_line_parser(finder): - # type: (Optional[PackageFinder]) -> LineParser - def parse_line(line): - # type: (Text) -> Tuple[str, Values] +def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser: + def parse_line(line: str) -> Tuple[str, Values]: # Build new parser for each line since it accumulates appendable # options. parser = build_parser() @@ -402,46 +389,37 @@ def get_line_parser(finder): defaults.format_control = finder.format_control args_str, options_str = break_args_options(line) - # Prior to 2.7.3, shlex cannot deal with unicode entries - if sys.version_info < (2, 7, 3): - # https://github.com/python/mypy/issues/1174 - options_str = options_str.encode('utf8') # type: ignore - # https://github.com/python/mypy/issues/1174 - opts, _ = parser.parse_args( - shlex.split(options_str), defaults) # type: ignore + opts, _ = parser.parse_args(shlex.split(options_str), defaults) return args_str, opts return parse_line -def break_args_options(line): - # type: (Text) -> Tuple[str, Text] +def break_args_options(line: str) -> Tuple[str, str]: """Break up the line into an args and options string. We only want to shlex (and then optparse) the options, not the args. args can contain markers which are corrupted by shlex. """ - tokens = line.split(' ') + tokens = line.split(" ") args = [] options = tokens[:] for token in tokens: - if token.startswith('-') or token.startswith('--'): + if token.startswith("-") or token.startswith("--"): break else: args.append(token) options.pop(0) - return ' '.join(args), ' '.join(options) # type: ignore + return " ".join(args), " ".join(options) class OptionParsingError(Exception): - def __init__(self, msg): - # type: (str) -> None + def __init__(self, msg: str) -> None: self.msg = msg -def build_parser(): - # type: () -> optparse.OptionParser +def build_parser() -> optparse.OptionParser: """ Return a parser for parsing requirement lines """ @@ -454,9 +432,9 @@ def build_parser(): # By default optparse sys.exits on parsing errors. We want to wrap # that in our own exception. - def parser_exit(self, msg): - # type: (Any, str) -> NoReturn + def parser_exit(self: Any, msg: str) -> "NoReturn": raise OptionParsingError(msg) + # NOTE: mypy disallows assigning to a method # https://github.com/python/mypy/issues/2427 parser.exit = parser_exit # type: ignore @@ -464,50 +442,49 @@ def build_parser(): return parser -def join_lines(lines_enum): - # type: (ReqFileLines) -> ReqFileLines +def join_lines(lines_enum: ReqFileLines) -> ReqFileLines: """Joins a line ending in '\' with the previous line (except when following comments). The joined line takes on the index of the first line. """ primary_line_number = None - new_line = [] # type: List[Text] + new_line: List[str] = [] for line_number, line in lines_enum: - if not line.endswith('\\') or COMMENT_RE.match(line): + if not line.endswith("\\") or COMMENT_RE.match(line): if COMMENT_RE.match(line): # this ensures comments are always matched later - line = ' ' + line + line = " " + line if new_line: new_line.append(line) - yield primary_line_number, ''.join(new_line) + assert primary_line_number is not None + yield primary_line_number, "".join(new_line) new_line = [] else: yield line_number, line else: if not new_line: primary_line_number = line_number - new_line.append(line.strip('\\')) + new_line.append(line.strip("\\")) # last line contains \ if new_line: - yield primary_line_number, ''.join(new_line) + assert primary_line_number is not None + yield primary_line_number, "".join(new_line) # TODO: handle space after '\'. -def ignore_comments(lines_enum): - # type: (ReqFileLines) -> ReqFileLines +def ignore_comments(lines_enum: ReqFileLines) -> ReqFileLines: """ Strips comments and filter empty lines. """ for line_number, line in lines_enum: - line = COMMENT_RE.sub('', line) + line = COMMENT_RE.sub("", line) line = line.strip() if line: yield line_number, line -def expand_env_variables(lines_enum): - # type: (ReqFileLines) -> ReqFileLines +def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines: """Replace all environment variables that can be retrieved via `os.getenv`. The only allowed format for environment variables defined in the @@ -534,49 +511,26 @@ def expand_env_variables(lines_enum): yield line_number, line -def get_file_content(url, session, comes_from=None): - # type: (str, PipSession, Optional[str]) -> Tuple[str, Text] +def get_file_content(url: str, session: PipSession) -> Tuple[str, str]: """Gets the content of a file; it may be a filename, file: URL, or http: URL. Returns (location, content). Content is unicode. Respects # -*- coding: declarations on the retrieved files. :param url: File path or url. :param session: PipSession instance. - :param comes_from: Origin description of requirements. """ scheme = get_url_scheme(url) - if scheme in ['http', 'https']: - # FIXME: catch some errors + # Pip has special support for file:// URLs (LocalFSAdapter). + if scheme in ["http", "https", "file"]: resp = session.get(url) - resp.raise_for_status() + raise_for_status(resp) return resp.url, resp.text - elif scheme == 'file': - if comes_from and comes_from.startswith('http'): - raise InstallationError( - 'Requirements file {} references URL {}, ' - 'which is local'.format(comes_from, url) - ) - - path = url.split(':', 1)[1] - path = path.replace('\\', '/') - match = _url_slash_drive_re.match(path) - if match: - path = match.group(1) + ':' + path.split('|', 1)[1] - path = urllib_parse.unquote(path) - if path.startswith('/'): - path = '/' + path.lstrip('/') - url = path - + # Assume this is a bare path. try: - with open(url, 'rb') as f: + with open(url, "rb") as f: content = auto_decode(f.read()) - except IOError as exc: - raise InstallationError( - 'Could not open requirements file: {}'.format(exc) - ) + except OSError as exc: + raise InstallationError(f"Could not open requirements file: {exc}") return url, content - - -_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) diff --git a/venv/Lib/site-packages/pip/_internal/req/req_install.py b/venv/Lib/site-packages/pip/_internal/req/req_install.py index 3b28209b1bd2143173f691a5f8b6804b551019f6..95dacab53ef44fe7983eafd3fcd93aa32f972835 100644 --- a/venv/Lib/site-packages/pip/_internal/req/req_install.py +++ b/venv/Lib/site-packages/pip/_internal/req/req_install.py @@ -1,70 +1,68 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False -from __future__ import absolute_import - +import functools import logging import os import shutil import sys +import uuid import zipfile +from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union -from pip._vendor import pkg_resources, six +from pip._vendor import pkg_resources +from pip._vendor.packaging.markers import Marker from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.packaging.version import Version from pip._vendor.packaging.version import parse as parse_version from pip._vendor.pep517.wrappers import Pep517HookCaller +from pip._vendor.pkg_resources import Distribution -from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment from pip._internal.exceptions import InstallationError from pip._internal.locations import get_scheme from pip._internal.models.link import Link from pip._internal.operations.build.metadata import generate_metadata -from pip._internal.operations.build.metadata_legacy import \ - generate_metadata as generate_metadata_legacy -from pip._internal.operations.install.editable_legacy import \ - install_editable as install_editable_legacy +from pip._internal.operations.build.metadata_editable import generate_editable_metadata +from pip._internal.operations.build.metadata_legacy import ( + generate_metadata as generate_metadata_legacy, +) +from pip._internal.operations.install.editable_legacy import ( + install_editable as install_editable_legacy, +) from pip._internal.operations.install.legacy import LegacyInstallFailure from pip._internal.operations.install.legacy import install as install_legacy from pip._internal.operations.install.wheel import install_wheel from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path from pip._internal.req.req_uninstall import UninstallPathSet from pip._internal.utils.deprecation import deprecated -from pip._internal.utils.direct_url_helpers import direct_url_from_link +from pip._internal.utils.direct_url_helpers import ( + direct_url_for_editable, + direct_url_from_link, +) from pip._internal.utils.hashes import Hashes -from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ( ask_path_exists, backup_dir, display_path, dist_in_site_packages, dist_in_usersite, - get_installed_version, + get_distribution, hide_url, redact_auth_from_url, ) from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.virtualenv import running_under_virtualenv from pip._internal.vcs import vcs -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Dict, Iterable, List, Optional, Sequence, Union, - ) - from pip._internal.build_env import BuildEnvironment - from pip._vendor.pkg_resources import Distribution - from pip._vendor.packaging.specifiers import SpecifierSet - from pip._vendor.packaging.markers import Marker - - logger = logging.getLogger(__name__) -def _get_dist(metadata_directory): - # type: (str) -> Distribution +def _get_dist(metadata_directory: str) -> Distribution: """Return a pkg_resources.Distribution for the provided metadata directory. """ @@ -90,7 +88,7 @@ def _get_dist(metadata_directory): ) -class InstallRequirement(object): +class InstallRequirement: """ Represents something that may be installed later on, may have information about where to fetch the relevant requirement and also contains logic for @@ -99,38 +97,39 @@ class InstallRequirement(object): def __init__( self, - req, # type: Optional[Requirement] - comes_from, # type: Optional[Union[str, InstallRequirement]] - editable=False, # type: bool - link=None, # type: Optional[Link] - markers=None, # type: Optional[Marker] - use_pep517=None, # type: Optional[bool] - isolated=False, # type: bool - install_options=None, # type: Optional[List[str]] - global_options=None, # type: Optional[List[str]] - hash_options=None, # type: Optional[Dict[str, List[str]]] - constraint=False, # type: bool - extras=() # type: Iterable[str] - ): - # type: (...) -> None + req: Optional[Requirement], + comes_from: Optional[Union[str, "InstallRequirement"]], + editable: bool = False, + link: Optional[Link] = None, + markers: Optional[Marker] = None, + use_pep517: Optional[bool] = None, + isolated: bool = False, + install_options: Optional[List[str]] = None, + global_options: Optional[List[str]] = None, + hash_options: Optional[Dict[str, List[str]]] = None, + constraint: bool = False, + extras: Collection[str] = (), + user_supplied: bool = False, + permit_editable_wheels: bool = False, + ) -> None: assert req is None or isinstance(req, Requirement), req self.req = req self.comes_from = comes_from self.constraint = constraint self.editable = editable + self.permit_editable_wheels = permit_editable_wheels + self.legacy_install_reason: Optional[int] = None # source_dir is the local directory where the linked requirement is # located, or unpacked. In case unpacking is needed, creating and # populating source_dir is done by the RequirementPreparer. Note this # is not necessarily the directory where pyproject.toml or setup.py is # located - that one is obtained via unpacked_source_directory. - self.source_dir = None # type: Optional[str] + self.source_dir: Optional[str] = None if self.editable: assert link if link.is_file: - self.source_dir = os.path.normpath( - os.path.abspath(link.file_path) - ) + self.source_dir = os.path.normpath(os.path.abspath(link.file_path)) if link is None and req and req.url: # PEP 508 URL requirement @@ -139,16 +138,14 @@ class InstallRequirement(object): self.original_link_is_in_wheel_cache = False # Path to any downloaded or already-existing package. - self.local_file_path = None # type: Optional[str] + self.local_file_path: Optional[str] = None if self.link and self.link.is_file: self.local_file_path = self.link.file_path if extras: self.extras = extras elif req: - self.extras = { - pkg_resources.safe_extra(extra) for extra in req.extras - } + self.extras = {pkg_resources.safe_extra(extra) for extra in req.extras} else: self.extras = set() if markers is None and req: @@ -157,48 +154,42 @@ class InstallRequirement(object): # This holds the pkg_resources.Distribution object if this requirement # is already available: - self.satisfied_by = None # type: Optional[Distribution] + self.satisfied_by: Optional[Distribution] = None # Whether the installation process should try to uninstall an existing # distribution before installing this requirement. self.should_reinstall = False # Temporary build location - self._temp_build_dir = None # type: Optional[TempDirectory] + self._temp_build_dir: Optional[TempDirectory] = None # Set to True after successful installation - self.install_succeeded = None # type: Optional[bool] + self.install_succeeded: Optional[bool] = None # Supplied options self.install_options = install_options if install_options else [] self.global_options = global_options if global_options else [] self.hash_options = hash_options if hash_options else {} # Set to True after successful preparation of this requirement self.prepared = False - self.is_direct = False - - # Set by the legacy resolver when the requirement has been downloaded - # TODO: This introduces a strong coupling between the resolver and the - # requirement (the coupling was previously between the resolver - # and the requirement set). This should be refactored to allow - # the requirement to decide for itself when it has been - # successfully downloaded - but that is more tricky to get right, - # se we are making the change in stages. - self.successfully_downloaded = False + # User supplied requirement are explicitly requested for installation + # by the user via CLI arguments or requirements files, as opposed to, + # e.g. dependencies, extras or constraints. + self.user_supplied = user_supplied self.isolated = isolated - self.build_env = NoOpBuildEnvironment() # type: BuildEnvironment + self.build_env: BuildEnvironment = NoOpBuildEnvironment() # For PEP 517, the directory where we request the project metadata # gets stored. We need this to pass to build_wheel, so the backend # can ensure that the wheel matches the metadata (see the PEP for # details). - self.metadata_directory = None # type: Optional[str] + self.metadata_directory: Optional[str] = None # The static build requirements (from pyproject.toml) - self.pyproject_requires = None # type: Optional[List[str]] + self.pyproject_requires: Optional[List[str]] = None # Build requirements that we will check are available - self.requirements_to_check = [] # type: List[str] + self.requirements_to_check: List[str] = [] # The PEP 517 backend we should use to build the project - self.pep517_backend = None # type: Optional[Pep517HookCaller] + self.pep517_backend: Optional[Pep517HookCaller] = None # Are we using PEP 517 for this requirement? # After pyproject.toml has been loaded, the only valid values are True @@ -207,92 +198,91 @@ class InstallRequirement(object): # but after loading this flag should be treated as read only. self.use_pep517 = use_pep517 - def __str__(self): - # type: () -> str + # This requirement needs more preparation before it can be built + self.needs_more_preparation = False + + def __str__(self) -> str: if self.req: s = str(self.req) if self.link: - s += ' from {}'.format(redact_auth_from_url(self.link.url)) + s += " from {}".format(redact_auth_from_url(self.link.url)) elif self.link: s = redact_auth_from_url(self.link.url) else: - s = '' + s = "" if self.satisfied_by is not None: - s += ' in {}'.format(display_path(self.satisfied_by.location)) + s += " in {}".format(display_path(self.satisfied_by.location)) if self.comes_from: - if isinstance(self.comes_from, six.string_types): - comes_from = self.comes_from # type: Optional[str] + if isinstance(self.comes_from, str): + comes_from: Optional[str] = self.comes_from else: comes_from = self.comes_from.from_path() if comes_from: - s += ' (from {})'.format(comes_from) + s += f" (from {comes_from})" return s - def __repr__(self): - # type: () -> str - return '<{} object: {} editable={!r}>'.format( - self.__class__.__name__, str(self), self.editable) + def __repr__(self) -> str: + return "<{} object: {} editable={!r}>".format( + self.__class__.__name__, str(self), self.editable + ) - def format_debug(self): - # type: () -> str - """An un-tested helper for getting state, for debugging. - """ + def format_debug(self) -> str: + """An un-tested helper for getting state, for debugging.""" attributes = vars(self) names = sorted(attributes) - state = ( - "{}={!r}".format(attr, attributes[attr]) for attr in sorted(names) - ) - return '<{name} object: {{{state}}}>'.format( + state = ("{}={!r}".format(attr, attributes[attr]) for attr in sorted(names)) + return "<{name} object: {{{state}}}>".format( name=self.__class__.__name__, state=", ".join(state), ) # Things that are valid for all kinds of requirements? @property - def name(self): - # type: () -> Optional[str] + def name(self) -> Optional[str]: if self.req is None: return None - return six.ensure_str(pkg_resources.safe_name(self.req.name)) + return pkg_resources.safe_name(self.req.name) + + @functools.lru_cache() # use cached_property in python 3.8+ + def supports_pyproject_editable(self) -> bool: + if not self.use_pep517: + return False + assert self.pep517_backend + with self.build_env: + runner = runner_with_spinner_message( + "Checking if build backend supports build_editable" + ) + with self.pep517_backend.subprocess_runner(runner): + return "build_editable" in self.pep517_backend._supported_features() @property - def specifier(self): - # type: () -> SpecifierSet + def specifier(self) -> SpecifierSet: return self.req.specifier @property - def is_pinned(self): - # type: () -> bool + def is_pinned(self) -> bool: """Return whether I am pinned to an exact version. For example, some-package==1.2 is pinned; some-package>1.2 is not. """ specifiers = self.specifier - return (len(specifiers) == 1 and - next(iter(specifiers)).operator in {'==', '==='}) + return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="} - @property - def installed_version(self): - # type: () -> Optional[str] - return get_installed_version(self.name) - - def match_markers(self, extras_requested=None): - # type: (Optional[Iterable[str]]) -> bool + def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool: if not extras_requested: # Provide an extra to safely evaluate the markers # without matching any extra - extras_requested = ('',) + extras_requested = ("",) if self.markers is not None: return any( - self.markers.evaluate({'extra': extra}) - for extra in extras_requested) + self.markers.evaluate({"extra": extra}) for extra in extras_requested + ) else: return True @property - def has_hash_options(self): - # type: () -> bool + def has_hash_options(self) -> bool: """Return whether any known-good hashes are specified as options. These activate --require-hashes mode; hashes specified as part of a @@ -301,8 +291,7 @@ class InstallRequirement(object): """ return bool(self.hash_options) - def hashes(self, trust_internet=True): - # type: (bool) -> Hashes + def hashes(self, trust_internet: bool = True) -> Hashes: """Return a hash-comparer that considers my option- and URL-based hashes to be known-good. @@ -323,24 +312,23 @@ class InstallRequirement(object): good_hashes.setdefault(link.hash_name, []).append(link.hash) return Hashes(good_hashes) - def from_path(self): - # type: () -> Optional[str] - """Format a nice indicator to show where this "comes from" - """ + def from_path(self) -> Optional[str]: + """Format a nice indicator to show where this "comes from" """ if self.req is None: return None s = str(self.req) if self.comes_from: - if isinstance(self.comes_from, six.string_types): + if isinstance(self.comes_from, str): comes_from = self.comes_from else: comes_from = self.comes_from.from_path() if comes_from: - s += '->' + comes_from + s += "->" + comes_from return s - def ensure_build_location(self, build_dir, autodelete): - # type: (str, bool) -> str + def ensure_build_location( + self, build_dir: str, autodelete: bool, parallel_builds: bool + ) -> str: assert build_dir is not None if self._temp_build_dir is not None: assert self._temp_build_dir.path @@ -354,16 +342,23 @@ class InstallRequirement(object): ) return self._temp_build_dir.path - if self.editable: - name = self.name.lower() - else: - name = self.name + + # This is the only remaining place where we manually determine the path + # for the temporary directory. It is only needed for editables where + # it is the value of the --src option. + + # When parallel builds are enabled, add a UUID to the build directory + # name so multiple builds do not interfere with each other. + dir_name: str = canonicalize_name(self.name) + if parallel_builds: + dir_name = f"{dir_name}_{uuid.uuid4().hex}" + # FIXME: Is there a better place to create the build_dir? (hg and bzr # need this) if not os.path.exists(build_dir): - logger.debug('Creating directory %s', build_dir) + logger.debug("Creating directory %s", build_dir) os.makedirs(build_dir) - actual_build_dir = os.path.join(build_dir, name) + actual_build_dir = os.path.join(build_dir, dir_name) # `None` indicates that we respect the globally-configured deletion # settings, which is what we actually want when auto-deleting. delete_arg = None if autodelete else False @@ -374,10 +369,8 @@ class InstallRequirement(object): globally_managed=True, ).path - def _set_requirement(self): - # type: () -> None - """Set requirement after generating metadata. - """ + def _set_requirement(self) -> None: + """Set requirement after generating metadata.""" assert self.req is None assert self.metadata is not None assert self.source_dir is not None @@ -389,15 +382,16 @@ class InstallRequirement(object): op = "===" self.req = Requirement( - "".join([ - self.metadata["Name"], - op, - self.metadata["Version"], - ]) + "".join( + [ + self.metadata["Name"], + op, + self.metadata["Version"], + ] + ) ) - def warn_on_mismatching_name(self): - # type: () -> None + def warn_on_mismatching_name(self) -> None: metadata_name = canonicalize_name(self.metadata["Name"]) if canonicalize_name(self.req.name) == metadata_name: # Everything is fine. @@ -405,90 +399,94 @@ class InstallRequirement(object): # If we're here, there's a mismatch. Log a warning about it. logger.warning( - 'Generating metadata for package %s ' - 'produced metadata for project name %s. Fix your ' - '#egg=%s fragments.', - self.name, metadata_name, self.name + "Generating metadata for package %s " + "produced metadata for project name %s. Fix your " + "#egg=%s fragments.", + self.name, + metadata_name, + self.name, ) self.req = Requirement(metadata_name) - def check_if_exists(self, use_user_site): - # type: (bool) -> None + def check_if_exists(self, use_user_site: bool) -> None: """Find an installed distribution that satisfies or conflicts with this requirement, and set self.satisfied_by or self.should_reinstall appropriately. """ if self.req is None: return - # get_distribution() will resolve the entire list of requirements - # anyway, and we've already determined that we need the requirement - # in question, so strip the marker so that we don't try to - # evaluate it. - no_marker = Requirement(str(self.req)) - no_marker.marker = None - try: - self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) - except pkg_resources.DistributionNotFound: + existing_dist = get_distribution(self.req.name) + if not existing_dist: return - except pkg_resources.VersionConflict: - existing_dist = pkg_resources.get_distribution( - self.req.name - ) + + # pkg_resouces may contain a different copy of packaging.version from + # pip in if the downstream distributor does a poor job debundling pip. + # We avoid existing_dist.parsed_version and let SpecifierSet.contains + # parses the version instead. + existing_version = existing_dist.version + version_compatible = ( + existing_version is not None + and self.req.specifier.contains(existing_version, prereleases=True) + ) + if not version_compatible: + self.satisfied_by = None if use_user_site: if dist_in_usersite(existing_dist): self.should_reinstall = True - elif (running_under_virtualenv() and - dist_in_site_packages(existing_dist)): + elif running_under_virtualenv() and dist_in_site_packages( + existing_dist + ): raise InstallationError( "Will not install to the user site because it will " "lack sys.path precedence to {} in {}".format( - existing_dist.project_name, existing_dist.location) + existing_dist.project_name, existing_dist.location + ) ) else: self.should_reinstall = True else: - if self.editable and self.satisfied_by: + if self.editable: self.should_reinstall = True # when installing editables, nothing pre-existing should ever # satisfy self.satisfied_by = None + else: + self.satisfied_by = existing_dist # Things valid for wheels @property - def is_wheel(self): - # type: () -> bool + def is_wheel(self) -> bool: if not self.link: return False return self.link.is_wheel # Things valid for sdists @property - def unpacked_source_directory(self): - # type: () -> str + def unpacked_source_directory(self) -> str: return os.path.join( - self.source_dir, - self.link and self.link.subdirectory_fragment or '') + self.source_dir, self.link and self.link.subdirectory_fragment or "" + ) @property - def setup_py_path(self): - # type: () -> str - assert self.source_dir, "No source dir for {}".format(self) - setup_py = os.path.join(self.unpacked_source_directory, 'setup.py') - - # Python2 __file__ should not be unicode - if six.PY2 and isinstance(setup_py, six.text_type): - setup_py = setup_py.encode(sys.getfilesystemencoding()) + def setup_py_path(self) -> str: + assert self.source_dir, f"No source dir for {self}" + setup_py = os.path.join(self.unpacked_source_directory, "setup.py") return setup_py @property - def pyproject_toml_path(self): - # type: () -> str - assert self.source_dir, "No source dir for {}".format(self) + def setup_cfg_path(self) -> str: + assert self.source_dir, f"No source dir for {self}" + setup_cfg = os.path.join(self.unpacked_source_directory, "setup.cfg") + + return setup_cfg + + @property + def pyproject_toml_path(self) -> str: + assert self.source_dir, f"No source dir for {self}" return make_pyproject_path(self.unpacked_source_directory) - def load_pyproject_toml(self): - # type: () -> None + def load_pyproject_toml(self) -> None: """Load the pyproject.toml file. After calling this routine, all of the attributes related to PEP 517 @@ -497,10 +495,7 @@ class InstallRequirement(object): follow the PEP 517 or legacy (setup.py) code path. """ pyproject_toml_data = load_pyproject_toml( - self.use_pep517, - self.pyproject_toml_path, - self.setup_py_path, - str(self) + self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self) ) if pyproject_toml_data is None: @@ -512,42 +507,64 @@ class InstallRequirement(object): self.requirements_to_check = check self.pyproject_requires = requires self.pep517_backend = Pep517HookCaller( - self.unpacked_source_directory, backend, backend_path=backend_path, + self.unpacked_source_directory, + backend, + backend_path=backend_path, ) - def _generate_metadata(self): - # type: () -> str - """Invokes metadata generator functions, with the required arguments. - """ - if not self.use_pep517: - assert self.unpacked_source_directory + def isolated_editable_sanity_check(self) -> None: + """Check that an editable requirement if valid for use with PEP 517/518. - return generate_metadata_legacy( - build_env=self.build_env, - setup_py_path=self.setup_py_path, - source_dir=self.unpacked_source_directory, - isolated=self.isolated, - details=self.name or "from {}".format(self.link) + This verifies that an editable that has a pyproject.toml either supports PEP 660 + or as a setup.py or a setup.cfg + """ + if ( + self.editable + and self.use_pep517 + and not self.supports_pyproject_editable() + and not os.path.isfile(self.setup_py_path) + and not os.path.isfile(self.setup_cfg_path) + ): + raise InstallationError( + f"Project {self} has a 'pyproject.toml' and its build " + f"backend is missing the 'build_editable' hook. Since it does not " + f"have a 'setup.py' nor a 'setup.cfg', " + f"it cannot be installed in editable mode. " + f"Consider using a build backend that supports PEP 660." ) - assert self.pep517_backend is not None - - return generate_metadata( - build_env=self.build_env, - backend=self.pep517_backend, - ) - - def prepare_metadata(self): - # type: () -> None + def prepare_metadata(self) -> None: """Ensure that project metadata is available. - Under PEP 517, call the backend hook to prepare the metadata. + Under PEP 517 and PEP 660, call the backend hook to prepare the metadata. Under legacy processing, call setup.py egg-info. """ assert self.source_dir - with indent_log(): - self.metadata_directory = self._generate_metadata() + if self.use_pep517: + assert self.pep517_backend is not None + if ( + self.editable + and self.permit_editable_wheels + and self.supports_pyproject_editable() + ): + self.metadata_directory = generate_editable_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + ) + else: + self.metadata_directory = generate_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + ) + else: + self.metadata_directory = generate_metadata_legacy( + build_env=self.build_env, + setup_py_path=self.setup_py_path, + source_dir=self.unpacked_source_directory, + isolated=self.isolated, + details=self.name or f"from {self.link}", + ) # Act on the newly generated metadata, based on the name and version. if not self.name: @@ -558,38 +575,39 @@ class InstallRequirement(object): self.assert_source_matches_version() @property - def metadata(self): - # type: () -> Any - if not hasattr(self, '_metadata'): + def metadata(self) -> Any: + if not hasattr(self, "_metadata"): self._metadata = get_metadata(self.get_dist()) return self._metadata - def get_dist(self): - # type: () -> Distribution + def get_dist(self) -> Distribution: return _get_dist(self.metadata_directory) - def assert_source_matches_version(self): - # type: () -> None + def assert_source_matches_version(self) -> None: assert self.source_dir - version = self.metadata['version'] + version = self.metadata["version"] if self.req.specifier and version not in self.req.specifier: logger.warning( - 'Requested %s, but installing version %s', + "Requested %s, but installing version %s", self, version, ) else: logger.debug( - 'Source in %s has version %s, which satisfies requirement %s', + "Source in %s has version %s, which satisfies requirement %s", display_path(self.source_dir), version, self, ) # For both source distributions and editables - def ensure_has_source_dir(self, parent_dir, autodelete=False): - # type: (str, bool) -> None + def ensure_has_source_dir( + self, + parent_dir: str, + autodelete: bool = False, + parallel_builds: bool = False, + ) -> None: """Ensure that a source_dir is set. This will create a temporary build dir if the name of the requirement @@ -601,56 +619,35 @@ class InstallRequirement(object): """ if self.source_dir is None: self.source_dir = self.ensure_build_location( - parent_dir, autodelete + parent_dir, + autodelete=autodelete, + parallel_builds=parallel_builds, ) # For editable installations - def update_editable(self, obtain=True): - # type: (bool) -> None + def update_editable(self) -> None: if not self.link: logger.debug( - "Cannot update repository at %s; repository location is " - "unknown", + "Cannot update repository at %s; repository location is unknown", self.source_dir, ) return assert self.editable assert self.source_dir - if self.link.scheme == 'file': + if self.link.scheme == "file": # Static paths don't get updated return - assert '+' in self.link.url, \ - "bad url: {self.link.url!r}".format(**locals()) - vc_type, url = self.link.url.split('+', 1) - vcs_backend = vcs.get_backend(vc_type) - if vcs_backend: - if not self.link.is_vcs: - reason = ( - "This form of VCS requirement is being deprecated: {}." - ).format( - self.link.url - ) - replacement = None - if self.link.url.startswith("git+git@"): - replacement = ( - "git+https://git@example.com/..., " - "git+ssh://git@example.com/..., " - "or the insecure git+git://git@example.com/..." - ) - deprecated(reason, replacement, gone_in="21.0", issue=7554) - hidden_url = hide_url(self.link.url) - if obtain: - vcs_backend.obtain(self.source_dir, url=hidden_url) - else: - vcs_backend.export(self.source_dir, url=hidden_url) - else: - assert 0, ( - 'Unexpected version control type (in {}): {}'.format( - self.link, vc_type)) + vcs_backend = vcs.get_backend_for_scheme(self.link.scheme) + # Editable requirements are validated in Requirement constructors. + # So here, if it's neither a path nor a valid VCS URL, it's a bug. + assert vcs_backend, f"Unsupported VCS URL {self.link.url}" + hidden_url = hide_url(self.link.url) + vcs_backend.obtain(self.source_dir, url=hidden_url) # Top-level Actions - def uninstall(self, auto_confirm=False, verbose=False): - # type: (bool, bool) -> Optional[UninstallPathSet] + def uninstall( + self, auto_confirm: bool = False, verbose: bool = False + ) -> Optional[UninstallPathSet]: """ Uninstall the distribution currently satisfying this requirement. @@ -664,108 +661,107 @@ class InstallRequirement(object): """ assert self.req - try: - dist = pkg_resources.get_distribution(self.req.name) - except pkg_resources.DistributionNotFound: + dist = get_distribution(self.req.name) + if not dist: logger.warning("Skipping %s as it is not installed.", self.name) return None - else: - logger.info('Found existing installation: %s', dist) + logger.info("Found existing installation: %s", dist) uninstalled_pathset = UninstallPathSet.from_dist(dist) uninstalled_pathset.remove(auto_confirm, verbose) return uninstalled_pathset - def _get_archive_name(self, path, parentdir, rootdir): - # type: (str, str, str) -> str - - def _clean_zip_name(name, prefix): - # type: (str, str) -> str - assert name.startswith(prefix + os.path.sep), ( - "name {name!r} doesn't start with prefix {prefix!r}" - .format(**locals()) - ) - name = name[len(prefix) + 1:] - name = name.replace(os.path.sep, '/') + def _get_archive_name(self, path: str, parentdir: str, rootdir: str) -> str: + def _clean_zip_name(name: str, prefix: str) -> str: + assert name.startswith( + prefix + os.path.sep + ), f"name {name!r} doesn't start with prefix {prefix!r}" + name = name[len(prefix) + 1 :] + name = name.replace(os.path.sep, "/") return name path = os.path.join(parentdir, path) name = _clean_zip_name(path, rootdir) - return self.name + '/' + name + return self.name + "/" + name - def archive(self, build_dir): - # type: (str) -> None + def archive(self, build_dir: Optional[str]) -> None: """Saves archive to provided build_dir. Used for saving downloaded VCS requirements as part of `pip download`. """ assert self.source_dir + if build_dir is None: + return create_archive = True - archive_name = '{}-{}.zip'.format(self.name, self.metadata["version"]) + archive_name = "{}-{}.zip".format(self.name, self.metadata["version"]) archive_path = os.path.join(build_dir, archive_name) if os.path.exists(archive_path): response = ask_path_exists( - 'The file {} exists. (i)gnore, (w)ipe, ' - '(b)ackup, (a)bort '.format( - display_path(archive_path)), - ('i', 'w', 'b', 'a')) - if response == 'i': + "The file {} exists. (i)gnore, (w)ipe, " + "(b)ackup, (a)bort ".format(display_path(archive_path)), + ("i", "w", "b", "a"), + ) + if response == "i": create_archive = False - elif response == 'w': - logger.warning('Deleting %s', display_path(archive_path)) + elif response == "w": + logger.warning("Deleting %s", display_path(archive_path)) os.remove(archive_path) - elif response == 'b': + elif response == "b": dest_file = backup_dir(archive_path) logger.warning( - 'Backing up %s to %s', + "Backing up %s to %s", display_path(archive_path), display_path(dest_file), ) shutil.move(archive_path, dest_file) - elif response == 'a': + elif response == "a": sys.exit(-1) if not create_archive: return zip_output = zipfile.ZipFile( - archive_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True, + archive_path, + "w", + zipfile.ZIP_DEFLATED, + allowZip64=True, ) with zip_output: - dir = os.path.normcase( - os.path.abspath(self.unpacked_source_directory) - ) + dir = os.path.normcase(os.path.abspath(self.unpacked_source_directory)) for dirpath, dirnames, filenames in os.walk(dir): for dirname in dirnames: dir_arcname = self._get_archive_name( - dirname, parentdir=dirpath, rootdir=dir, + dirname, + parentdir=dirpath, + rootdir=dir, ) - zipdir = zipfile.ZipInfo(dir_arcname + '/') + zipdir = zipfile.ZipInfo(dir_arcname + "/") zipdir.external_attr = 0x1ED << 16 # 0o755 - zip_output.writestr(zipdir, '') + zip_output.writestr(zipdir, "") for filename in filenames: file_arcname = self._get_archive_name( - filename, parentdir=dirpath, rootdir=dir, + filename, + parentdir=dirpath, + rootdir=dir, ) filename = os.path.join(dirpath, filename) zip_output.write(filename, file_arcname) - logger.info('Saved %s', display_path(archive_path)) + logger.info("Saved %s", display_path(archive_path)) def install( self, - install_options, # type: List[str] - global_options=None, # type: Optional[Sequence[str]] - root=None, # type: Optional[str] - home=None, # type: Optional[str] - prefix=None, # type: Optional[str] - warn_script_location=True, # type: bool - use_user_site=False, # type: bool - pycompile=True # type: bool - ): - # type: (...) -> None + install_options: List[str], + global_options: Optional[Sequence[str]] = None, + root: Optional[str] = None, + home: Optional[str] = None, + prefix: Optional[str] = None, + warn_script_location: bool = True, + use_user_site: bool = False, + pycompile: bool = True, + ) -> None: scheme = get_scheme( self.name, user=use_user_site, @@ -776,7 +772,7 @@ class InstallRequirement(object): ) global_options = global_options if global_options is not None else [] - if self.editable: + if self.editable and not self.is_wheel: install_editable_legacy( install_options, global_options, @@ -795,7 +791,9 @@ class InstallRequirement(object): if self.is_wheel: assert self.local_file_path direct_url = None - if self.original_link: + if self.editable: + direct_url = direct_url_for_editable(self.unpacked_source_directory) + elif self.original_link: direct_url = direct_url_from_link( self.original_link, self.source_dir, @@ -809,6 +807,7 @@ class InstallRequirement(object): pycompile=pycompile, warn_script_location=warn_script_location, direct_url=direct_url, + requested=self.user_supplied, ) self.install_succeeded = True return @@ -842,9 +841,51 @@ class InstallRequirement(object): ) except LegacyInstallFailure as exc: self.install_succeeded = False - six.reraise(*exc.parent) + raise exc.__cause__ except Exception: self.install_succeeded = True raise self.install_succeeded = success + + if success and self.legacy_install_reason == 8368: + deprecated( + reason=( + "{} was installed using the legacy 'setup.py install' " + "method, because a wheel could not be built for it.".format( + self.name + ) + ), + replacement="to fix the wheel build issue reported above", + gone_in=None, + issue=8368, + ) + + +def check_invalid_constraint_type(req: InstallRequirement) -> str: + + # Check for unsupported forms + problem = "" + if not req.name: + problem = "Unnamed requirements are not allowed as constraints" + elif req.editable: + problem = "Editable requirements are not allowed as constraints" + elif req.extras: + problem = "Constraints cannot have extras" + + if problem: + deprecated( + reason=( + "Constraints are only allowed to take the form of a package " + "name and a version specifier. Other forms were originally " + "permitted as an accident of the implementation, but were " + "undocumented. The new implementation of the resolver no " + "longer supports these forms." + ), + replacement="replacing the constraint with a requirement", + # No plan yet for when the new resolver becomes default + gone_in=None, + issue=8210, + ) + + return problem diff --git a/venv/Lib/site-packages/pip/_internal/req/req_set.py b/venv/Lib/site-packages/pip/_internal/req/req_set.py index f168ce17abd53e0de3d4a3714f9a0cc25d33b8fd..6626c37e2e1f59f2fd432a60556fc2688cb3d138 100644 --- a/venv/Lib/site-packages/pip/_internal/req/req_set.py +++ b/venv/Lib/site-packages/pip/_internal/req/req_set.py @@ -1,67 +1,51 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - import logging from collections import OrderedDict +from typing import Dict, Iterable, List, Optional, Tuple from pip._vendor.packaging.utils import canonicalize_name from pip._internal.exceptions import InstallationError from pip._internal.models.wheel import Wheel +from pip._internal.req.req_install import InstallRequirement from pip._internal.utils import compatibility_tags -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Dict, Iterable, List, Optional, Tuple - from pip._internal.req.req_install import InstallRequirement - logger = logging.getLogger(__name__) -class RequirementSet(object): - - def __init__(self, check_supported_wheels=True): - # type: (bool) -> None - """Create a RequirementSet. - """ +class RequirementSet: + def __init__(self, check_supported_wheels: bool = True) -> None: + """Create a RequirementSet.""" - self.requirements = OrderedDict() # type: Dict[str, InstallRequirement] # noqa: E501 + self.requirements: Dict[str, InstallRequirement] = OrderedDict() self.check_supported_wheels = check_supported_wheels - self.unnamed_requirements = [] # type: List[InstallRequirement] + self.unnamed_requirements: List[InstallRequirement] = [] - def __str__(self): - # type: () -> str + def __str__(self) -> str: requirements = sorted( (req for req in self.requirements.values() if not req.comes_from), - key=lambda req: canonicalize_name(req.name), + key=lambda req: canonicalize_name(req.name or ""), ) - return ' '.join(str(req.req) for req in requirements) + return " ".join(str(req.req) for req in requirements) - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: requirements = sorted( self.requirements.values(), - key=lambda req: canonicalize_name(req.name), + key=lambda req: canonicalize_name(req.name or ""), ) - format_string = '<{classname} object; {count} requirement(s): {reqs}>' + format_string = "<{classname} object; {count} requirement(s): {reqs}>" return format_string.format( classname=self.__class__.__name__, count=len(requirements), - reqs=', '.join(str(req.req) for req in requirements), + reqs=", ".join(str(req.req) for req in requirements), ) - def add_unnamed_requirement(self, install_req): - # type: (InstallRequirement) -> None + def add_unnamed_requirement(self, install_req: InstallRequirement) -> None: assert not install_req.name self.unnamed_requirements.append(install_req) - def add_named_requirement(self, install_req): - # type: (InstallRequirement) -> None + def add_named_requirement(self, install_req: InstallRequirement) -> None: assert install_req.name project_name = canonicalize_name(install_req.name) @@ -69,11 +53,10 @@ class RequirementSet(object): def add_requirement( self, - install_req, # type: InstallRequirement - parent_req_name=None, # type: Optional[str] - extras_requested=None # type: Optional[Iterable[str]] - ): - # type: (...) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]] # noqa: E501 + install_req: InstallRequirement, + parent_req_name: Optional[str] = None, + extras_requested: Optional[Iterable[str]] = None, + ) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]]: """Add install_req as a requirement to install. :param parent_req_name: The name of the requirement that needed this @@ -92,7 +75,8 @@ class RequirementSet(object): if not install_req.match_markers(extras_requested): logger.info( "Ignoring %s: markers '%s' don't match your environment", - install_req.name, install_req.markers, + install_req.name, + install_req.markers, ) return [], None @@ -103,17 +87,17 @@ class RequirementSet(object): if install_req.link and install_req.link.is_wheel: wheel = Wheel(install_req.link.filename) tags = compatibility_tags.get_supported() - if (self.check_supported_wheels and not wheel.supported(tags)): + if self.check_supported_wheels and not wheel.supported(tags): raise InstallationError( "{} is not a supported wheel on this platform.".format( - wheel.filename) + wheel.filename + ) ) # This next bit is really a sanity check. - assert install_req.is_direct == (parent_req_name is None), ( - "a direct req shouldn't have a parent and also, " - "a non direct req should have a parent" - ) + assert ( + not install_req.user_supplied or parent_req_name is None + ), "a user supplied req shouldn't have a parent" # Unnamed requirements are scanned again and the requirement won't be # added as a dependency until after scanning. @@ -122,21 +106,26 @@ class RequirementSet(object): return [install_req], None try: - existing_req = self.get_requirement(install_req.name) + existing_req: Optional[InstallRequirement] = self.get_requirement( + install_req.name + ) except KeyError: existing_req = None has_conflicting_requirement = ( - parent_req_name is None and - existing_req and - not existing_req.constraint and - existing_req.extras == install_req.extras and - existing_req.req.specifier != install_req.req.specifier + parent_req_name is None + and existing_req + and not existing_req.constraint + and existing_req.extras == install_req.extras + and existing_req.req + and install_req.req + and existing_req.req.specifier != install_req.req.specifier ) if has_conflicting_requirement: raise InstallationError( - "Double requirement given: {} (already in {}, name={!r})" - .format(install_req, existing_req, install_req.name) + "Double requirement given: {} (already in {}, name={!r})".format( + install_req, existing_req, install_req.name + ) ) # When no existing requirement exists, add the requirement as a @@ -151,12 +140,8 @@ class RequirementSet(object): if install_req.constraint or not existing_req.constraint: return [], existing_req - does_not_satisfy_constraint = ( - install_req.link and - not ( - existing_req.link and - install_req.link.path == existing_req.link.path - ) + does_not_satisfy_constraint = install_req.link and not ( + existing_req.link and install_req.link.path == existing_req.link.path ) if does_not_satisfy_constraint: raise InstallationError( @@ -167,36 +152,38 @@ class RequirementSet(object): # If we're now installing a constraint, mark the existing # object for real installation. existing_req.constraint = False - existing_req.extras = tuple(sorted( - set(existing_req.extras) | set(install_req.extras) - )) + # If we're now installing a user supplied requirement, + # mark the existing object as such. + if install_req.user_supplied: + existing_req.user_supplied = True + existing_req.extras = tuple( + sorted(set(existing_req.extras) | set(install_req.extras)) + ) logger.debug( "Setting %s extras to: %s", - existing_req, existing_req.extras, + existing_req, + existing_req.extras, ) # Return the existing requirement for addition to the parent and # scanning again. return [existing_req], existing_req - def has_requirement(self, name): - # type: (str) -> bool + def has_requirement(self, name: str) -> bool: project_name = canonicalize_name(name) return ( - project_name in self.requirements and - not self.requirements[project_name].constraint + project_name in self.requirements + and not self.requirements[project_name].constraint ) - def get_requirement(self, name): - # type: (str) -> InstallRequirement + def get_requirement(self, name: str) -> InstallRequirement: project_name = canonicalize_name(name) if project_name in self.requirements: return self.requirements[project_name] - raise KeyError("No project with the name {name!r}".format(**locals())) + raise KeyError(f"No project with the name {name!r}") @property - def all_requirements(self): - # type: () -> List[InstallRequirement] + def all_requirements(self) -> List[InstallRequirement]: return self.unnamed_requirements + list(self.requirements.values()) diff --git a/venv/Lib/site-packages/pip/_internal/req/req_tracker.py b/venv/Lib/site-packages/pip/_internal/req/req_tracker.py index 14adeab29b5b78706500639d2000d9703dffc7e6..24d3c530335d0bc108055ca68744893fe5108b53 100644 --- a/venv/Lib/site-packages/pip/_internal/req/req_tracker.py +++ b/venv/Lib/site-packages/pip/_internal/req/req_tracker.py @@ -1,36 +1,24 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - import contextlib -import errno import hashlib import logging import os +from types import TracebackType +from typing import Dict, Iterator, Optional, Set, Type, Union -from pip._vendor import contextlib2 - +from pip._internal.models.link import Link +from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from types import TracebackType - from typing import Dict, Iterator, Optional, Set, Type, Union - from pip._internal.req.req_install import InstallRequirement - from pip._internal.models.link import Link logger = logging.getLogger(__name__) @contextlib.contextmanager -def update_env_context_manager(**changes): - # type: (str) -> Iterator[None] +def update_env_context_manager(**changes: str) -> Iterator[None]: target = os.environ # Save values from the target and change them. non_existent_marker = object() - saved_values = {} # type: Dict[str, Union[object, str]] + saved_values: Dict[str, Union[object, str]] = {} for name, new_value in changes.items(): try: saved_values[name] = target[name] @@ -51,14 +39,11 @@ def update_env_context_manager(**changes): @contextlib.contextmanager -def get_requirement_tracker(): - # type: () -> Iterator[RequirementTracker] - root = os.environ.get('PIP_REQ_TRACKER') - with contextlib2.ExitStack() as ctx: +def get_requirement_tracker() -> Iterator["RequirementTracker"]: + root = os.environ.get("PIP_REQ_TRACKER") + with contextlib.ExitStack() as ctx: if root is None: - root = ctx.enter_context( - TempDirectory(kind='req-tracker') - ).path + root = ctx.enter_context(TempDirectory(kind="req-tracker")).path ctx.enter_context(update_env_context_manager(PIP_REQ_TRACKER=root)) logger.debug("Initialized build tracking at %s", root) @@ -66,38 +51,32 @@ def get_requirement_tracker(): yield tracker -class RequirementTracker(object): - - def __init__(self, root): - # type: (str) -> None +class RequirementTracker: + def __init__(self, root: str) -> None: self._root = root - self._entries = set() # type: Set[InstallRequirement] + self._entries: Set[InstallRequirement] = set() logger.debug("Created build tracker: %s", self._root) - def __enter__(self): - # type: () -> RequirementTracker + def __enter__(self) -> "RequirementTracker": logger.debug("Entered build tracker: %s", self._root) return self def __exit__( self, - exc_type, # type: Optional[Type[BaseException]] - exc_val, # type: Optional[BaseException] - exc_tb # type: Optional[TracebackType] - ): - # type: (...) -> None + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self.cleanup() - def _entry_path(self, link): - # type: (Link) -> str + def _entry_path(self, link: Link) -> str: hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest() return os.path.join(self._root, hashed) - def add(self, req): - # type: (InstallRequirement) -> None - """Add an InstallRequirement to build tracking. - """ + def add(self, req: InstallRequirement) -> None: + """Add an InstallRequirement to build tracking.""" + assert req.link # Get the file to write information about this requirement. entry_path = self._entry_path(req.link) @@ -106,46 +85,40 @@ class RequirementTracker(object): try: with open(entry_path) as fp: contents = fp.read() - except IOError as e: - # if the error is anything other than "file does not exist", raise. - if e.errno != errno.ENOENT: - raise + except FileNotFoundError: + pass else: - message = '{} is already being built: {}'.format( - req.link, contents) + message = "{} is already being built: {}".format(req.link, contents) raise LookupError(message) # If we're here, req should really not be building already. assert req not in self._entries # Start tracking this requirement. - with open(entry_path, 'w') as fp: + with open(entry_path, "w", encoding="utf-8") as fp: fp.write(str(req)) self._entries.add(req) - logger.debug('Added %s to build tracker %r', req, self._root) + logger.debug("Added %s to build tracker %r", req, self._root) - def remove(self, req): - # type: (InstallRequirement) -> None - """Remove an InstallRequirement from build tracking. - """ + def remove(self, req: InstallRequirement) -> None: + """Remove an InstallRequirement from build tracking.""" + assert req.link # Delete the created file and the corresponding entries. os.unlink(self._entry_path(req.link)) self._entries.remove(req) - logger.debug('Removed %s from build tracker %r', req, self._root) + logger.debug("Removed %s from build tracker %r", req, self._root) - def cleanup(self): - # type: () -> None + def cleanup(self) -> None: for req in set(self._entries): self.remove(req) logger.debug("Removed build tracker: %r", self._root) @contextlib.contextmanager - def track(self, req): - # type: (InstallRequirement) -> Iterator[None] + def track(self, req: InstallRequirement) -> Iterator[None]: self.add(req) yield self.remove(req) diff --git a/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py b/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py index 559061a62965663184197c7519ccecaef99ee822..779e93b44aff84b83358350fd1464e5231d83a86 100644 --- a/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py +++ b/venv/Lib/site-packages/pip/_internal/req/req_uninstall.py @@ -1,79 +1,68 @@ -from __future__ import absolute_import - import csv import functools -import logging import os import sys import sysconfig +from importlib.util import cache_from_source +from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, Tuple from pip._vendor import pkg_resources +from pip._vendor.pkg_resources import Distribution from pip._internal.exceptions import UninstallationError -from pip._internal.locations import bin_py, bin_user -from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache -from pip._internal.utils.logging import indent_log +from pip._internal.locations import get_bin_prefix, get_bin_user +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.egg_link import egg_link_path_from_location +from pip._internal.utils.logging import getLogger, indent_log from pip._internal.utils.misc import ( - FakeFile, ask, dist_in_usersite, dist_is_local, - egg_link_path, is_local, normalize_path, renames, rmtree, ) from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, Tuple, - ) - from pip._vendor.pkg_resources import Distribution -logger = logging.getLogger(__name__) +logger = getLogger(__name__) -def _script_names(dist, script_name, is_gui): - # type: (Distribution, str, bool) -> List[str] +def _script_names(dist: Distribution, script_name: str, is_gui: bool) -> List[str]: """Create the fully qualified name of the files created by {console,gui}_scripts for the given ``dist``. Returns the list of file names """ if dist_in_usersite(dist): - bin_dir = bin_user + bin_dir = get_bin_user() else: - bin_dir = bin_py + bin_dir = get_bin_prefix() exe_name = os.path.join(bin_dir, script_name) paths_to_remove = [exe_name] if WINDOWS: - paths_to_remove.append(exe_name + '.exe') - paths_to_remove.append(exe_name + '.exe.manifest') + paths_to_remove.append(exe_name + ".exe") + paths_to_remove.append(exe_name + ".exe.manifest") if is_gui: - paths_to_remove.append(exe_name + '-script.pyw') + paths_to_remove.append(exe_name + "-script.pyw") else: - paths_to_remove.append(exe_name + '-script.py') + paths_to_remove.append(exe_name + "-script.py") return paths_to_remove -def _unique(fn): - # type: (Callable[..., Iterator[Any]]) -> Callable[..., Iterator[Any]] +def _unique(fn: Callable[..., Iterator[Any]]) -> Callable[..., Iterator[Any]]: @functools.wraps(fn) - def unique(*args, **kw): - # type: (Any, Any) -> Iterator[Any] - seen = set() # type: Set[Any] + def unique(*args: Any, **kw: Any) -> Iterator[Any]: + seen: Set[Any] = set() for item in fn(*args, **kw): if item not in seen: seen.add(item) yield item + return unique @_unique -def uninstallation_paths(dist): - # type: (Distribution) -> Iterator[str] +def uninstallation_paths(dist: Distribution) -> Iterator[str]: """ Yield all the uninstallation paths for dist based on RECORD-without-.py[co] @@ -81,33 +70,53 @@ def uninstallation_paths(dist): the .pyc and .pyo in the same directory. UninstallPathSet.add() takes care of the __pycache__ .py[co]. + + If RECORD is not found, raises UninstallationError, + with possible information from the INSTALLER file. + + https://packaging.python.org/specifications/recording-installed-packages/ """ - r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + try: + r = csv.reader(dist.get_metadata_lines("RECORD")) + except FileNotFoundError as missing_record_exception: + msg = "Cannot uninstall {dist}, RECORD file not found.".format(dist=dist) + try: + installer = next(dist.get_metadata_lines("INSTALLER")) + if not installer or installer == "pip": + raise ValueError() + except (OSError, StopIteration, ValueError): + dep = "{}=={}".format(dist.project_name, dist.version) + msg += ( + " You might be able to recover from this via: " + "'pip install --force-reinstall --no-deps {}'.".format(dep) + ) + else: + msg += " Hint: The package was installed by {}.".format(installer) + raise UninstallationError(msg) from missing_record_exception for row in r: path = os.path.join(dist.location, row[0]) yield path - if path.endswith('.py'): + if path.endswith(".py"): dn, fn = os.path.split(path) base = fn[:-3] - path = os.path.join(dn, base + '.pyc') + path = os.path.join(dn, base + ".pyc") yield path - path = os.path.join(dn, base + '.pyo') + path = os.path.join(dn, base + ".pyo") yield path -def compact(paths): - # type: (Iterable[str]) -> Set[str] +def compact(paths: Iterable[str]) -> Set[str]: """Compact a path set to contain the minimal number of paths necessary to contain all paths in the set. If /a/path/ and /a/path/to/a/file.txt are both in the set, leave only the shorter path.""" sep = os.path.sep - short_paths = set() # type: Set[str] + short_paths: Set[str] = set() for path in sorted(paths, key=len): should_skip = any( - path.startswith(shortpath.rstrip("*")) and - path[len(shortpath.rstrip("*").rstrip(sep))] == sep + path.startswith(shortpath.rstrip("*")) + and path[len(shortpath.rstrip("*").rstrip(sep))] == sep for shortpath in short_paths ) if not should_skip: @@ -115,36 +124,30 @@ def compact(paths): return short_paths -def compress_for_rename(paths): - # type: (Iterable[str]) -> Set[str] +def compress_for_rename(paths: Iterable[str]) -> Set[str]: """Returns a set containing the paths that need to be renamed. This set may include directories when the original sequence of paths included every file on disk. """ - case_map = dict((os.path.normcase(p), p) for p in paths) + case_map = {os.path.normcase(p): p for p in paths} remaining = set(case_map) - unchecked = sorted(set(os.path.split(p)[0] - for p in case_map.values()), key=len) - wildcards = set() # type: Set[str] + unchecked = sorted({os.path.split(p)[0] for p in case_map.values()}, key=len) + wildcards: Set[str] = set() - def norm_join(*a): - # type: (str) -> str + def norm_join(*a: str) -> str: return os.path.normcase(os.path.join(*a)) for root in unchecked: - if any(os.path.normcase(root).startswith(w) - for w in wildcards): + if any(os.path.normcase(root).startswith(w) for w in wildcards): # This directory has already been handled. continue - all_files = set() # type: Set[str] - all_subdirs = set() # type: Set[str] + all_files: Set[str] = set() + all_subdirs: Set[str] = set() for dirname, subdirs, files in os.walk(root): - all_subdirs.update(norm_join(root, dirname, d) - for d in subdirs) - all_files.update(norm_join(root, dirname, f) - for f in files) + all_subdirs.update(norm_join(root, dirname, d) for d in subdirs) + all_files.update(norm_join(root, dirname, f) for f in files) # If all the files we found are in our remaining set of files to # remove, then remove them from the latter set and add a wildcard # for the directory. @@ -155,8 +158,7 @@ def compress_for_rename(paths): return set(map(case_map.__getitem__, remaining)) | wildcards -def compress_for_output_listing(paths): - # type: (Iterable[str]) -> Tuple[Set[str], Set[str]] +def compress_for_output_listing(paths: Iterable[str]) -> Tuple[Set[str], Set[str]]: """Returns a tuple of 2 sets of which paths to display to user The first set contains paths that would be deleted. Files of a package @@ -194,47 +196,45 @@ def compress_for_output_listing(paths): continue file_ = os.path.join(dirpath, fname) - if (os.path.isfile(file_) and - os.path.normcase(file_) not in _normcased_files): + if ( + os.path.isfile(file_) + and os.path.normcase(file_) not in _normcased_files + ): # We are skipping this file. Add it to the set. will_skip.add(file_) - will_remove = files | { - os.path.join(folder, "*") for folder in folders - } + will_remove = files | {os.path.join(folder, "*") for folder in folders} return will_remove, will_skip -class StashedUninstallPathSet(object): +class StashedUninstallPathSet: """A set of file rename operations to stash files while tentatively uninstalling them.""" - def __init__(self): - # type: () -> None + + def __init__(self) -> None: # Mapping from source file root to [Adjacent]TempDirectory # for files under that directory. - self._save_dirs = {} # type: Dict[str, TempDirectory] + self._save_dirs: Dict[str, TempDirectory] = {} # (old path, new path) tuples for each move that may need # to be undone. - self._moves = [] # type: List[Tuple[str, str]] + self._moves: List[Tuple[str, str]] = [] - def _get_directory_stash(self, path): - # type: (str) -> str + def _get_directory_stash(self, path: str) -> str: """Stashes a directory. Directories are stashed adjacent to their original location if possible, or else moved/copied into the user's temp dir.""" try: - save_dir = AdjacentTempDirectory(path) # type: TempDirectory + save_dir: TempDirectory = AdjacentTempDirectory(path) except OSError: save_dir = TempDirectory(kind="uninstall") self._save_dirs[os.path.normcase(path)] = save_dir return save_dir.path - def _get_file_stash(self, path): - # type: (str) -> str + def _get_file_stash(self, path: str) -> str: """Stashes a file. If no root has been provided, one will be created for the directory @@ -253,7 +253,7 @@ class StashedUninstallPathSet(object): else: # Did not find any suitable root head = os.path.dirname(path) - save_dir = TempDirectory(kind='uninstall') + save_dir = TempDirectory(kind="uninstall") self._save_dirs[head] = save_dir relpath = os.path.relpath(path, head) @@ -261,8 +261,7 @@ class StashedUninstallPathSet(object): return os.path.join(save_dir.path, relpath) return save_dir.path - def stash(self, path): - # type: (str) -> str + def stash(self, path: str) -> str: """Stashes the directory or file and returns its new location. Handle symlinks as files to avoid modifying the symlink targets. """ @@ -273,7 +272,7 @@ class StashedUninstallPathSet(object): new_path = self._get_file_stash(path) self._moves.append((path, new_path)) - if (path_is_dir and os.path.isdir(new_path)): + if path_is_dir and os.path.isdir(new_path): # If we're moving a directory, we need to # remove the destination first or else it will be # moved to inside the existing directory. @@ -283,23 +282,21 @@ class StashedUninstallPathSet(object): renames(path, new_path) return new_path - def commit(self): - # type: () -> None + def commit(self) -> None: """Commits the uninstall by removing stashed files.""" for _, save_dir in self._save_dirs.items(): save_dir.cleanup() self._moves = [] self._save_dirs = {} - def rollback(self): - # type: () -> None + def rollback(self) -> None: """Undoes the uninstall by moving stashed files back.""" for p in self._moves: logger.info("Moving to %s\n from %s", *p) for new_path, path in self._moves: try: - logger.debug('Replacing %s from %s', new_path, path) + logger.debug("Replacing %s from %s", new_path, path) if os.path.isfile(new_path) or os.path.islink(new_path): os.unlink(new_path) elif os.path.isdir(new_path): @@ -312,24 +309,22 @@ class StashedUninstallPathSet(object): self.commit() @property - def can_rollback(self): - # type: () -> bool + def can_rollback(self) -> bool: return bool(self._moves) -class UninstallPathSet(object): +class UninstallPathSet: """A set of file paths to be removed in the uninstallation of a requirement.""" - def __init__(self, dist): - # type: (Distribution) -> None - self.paths = set() # type: Set[str] - self._refuse = set() # type: Set[str] - self.pth = {} # type: Dict[str, UninstallPthEntries] + + def __init__(self, dist: Distribution) -> None: + self.paths: Set[str] = set() + self._refuse: Set[str] = set() + self.pth: Dict[str, UninstallPthEntries] = {} self.dist = dist self._moved_paths = StashedUninstallPathSet() - def _permitted(self, path): - # type: (str) -> bool + def _permitted(self, path: str) -> bool: """ Return True if the given path is one we are permitted to remove/modify, False otherwise. @@ -337,8 +332,7 @@ class UninstallPathSet(object): """ return is_local(path) - def add(self, path): - # type: (str) -> None + def add(self, path: str) -> None: head, tail = os.path.split(path) # we normalize the head to resolve parent directory symlinks, but not @@ -354,11 +348,10 @@ class UninstallPathSet(object): # __pycache__ files can show up after 'installed-files.txt' is created, # due to imports - if os.path.splitext(path)[1] == '.py' and uses_pycache: + if os.path.splitext(path)[1] == ".py": self.add(cache_from_source(path)) - def add_pth(self, pth_file, entry): - # type: (str, str) -> None + def add_pth(self, pth_file: str, entry: str) -> None: pth_file = normalize_path(pth_file) if self._permitted(pth_file): if pth_file not in self.pth: @@ -367,8 +360,7 @@ class UninstallPathSet(object): else: self._refuse.add(pth_file) - def remove(self, auto_confirm=False, verbose=False): - # type: (bool, bool) -> None + def remove(self, auto_confirm: bool = False, verbose: bool = False) -> None: """Remove paths in ``self.paths`` with confirmation (unless ``auto_confirm`` is True).""" @@ -379,10 +371,8 @@ class UninstallPathSet(object): ) return - dist_name_version = ( - self.dist.project_name + "-" + self.dist.version - ) - logger.info('Uninstalling %s:', dist_name_version) + dist_name_version = self.dist.project_name + "-" + self.dist.version + logger.info("Uninstalling %s:", dist_name_version) with indent_log(): if auto_confirm or self._allowed_to_proceed(verbose): @@ -392,20 +382,17 @@ class UninstallPathSet(object): for path in sorted(compact(for_rename)): moved.stash(path) - logger.debug('Removing file or directory %s', path) + logger.verbose("Removing file or directory %s", path) for pth in self.pth.values(): pth.remove() - logger.info('Successfully uninstalled %s', dist_name_version) + logger.info("Successfully uninstalled %s", dist_name_version) - def _allowed_to_proceed(self, verbose): - # type: (bool) -> bool - """Display which files would be deleted and prompt for confirmation - """ + def _allowed_to_proceed(self, verbose: bool) -> bool: + """Display which files would be deleted and prompt for confirmation""" - def _display(msg, paths): - # type: (str, Iterable[str]) -> None + def _display(msg: str, paths: Iterable[str]) -> None: if not paths: return @@ -422,16 +409,15 @@ class UninstallPathSet(object): will_remove = set(self.paths) will_skip = set() - _display('Would remove:', will_remove) - _display('Would not remove (might be manually added):', will_skip) - _display('Would not remove (outside of prefix):', self._refuse) + _display("Would remove:", will_remove) + _display("Would not remove (might be manually added):", will_skip) + _display("Would not remove (outside of prefix):", self._refuse) if verbose: - _display('Will actually move:', compress_for_rename(self.paths)) + _display("Will actually move:", compress_for_rename(self.paths)) - return ask('Proceed (y/n)? ', ('y', 'n')) == 'y' + return ask("Proceed (Y/n)? ", ("y", "n", "")) != "n" - def rollback(self): - # type: () -> None + def rollback(self) -> None: """Rollback the changes previously made by remove().""" if not self._moved_paths.can_rollback: logger.error( @@ -439,19 +425,17 @@ class UninstallPathSet(object): self.dist.project_name, ) return - logger.info('Rolling back uninstall of %s', self.dist.project_name) + logger.info("Rolling back uninstall of %s", self.dist.project_name) self._moved_paths.rollback() for pth in self.pth.values(): pth.rollback() - def commit(self): - # type: () -> None + def commit(self) -> None: """Remove temporary save dir: rollback will no longer be possible.""" self._moved_paths.commit() @classmethod - def from_dist(cls, dist): - # type: (Distribution) -> UninstallPathSet + def from_dist(cls, dist: Distribution) -> "UninstallPathSet": dist_path = normalize_path(dist.location) if not dist_is_local(dist): logger.info( @@ -462,9 +446,11 @@ class UninstallPathSet(object): ) return cls(dist) - if dist_path in {p for p in {sysconfig.get_path("stdlib"), - sysconfig.get_path("platstdlib")} - if p}: + if dist_path in { + p + for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} + if p + }: logger.info( "Not uninstalling %s at %s, as it is in the standard library.", dist.key, @@ -473,44 +459,48 @@ class UninstallPathSet(object): return cls(dist) paths_to_remove = cls(dist) - develop_egg_link = egg_link_path(dist) - develop_egg_link_egg_info = '{}.egg-info'.format( - pkg_resources.to_filename(dist.project_name)) + develop_egg_link = egg_link_path_from_location(dist.project_name) + develop_egg_link_egg_info = "{}.egg-info".format( + pkg_resources.to_filename(dist.project_name) + ) egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) # Special case for distutils installed package - distutils_egg_info = getattr(dist._provider, 'path', None) + distutils_egg_info = getattr(dist._provider, "path", None) # Uninstall cases order do matter as in the case of 2 installs of the # same package, pip needs to uninstall the currently detected version - if (egg_info_exists and dist.egg_info.endswith('.egg-info') and - not dist.egg_info.endswith(develop_egg_link_egg_info)): + if ( + egg_info_exists + and dist.egg_info.endswith(".egg-info") + and not dist.egg_info.endswith(develop_egg_link_egg_info) + ): # if dist.egg_info.endswith(develop_egg_link_egg_info), we # are in fact in the develop_egg_link case paths_to_remove.add(dist.egg_info) - if dist.has_metadata('installed-files.txt'): + if dist.has_metadata("installed-files.txt"): for installed_file in dist.get_metadata( - 'installed-files.txt').splitlines(): - path = os.path.normpath( - os.path.join(dist.egg_info, installed_file) - ) + "installed-files.txt" + ).splitlines(): + path = os.path.normpath(os.path.join(dist.egg_info, installed_file)) paths_to_remove.add(path) # FIXME: need a test for this elif block # occurs with --single-version-externally-managed/--record outside # of pip - elif dist.has_metadata('top_level.txt'): - if dist.has_metadata('namespace_packages.txt'): - namespaces = dist.get_metadata('namespace_packages.txt') + elif dist.has_metadata("top_level.txt"): + if dist.has_metadata("namespace_packages.txt"): + namespaces = dist.get_metadata("namespace_packages.txt") else: namespaces = [] for top_level_pkg in [ - p for p - in dist.get_metadata('top_level.txt').splitlines() - if p and p not in namespaces]: + p + for p in dist.get_metadata("top_level.txt").splitlines() + if p and p not in namespaces + ]: path = os.path.join(dist.location, top_level_pkg) paths_to_remove.add(path) - paths_to_remove.add(path + '.py') - paths_to_remove.add(path + '.pyc') - paths_to_remove.add(path + '.pyo') + paths_to_remove.add(path + ".py") + paths_to_remove.add(path + ".pyc") + paths_to_remove.add(path + ".pyo") elif distutils_egg_info: raise UninstallationError( @@ -521,58 +511,61 @@ class UninstallPathSet(object): ) ) - elif dist.location.endswith('.egg'): + elif dist.location.endswith(".egg"): # package installed by easy_install # We cannot match on dist.egg_name because it can slightly vary # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg paths_to_remove.add(dist.location) easy_install_egg = os.path.split(dist.location)[1] - easy_install_pth = os.path.join(os.path.dirname(dist.location), - 'easy-install.pth') - paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + easy_install_pth = os.path.join( + os.path.dirname(dist.location), "easy-install.pth" + ) + paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg) - elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + elif egg_info_exists and dist.egg_info.endswith(".dist-info"): for path in uninstallation_paths(dist): paths_to_remove.add(path) elif develop_egg_link: # develop egg - with open(develop_egg_link, 'r') as fh: + with open(develop_egg_link) as fh: link_pointer = os.path.normcase(fh.readline().strip()) - assert (link_pointer == dist.location), ( - 'Egg-link {} does not match installed location of {} ' - '(at {})'.format( - link_pointer, dist.project_name, dist.location) + assert ( + link_pointer == dist.location + ), "Egg-link {} does not match installed location of {} (at {})".format( + link_pointer, dist.project_name, dist.location ) paths_to_remove.add(develop_egg_link) - easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), - 'easy-install.pth') + easy_install_pth = os.path.join( + os.path.dirname(develop_egg_link), "easy-install.pth" + ) paths_to_remove.add_pth(easy_install_pth, dist.location) else: logger.debug( - 'Not sure how to uninstall: %s - Check: %s', - dist, dist.location, + "Not sure how to uninstall: %s - Check: %s", + dist, + dist.location, ) # find distutils scripts= scripts - if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): - for script in dist.metadata_listdir('scripts'): + if dist.has_metadata("scripts") and dist.metadata_isdir("scripts"): + for script in dist.metadata_listdir("scripts"): if dist_in_usersite(dist): - bin_dir = bin_user + bin_dir = get_bin_user() else: - bin_dir = bin_py + bin_dir = get_bin_prefix() paths_to_remove.add(os.path.join(bin_dir, script)) if WINDOWS: - paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + paths_to_remove.add(os.path.join(bin_dir, script) + ".bat") # find console_scripts _scripts_to_remove = [] - console_scripts = dist.get_entry_map(group='console_scripts') + console_scripts = dist.get_entry_map(group="console_scripts") for name in console_scripts.keys(): _scripts_to_remove.extend(_script_names(dist, name, False)) # find gui_scripts - gui_scripts = dist.get_entry_map(group='gui_scripts') + gui_scripts = dist.get_entry_map(group="gui_scripts") for name in gui_scripts.keys(): _scripts_to_remove.extend(_script_names(dist, name, True)) @@ -582,15 +575,13 @@ class UninstallPathSet(object): return paths_to_remove -class UninstallPthEntries(object): - def __init__(self, pth_file): - # type: (str) -> None +class UninstallPthEntries: + def __init__(self, pth_file: str) -> None: self.file = pth_file - self.entries = set() # type: Set[str] - self._saved_lines = None # type: Optional[List[bytes]] + self.entries: Set[str] = set() + self._saved_lines: Optional[List[bytes]] = None - def add(self, entry): - # type: (str) -> None + def add(self, entry: str) -> None: entry = os.path.normcase(entry) # On Windows, os.path.normcase converts the entry to use # backslashes. This is correct for entries that describe absolute @@ -600,50 +591,43 @@ class UninstallPthEntries(object): # treats non-absolute paths with drive letter markings like c:foo\bar # as absolute paths. It also does not recognize UNC paths if they don't # have more than "\\sever\share". Valid examples: "\\server\share\" or - # "\\server\share\folder". Python 2.7.8+ support UNC in splitdrive. + # "\\server\share\folder". if WINDOWS and not os.path.splitdrive(entry)[0]: - entry = entry.replace('\\', '/') + entry = entry.replace("\\", "/") self.entries.add(entry) - def remove(self): - # type: () -> None - logger.debug('Removing pth entries from %s:', self.file) + def remove(self) -> None: + logger.verbose("Removing pth entries from %s:", self.file) # If the file doesn't exist, log a warning and return if not os.path.isfile(self.file): - logger.warning( - "Cannot remove entries from nonexistent file {}".format( - self.file) - ) + logger.warning("Cannot remove entries from nonexistent file %s", self.file) return - with open(self.file, 'rb') as fh: + with open(self.file, "rb") as fh: # windows uses '\r\n' with py3k, but uses '\n' with py2.x lines = fh.readlines() self._saved_lines = lines - if any(b'\r\n' in line for line in lines): - endline = '\r\n' + if any(b"\r\n" in line for line in lines): + endline = "\r\n" else: - endline = '\n' + endline = "\n" # handle missing trailing newline if lines and not lines[-1].endswith(endline.encode("utf-8")): lines[-1] = lines[-1] + endline.encode("utf-8") for entry in self.entries: try: - logger.debug('Removing entry: %s', entry) + logger.verbose("Removing entry: %s", entry) lines.remove((entry + endline).encode("utf-8")) except ValueError: pass - with open(self.file, 'wb') as fh: + with open(self.file, "wb") as fh: fh.writelines(lines) - def rollback(self): - # type: () -> bool + def rollback(self) -> bool: if self._saved_lines is None: - logger.error( - 'Cannot roll back changes to %s, none were made', self.file - ) + logger.error("Cannot roll back changes to %s, none were made", self.file) return False - logger.debug('Rolling %s back to previous state', self.file) - with open(self.file, 'wb') as fh: + logger.debug("Rolling %s back to previous state", self.file) + with open(self.file, "wb") as fh: fh.writelines(self._saved_lines) return True diff --git a/venv/Lib/site-packages/pip/_internal/resolution/base.py b/venv/Lib/site-packages/pip/_internal/resolution/base.py index 2fa118bd8949e38007939a7c69c9e8f90f8bd64e..42dade18c1ec2b825f756dad4aaa89f2d9e6ce21 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/base.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/base.py @@ -1,20 +1,20 @@ -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from typing import Callable, List, Optional -if MYPY_CHECK_RUNNING: - from typing import Callable, List - from pip._internal.req.req_install import InstallRequirement - from pip._internal.req.req_set import RequirementSet +from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_set import RequirementSet - InstallRequirementProvider = Callable[ - [str, InstallRequirement], InstallRequirement - ] +InstallRequirementProvider = Callable[ + [str, Optional[InstallRequirement]], InstallRequirement +] -class BaseResolver(object): - def resolve(self, root_reqs, check_supported_wheels): - # type: (List[InstallRequirement], bool) -> RequirementSet +class BaseResolver: + def resolve( + self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + ) -> RequirementSet: raise NotImplementedError() - def get_installation_order(self, req_set): - # type: (RequirementSet) -> List[InstallRequirement] + def get_installation_order( + self, req_set: RequirementSet + ) -> List[InstallRequirement]: raise NotImplementedError() diff --git a/venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py b/venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py index cdb44d19dbe6156966e6e0de25b64c0ed0617202..09caaa6d534f03041821b7a08ff9840713d79562 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -12,55 +12,50 @@ for sub-dependencies # The following comment should be removed at some point in the future. # mypy: strict-optional=False -# mypy: disallow-untyped-defs=False import logging import sys from collections import defaultdict from itertools import chain +from typing import DefaultDict, Iterable, List, Optional, Set, Tuple from pip._vendor.packaging import specifiers +from pip._vendor.packaging.requirements import Requirement +from pip._internal.cache import WheelCache from pip._internal.exceptions import ( BestVersionAlreadyInstalled, DistributionNotFound, HashError, HashErrors, + NoneMetadataError, UnsupportedPythonVersion, ) +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution +from pip._internal.models.link import Link +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.req_install import ( + InstallRequirement, + check_invalid_constraint_type, +) from pip._internal.req.req_set import RequirementSet -from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider from pip._internal.utils.compatibility_tags import get_supported from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import dist_in_usersite, normalize_version_info -from pip._internal.utils.packaging import ( - check_requires_python, - get_requires_python, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import DefaultDict, List, Optional, Set, Tuple - from pip._vendor import pkg_resources - - from pip._internal.cache import WheelCache - from pip._internal.distributions import AbstractDistribution - from pip._internal.index.package_finder import PackageFinder - from pip._internal.operations.prepare import RequirementPreparer - from pip._internal.req.req_install import InstallRequirement - from pip._internal.resolution.base import InstallRequirementProvider - - DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] +from pip._internal.utils.packaging import check_requires_python logger = logging.getLogger(__name__) +DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] + def _check_dist_requires_python( - dist, # type: pkg_resources.Distribution - version_info, # type: Tuple[int, int, int] - ignore_requires_python=False, # type: bool -): - # type: (...) -> None + dist: BaseDistribution, + version_info: Tuple[int, int, int], + ignore_requires_python: bool = False, +) -> None: """ Check whether the given Python version is compatible with a distribution's "Requires-Python" value. @@ -73,34 +68,42 @@ def _check_dist_requires_python( :raises UnsupportedPythonVersion: When the given Python version isn't compatible. """ - requires_python = get_requires_python(dist) + # This idiosyncratically converts the SpecifierSet to str and let + # check_requires_python then parse it again into SpecifierSet. But this + # is the legacy resolver so I'm just not going to bother refactoring. + try: + requires_python = str(dist.requires_python) + except FileNotFoundError as e: + raise NoneMetadataError(dist, str(e)) try: is_compatible = check_requires_python( - requires_python, version_info=version_info, + requires_python, + version_info=version_info, ) except specifiers.InvalidSpecifier as exc: logger.warning( - "Package %r has an invalid Requires-Python: %s", - dist.project_name, exc, + "Package %r has an invalid Requires-Python: %s", dist.raw_name, exc ) return if is_compatible: return - version = '.'.join(map(str, version_info)) + version = ".".join(map(str, version_info)) if ignore_requires_python: logger.debug( - 'Ignoring failed Requires-Python check for package %r: ' - '%s not in %r', - dist.project_name, version, requires_python, + "Ignoring failed Requires-Python check for package %r: %s not in %r", + dist.raw_name, + version, + requires_python, ) return raise UnsupportedPythonVersion( - 'Package {!r} requires a different Python: {} not in {!r}'.format( - dist.project_name, version, requires_python, - )) + "Package {!r} requires a different Python: {} not in {!r}".format( + dist.raw_name, version, requires_python + ) + ) class Resolver(BaseResolver): @@ -112,20 +115,19 @@ class Resolver(BaseResolver): def __init__( self, - preparer, # type: RequirementPreparer - finder, # type: PackageFinder - wheel_cache, # type: Optional[WheelCache] - make_install_req, # type: InstallRequirementProvider - use_user_site, # type: bool - ignore_dependencies, # type: bool - ignore_installed, # type: bool - ignore_requires_python, # type: bool - force_reinstall, # type: bool - upgrade_strategy, # type: str - py_version_info=None, # type: Optional[Tuple[int, ...]] - ): - # type: (...) -> None - super(Resolver, self).__init__() + preparer: RequirementPreparer, + finder: PackageFinder, + wheel_cache: Optional[WheelCache], + make_install_req: InstallRequirementProvider, + use_user_site: bool, + ignore_dependencies: bool, + ignore_installed: bool, + ignore_requires_python: bool, + force_reinstall: bool, + upgrade_strategy: str, + py_version_info: Optional[Tuple[int, ...]] = None, + ) -> None: + super().__init__() assert upgrade_strategy in self._allowed_strategies if py_version_info is None: @@ -147,11 +149,11 @@ class Resolver(BaseResolver): self.use_user_site = use_user_site self._make_install_req = make_install_req - self._discovered_dependencies = \ - defaultdict(list) # type: DiscoveredDependencies + self._discovered_dependencies: DiscoveredDependencies = defaultdict(list) - def resolve(self, root_reqs, check_supported_wheels): - # type: (List[InstallRequirement], bool) -> RequirementSet + def resolve( + self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + ) -> RequirementSet: """Resolve what operations need to be done As a side-effect of this method, the packages (and their dependencies) @@ -162,19 +164,19 @@ class Resolver(BaseResolver): possible to move the preparation to become a step separated from dependency resolution. """ - requirement_set = RequirementSet( - check_supported_wheels=check_supported_wheels - ) + requirement_set = RequirementSet(check_supported_wheels=check_supported_wheels) for req in root_reqs: + if req.constraint: + check_invalid_constraint_type(req) requirement_set.add_requirement(req) # Actually prepare the files, and collect any exceptions. Most hash # exceptions cannot be checked ahead of time, because # _populate_link() needs to be called before we can make decisions # based on link type. - discovered_reqs = [] # type: List[InstallRequirement] + discovered_reqs: List[InstallRequirement] = [] hash_errors = HashErrors() - for req in chain(root_reqs, discovered_reqs): + for req in chain(requirement_set.all_requirements, discovered_reqs): try: discovered_reqs.extend(self._resolve_one(requirement_set, req)) except HashError as exc: @@ -186,18 +188,16 @@ class Resolver(BaseResolver): return requirement_set - def _is_upgrade_allowed(self, req): - # type: (InstallRequirement) -> bool + def _is_upgrade_allowed(self, req: InstallRequirement) -> bool: if self.upgrade_strategy == "to-satisfy-only": return False elif self.upgrade_strategy == "eager": return True else: assert self.upgrade_strategy == "only-if-needed" - return req.is_direct + return req.user_supplied or req.constraint - def _set_req_to_reinstall(self, req): - # type: (InstallRequirement) -> None + def _set_req_to_reinstall(self, req: InstallRequirement) -> None: """ Set a requirement to be installed. """ @@ -207,8 +207,9 @@ class Resolver(BaseResolver): req.should_reinstall = True req.satisfied_by = None - def _check_skip_installed(self, req_to_install): - # type: (InstallRequirement) -> Optional[str] + def _check_skip_installed( + self, req_to_install: InstallRequirement + ) -> Optional[str]: """Check if req_to_install should be skipped. This will check if the req is installed, and whether we should upgrade @@ -239,8 +240,8 @@ class Resolver(BaseResolver): if not self._is_upgrade_allowed(req_to_install): if self.upgrade_strategy == "only-if-needed": - return 'already satisfied, skipping upgrade' - return 'already satisfied' + return "already satisfied, skipping upgrade" + return "already satisfied" # Check for the possibility of an upgrade. For link-based # requirements we have to pull the tree down and inspect to assess @@ -250,7 +251,7 @@ class Resolver(BaseResolver): self.finder.find_requirement(req_to_install, upgrade=True) except BestVersionAlreadyInstalled: # Then the best version is installed. - return 'already up-to-date' + return "already up-to-date" except DistributionNotFound: # No distribution found, so we squash the error. It will # be raised later when we re-try later to do the install. @@ -260,8 +261,29 @@ class Resolver(BaseResolver): self._set_req_to_reinstall(req_to_install) return None - def _populate_link(self, req): - # type: (InstallRequirement) -> None + def _find_requirement_link(self, req: InstallRequirement) -> Optional[Link]: + upgrade = self._is_upgrade_allowed(req) + best_candidate = self.finder.find_requirement(req, upgrade) + if not best_candidate: + return None + + # Log a warning per PEP 592 if necessary before returning. + link = best_candidate.link + if link.is_yanked: + reason = link.yanked_reason or "" + msg = ( + # Mark this as a unicode string to prevent + # "UnicodeEncodeError: 'ascii' codec can't encode character" + # in Python 2 when the reason contains non-ascii characters. + "The candidate selected for download or install is a " + "yanked version: {candidate}\n" + "Reason for being yanked: {reason}" + ).format(candidate=best_candidate, reason=reason) + logger.warning(msg) + + return link + + def _populate_link(self, req: InstallRequirement) -> None: """Ensure that if a link can be found for this, that it is found. Note that req.link may still be None - if the requirement is already @@ -274,9 +296,8 @@ class Resolver(BaseResolver): mismatches. Furthermore, cached wheels at present have undeterministic contents due to file modification times. """ - upgrade = self._is_upgrade_allowed(req) if req.link is None: - req.link = self.finder.find_requirement(req, upgrade) + req.link = self._find_requirement_link(req) if self.wheel_cache is None or self.preparer.require_hashes: return @@ -286,13 +307,12 @@ class Resolver(BaseResolver): supported_tags=get_supported(), ) if cache_entry is not None: - logger.debug('Using cached wheel link: %s', cache_entry.link) + logger.debug("Using cached wheel link: %s", cache_entry.link) if req.link is req.original_link and cache_entry.persistent: req.original_link_is_in_wheel_cache = True req.link = cache_entry.link - def _get_abstract_dist_for(self, req): - # type: (InstallRequirement) -> AbstractDistribution + def _get_dist_for(self, req: InstallRequirement) -> BaseDistribution: """Takes a InstallRequirement and returns a single AbstractDist \ representing a prepared variant of the same. """ @@ -305,13 +325,11 @@ class Resolver(BaseResolver): skip_reason = self._check_skip_installed(req) if req.satisfied_by: - return self.preparer.prepare_installed_requirement( - req, skip_reason - ) + return self.preparer.prepare_installed_requirement(req, skip_reason) # We eagerly populate the link, since that's our "legacy" behavior. self._populate_link(req) - abstract_dist = self.preparer.prepare_linked_requirement(req) + dist = self.preparer.prepare_linked_requirement(req) # NOTE # The following portion is for determining if a certain package is @@ -326,27 +344,25 @@ class Resolver(BaseResolver): if req.satisfied_by: should_modify = ( - self.upgrade_strategy != "to-satisfy-only" or - self.force_reinstall or - self.ignore_installed or - req.link.scheme == 'file' + self.upgrade_strategy != "to-satisfy-only" + or self.force_reinstall + or self.ignore_installed + or req.link.scheme == "file" ) if should_modify: self._set_req_to_reinstall(req) else: logger.info( - 'Requirement already satisfied (use --upgrade to upgrade):' - ' %s', req, + "Requirement already satisfied (use --upgrade to upgrade): %s", + req, ) - - return abstract_dist + return dist def _resolve_one( self, - requirement_set, # type: RequirementSet - req_to_install, # type: InstallRequirement - ): - # type: (...) -> List[InstallRequirement] + requirement_set: RequirementSet, + req_to_install: InstallRequirement, + ) -> List[InstallRequirement]: """Prepare a single requirements file. :return: A list of additional InstallRequirements to also install. @@ -359,24 +375,23 @@ class Resolver(BaseResolver): req_to_install.prepared = True - abstract_dist = self._get_abstract_dist_for(req_to_install) - # Parse and return dependencies - dist = abstract_dist.get_pkg_resources_distribution() + dist = self._get_dist_for(req_to_install) # This will raise UnsupportedPythonVersion if the given Python # version isn't compatible with the distribution's Requires-Python. _check_dist_requires_python( - dist, version_info=self._py_version_info, + dist, + version_info=self._py_version_info, ignore_requires_python=self.ignore_requires_python, ) - more_reqs = [] # type: List[InstallRequirement] + more_reqs: List[InstallRequirement] = [] - def add_req(subreq, extras_requested): - sub_install_req = self._make_install_req( - str(subreq), - req_to_install, - ) + def add_req(subreq: Requirement, extras_requested: Iterable[str]) -> None: + # This idiosyncratically converts the Requirement to str and let + # make_install_req then parse it again into Requirement. But this is + # the legacy resolver so I'm just not going to bother refactoring. + sub_install_req = self._make_install_req(str(subreq), req_to_install) parent_req_name = req_to_install.name to_scan_again, add_to_parent = requirement_set.add_requirement( sub_install_req, @@ -384,9 +399,7 @@ class Resolver(BaseResolver): extras_requested=extras_requested, ) if parent_req_name and add_to_parent: - self._discovered_dependencies[parent_req_name].append( - add_to_parent - ) + self._discovered_dependencies[parent_req_name].append(add_to_parent) more_reqs.extend(to_scan_again) with indent_log(): @@ -396,42 +409,37 @@ class Resolver(BaseResolver): # 'unnamed' requirements will get added here # 'unnamed' requirements can only come from being directly # provided by the user. - assert req_to_install.is_direct - requirement_set.add_requirement( - req_to_install, parent_req_name=None, - ) + assert req_to_install.user_supplied + requirement_set.add_requirement(req_to_install, parent_req_name=None) if not self.ignore_dependencies: if req_to_install.extras: logger.debug( "Installing extra requirements: %r", - ','.join(req_to_install.extras), + ",".join(req_to_install.extras), ) missing_requested = sorted( - set(req_to_install.extras) - set(dist.extras) + set(req_to_install.extras) - set(dist.iter_provided_extras()) ) for missing in missing_requested: logger.warning( - '%s does not provide the extra \'%s\'', - dist, missing + "%s %s does not provide the extra '%s'", + dist.raw_name, + dist.version, + missing, ) available_requested = sorted( - set(dist.extras) & set(req_to_install.extras) + set(dist.iter_provided_extras()) & set(req_to_install.extras) ) - for subreq in dist.requires(available_requested): + for subreq in dist.iter_dependencies(available_requested): add_req(subreq, extras_requested=available_requested) - if not req_to_install.editable and not req_to_install.satisfied_by: - # XXX: --no-install leads this to report 'Successfully - # downloaded' for only non-editable reqs, even though we took - # action on them. - req_to_install.successfully_downloaded = True - return more_reqs - def get_installation_order(self, req_set): - # type: (RequirementSet) -> List[InstallRequirement] + def get_installation_order( + self, req_set: RequirementSet + ) -> List[InstallRequirement]: """Create the installation order. The installation order is topological - requirements are installed @@ -442,9 +450,9 @@ class Resolver(BaseResolver): # installs the user specified things in the order given, except when # dependencies must come earlier to achieve topological order. order = [] - ordered_reqs = set() # type: Set[InstallRequirement] + ordered_reqs: Set[InstallRequirement] = set() - def schedule(req): + def schedule(req: InstallRequirement) -> None: if req.satisfied_by or req in ordered_reqs: return if req.constraint: diff --git a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py index 5f99618ce952e3048f4ddb8d3c72fa026ca97141..b206692a0a976d8336e3f5896eadf4765a33fb2c 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py @@ -1,52 +1,141 @@ -from pip._vendor.packaging.utils import canonicalize_name +from typing import FrozenSet, Iterable, Optional, Tuple, Union -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.version import LegacyVersion, Version -if MYPY_CHECK_RUNNING: - from typing import Optional, Sequence, Set +from pip._internal.models.link import Link, links_equivalent +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.hashes import Hashes - from pip._internal.req.req_install import InstallRequirement - from pip._vendor.packaging.version import _BaseVersion +CandidateLookup = Tuple[Optional["Candidate"], Optional[InstallRequirement]] +CandidateVersion = Union[LegacyVersion, Version] -def format_name(project, extras): - # type: (str, Set[str]) -> str +def format_name(project: str, extras: FrozenSet[str]) -> str: if not extras: return project canonical_extras = sorted(canonicalize_name(e) for e in extras) return "{}[{}]".format(project, ",".join(canonical_extras)) -class Requirement(object): +class Constraint: + def __init__( + self, specifier: SpecifierSet, hashes: Hashes, links: FrozenSet[Link] + ) -> None: + self.specifier = specifier + self.hashes = hashes + self.links = links + + @classmethod + def empty(cls) -> "Constraint": + return Constraint(SpecifierSet(), Hashes(), frozenset()) + + @classmethod + def from_ireq(cls, ireq: InstallRequirement) -> "Constraint": + links = frozenset([ireq.link]) if ireq.link else frozenset() + return Constraint(ireq.specifier, ireq.hashes(trust_internet=False), links) + + def __bool__(self) -> bool: + return bool(self.specifier) or bool(self.hashes) or bool(self.links) + + def __and__(self, other: InstallRequirement) -> "Constraint": + if not isinstance(other, InstallRequirement): + return NotImplemented + specifier = self.specifier & other.specifier + hashes = self.hashes & other.hashes(trust_internet=False) + links = self.links + if other.link: + links = links.union([other.link]) + return Constraint(specifier, hashes, links) + + def is_satisfied_by(self, candidate: "Candidate") -> bool: + # Reject if there are any mismatched URL constraints on this package. + if self.links and not all(_match_link(link, candidate) for link in self.links): + return False + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) + + +class Requirement: @property - def name(self): - # type: () -> str + def project_name(self) -> NormalizedName: + """The "project name" of a requirement. + + This is different from ``name`` if this requirement contains extras, + in which case ``name`` would contain the ``[...]`` part, while this + refers to the name of the project. + """ raise NotImplementedError("Subclass should override") - def find_matches(self): - # type: () -> Sequence[Candidate] + @property + def name(self) -> str: + """The name identifying this requirement in the resolver. + + This is different from ``project_name`` if this requirement contains + extras, where ``project_name`` would not contain the ``[...]`` part. + """ raise NotImplementedError("Subclass should override") - def is_satisfied_by(self, candidate): - # type: (Candidate) -> bool + def is_satisfied_by(self, candidate: "Candidate") -> bool: return False + def get_candidate_lookup(self) -> CandidateLookup: + raise NotImplementedError("Subclass should override") + + def format_for_error(self) -> str: + raise NotImplementedError("Subclass should override") + + +def _match_link(link: Link, candidate: "Candidate") -> bool: + if candidate.source_link: + return links_equivalent(link, candidate.source_link) + return False + -class Candidate(object): +class Candidate: @property - def name(self): - # type: () -> str + def project_name(self) -> NormalizedName: + """The "project name" of the candidate. + + This is different from ``name`` if this candidate contains extras, + in which case ``name`` would contain the ``[...]`` part, while this + refers to the name of the project. + """ + raise NotImplementedError("Override in subclass") + + @property + def name(self) -> str: + """The name identifying this candidate in the resolver. + + This is different from ``project_name`` if this candidate contains + extras, where ``project_name`` would not contain the ``[...]`` part. + """ + raise NotImplementedError("Override in subclass") + + @property + def version(self) -> CandidateVersion: raise NotImplementedError("Override in subclass") @property - def version(self): - # type: () -> _BaseVersion + def is_installed(self) -> bool: raise NotImplementedError("Override in subclass") - def get_dependencies(self): - # type: () -> Sequence[Requirement] + @property + def is_editable(self) -> bool: + raise NotImplementedError("Override in subclass") + + @property + def source_link(self) -> Optional[Link]: raise NotImplementedError("Override in subclass") - def get_install_requirement(self): - # type: () -> Optional[InstallRequirement] + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: raise NotImplementedError("Override in subclass") + + def get_install_requirement(self) -> Optional[InstallRequirement]: + raise NotImplementedError("Override in subclass") + + def format_for_error(self) -> str: + raise NotImplementedError("Subclass should override") diff --git a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py index f8461ade266f780096e2d0d3393b5cd27dfc70bf..60fad55db0049267d703f93e35eaf8e7b51e34aa 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -1,323 +1,385 @@ import logging import sys +from typing import TYPE_CHECKING, Any, FrozenSet, Iterable, Optional, Tuple, Union, cast -from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet -from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name from pip._vendor.packaging.version import Version +from pip._internal.exceptions import HashError, MetadataInconsistent +from pip._internal.metadata import BaseDistribution +from pip._internal.models.link import Link, links_equivalent +from pip._internal.models.wheel import Wheel from pip._internal.req.constructors import ( install_req_from_editable, install_req_from_line, ) from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.misc import normalize_version_info -from pip._internal.utils.packaging import get_requires_python -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from .base import Candidate, format_name +from .base import Candidate, CandidateVersion, Requirement, format_name -if MYPY_CHECK_RUNNING: - from typing import Any, Optional, Sequence, Set, Tuple, Union - - from pip._vendor.packaging.version import _BaseVersion - from pip._vendor.pkg_resources import Distribution - - from pip._internal.distributions import AbstractDistribution - from pip._internal.models.link import Link - - from .base import Requirement +if TYPE_CHECKING: from .factory import Factory - BaseCandidate = Union[ - "AlreadyInstalledCandidate", - "EditableCandidate", - "LinkCandidate", - ] +logger = logging.getLogger(__name__) +BaseCandidate = Union[ + "AlreadyInstalledCandidate", + "EditableCandidate", + "LinkCandidate", +] -logger = logging.getLogger(__name__) +# Avoid conflicting with the PyPI package "Python". +REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, "") -def make_install_req_from_link(link, parent): - # type: (Link, InstallRequirement) -> InstallRequirement - assert not parent.editable, "parent is editable" - return install_req_from_line( - link.url, - comes_from=parent.comes_from, - use_pep517=parent.use_pep517, - isolated=parent.isolated, - constraint=parent.constraint, +def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]: + """The runtime version of BaseCandidate.""" + base_candidate_classes = ( + AlreadyInstalledCandidate, + EditableCandidate, + LinkCandidate, + ) + if isinstance(candidate, base_candidate_classes): + return candidate + return None + + +def make_install_req_from_link( + link: Link, template: InstallRequirement +) -> InstallRequirement: + assert not template.editable, "template is editable" + if template.req: + line = str(template.req) + else: + line = link.url + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, options=dict( - install_options=parent.install_options, - global_options=parent.global_options, - hashes=parent.hash_options + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options, ), ) + ireq.original_link = template.original_link + ireq.link = link + return ireq -def make_install_req_from_editable(link, parent): - # type: (Link, InstallRequirement) -> InstallRequirement - assert parent.editable, "parent not editable" +def make_install_req_from_editable( + link: Link, template: InstallRequirement +) -> InstallRequirement: + assert template.editable, "template not editable" return install_req_from_editable( link.url, - comes_from=parent.comes_from, - use_pep517=parent.use_pep517, - isolated=parent.isolated, - constraint=parent.constraint, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + permit_editable_wheels=template.permit_editable_wheels, options=dict( - install_options=parent.install_options, - global_options=parent.global_options, - hashes=parent.hash_options + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options, ), ) -def make_install_req_from_dist(dist, parent): - # type: (Distribution, InstallRequirement) -> InstallRequirement +def _make_install_req_from_dist( + dist: BaseDistribution, template: InstallRequirement +) -> InstallRequirement: + from pip._internal.metadata.pkg_resources import Distribution as _Dist + + if template.req: + line = str(template.req) + elif template.link: + line = f"{dist.canonical_name} @ {template.link.url}" + else: + line = f"{dist.canonical_name}=={dist.version}" ireq = install_req_from_line( - "{}=={}".format( - canonicalize_name(dist.project_name), - dist.parsed_version, - ), - comes_from=parent.comes_from, - use_pep517=parent.use_pep517, - isolated=parent.isolated, - constraint=parent.constraint, + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, options=dict( - install_options=parent.install_options, - global_options=parent.global_options, - hashes=parent.hash_options + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options, ), ) - ireq.satisfied_by = dist + ireq.satisfied_by = cast(_Dist, dist)._dist return ireq class _InstallRequirementBackedCandidate(Candidate): + """A candidate backed by an ``InstallRequirement``. + + This represents a package request with the target not being already + in the environment, and needs to be fetched and installed. The backing + ``InstallRequirement`` is responsible for most of the leg work; this + class exposes appropriate information to the resolver. + + :param link: The link passed to the ``InstallRequirement``. The backing + ``InstallRequirement`` will use this link to fetch the distribution. + :param source_link: The link this candidate "originates" from. This is + different from ``link`` when the link is found in the wheel cache. + ``link`` would point to the wheel cache, while this points to the + found remote link (e.g. from pypi.org). + """ + + dist: BaseDistribution + is_installed = False + def __init__( self, - link, # type: Link - ireq, # type: InstallRequirement - factory, # type: Factory - name=None, # type: Optional[str] - version=None, # type: Optional[_BaseVersion] - ): - # type: (...) -> None - self.link = link + link: Link, + source_link: Link, + ireq: InstallRequirement, + factory: "Factory", + name: Optional[NormalizedName] = None, + version: Optional[CandidateVersion] = None, + ) -> None: + self._link = link + self._source_link = source_link self._factory = factory self._ireq = ireq self._name = name self._version = version - self._dist = None # type: Optional[Distribution] + self.dist = self._prepare() + + def __str__(self) -> str: + return f"{self.name} {self.version}" - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "{class_name}({link!r})".format( class_name=self.__class__.__name__, - link=str(self.link), + link=str(self._link), ) - def __eq__(self, other): - # type: (Any) -> bool + def __hash__(self) -> int: + return hash((self.__class__, self._link)) + + def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): - return self.link == other.link + return links_equivalent(self._link, other._link) return False - # Needed for Python 2, which does not implement this by default - def __ne__(self, other): - # type: (Any) -> bool - return not self.__eq__(other) + @property + def source_link(self) -> Optional[Link]: + return self._source_link @property - def name(self): - # type: () -> str + def project_name(self) -> NormalizedName: """The normalised name of the project the candidate refers to""" if self._name is None: - self._name = canonicalize_name(self.dist.project_name) + self._name = self.dist.canonical_name return self._name @property - def version(self): - # type: () -> _BaseVersion + def name(self) -> str: + return self.project_name + + @property + def version(self) -> CandidateVersion: if self._version is None: - self._version = self.dist.parsed_version + self._version = self.dist.version return self._version - def _prepare_abstract_distribution(self): - # type: () -> AbstractDistribution - raise NotImplementedError("Override in subclass") - - def _prepare(self): - # type: () -> None - if self._dist is not None: - return - - abstract_dist = self._prepare_abstract_distribution() - self._dist = abstract_dist.get_pkg_resources_distribution() - assert self._dist is not None, "Distribution already installed" - - # TODO: Abort cleanly here, as the resolution has been - # based on the wrong name/version until now, and - # so is wrong. - # TODO: (Longer term) Rather than abort, reject this candidate - # and backtrack. This would need resolvelib support. - # These should be "proper" errors, not just asserts, as they - # can result from user errors like a requirement "foo @ URL" - # when the project at URL has a name of "bar" in its metadata. - assert ( - self._name is None or - self._name == canonicalize_name(self._dist.project_name) - ), "Name mismatch: {!r} vs {!r}".format( - self._name, canonicalize_name(self._dist.project_name), - ) - assert ( - self._version is None or - self._version == self._dist.parsed_version - ), "Version mismatch: {!r} vs {!r}".format( - self._version, self._dist.parsed_version, + def format_for_error(self) -> str: + return "{} {} (from {})".format( + self.name, + self.version, + self._link.file_path if self._link.is_file else self._link, ) - @property - def dist(self): - # type: () -> Distribution - self._prepare() - return self._dist - - def _get_requires_python_specifier(self): - # type: () -> Optional[SpecifierSet] - requires_python = get_requires_python(self.dist) - if requires_python is None: - return None - try: - spec = SpecifierSet(requires_python) - except InvalidSpecifier as e: - logger.warning( - "Package %r has an invalid Requires-Python: %s", self.name, e, + def _prepare_distribution(self) -> BaseDistribution: + raise NotImplementedError("Override in subclass") + + def _check_metadata_consistency(self, dist: BaseDistribution) -> None: + """Check for consistency of project name and version of dist.""" + if self._name is not None and self._name != dist.canonical_name: + raise MetadataInconsistent( + self._ireq, + "name", + self._name, + dist.canonical_name, + ) + if self._version is not None and self._version != dist.version: + raise MetadataInconsistent( + self._ireq, + "version", + str(self._version), + str(dist.version), ) - return None - return spec - - def get_dependencies(self): - # type: () -> Sequence[Requirement] - deps = [ - self._factory.make_requirement_from_spec(str(r), self._ireq) - for r in self.dist.requires() - ] - python_dep = self._factory.make_requires_python_requirement( - self._get_requires_python_specifier(), - ) - if python_dep: - deps.append(python_dep) - return deps - def get_install_requirement(self): - # type: () -> Optional[InstallRequirement] - self._prepare() + def _prepare(self) -> BaseDistribution: + try: + dist = self._prepare_distribution() + except HashError as e: + # Provide HashError the underlying ireq that caused it. This + # provides context for the resulting error message to show the + # offending line to the user. + e.req = self._ireq + raise + self._check_metadata_consistency(dist) + return dist + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + requires = self.dist.iter_dependencies() if with_requires else () + for r in requires: + yield self._factory.make_requirement_from_spec(str(r), self._ireq) + yield self._factory.make_requires_python_requirement(self.dist.requires_python) + + def get_install_requirement(self) -> Optional[InstallRequirement]: return self._ireq class LinkCandidate(_InstallRequirementBackedCandidate): + is_editable = False + def __init__( self, - link, # type: Link - parent, # type: InstallRequirement - factory, # type: Factory - name=None, # type: Optional[str] - version=None, # type: Optional[_BaseVersion] - ): - # type: (...) -> None - super(LinkCandidate, self).__init__( + link: Link, + template: InstallRequirement, + factory: "Factory", + name: Optional[NormalizedName] = None, + version: Optional[CandidateVersion] = None, + ) -> None: + source_link = link + cache_entry = factory.get_wheel_cache_entry(link, name) + if cache_entry is not None: + logger.debug("Using cached wheel link: %s", cache_entry.link) + link = cache_entry.link + ireq = make_install_req_from_link(link, template) + assert ireq.link == link + if ireq.link.is_wheel and not ireq.link.is_file: + wheel = Wheel(ireq.link.filename) + wheel_name = canonicalize_name(wheel.name) + assert name == wheel_name, f"{name!r} != {wheel_name!r} for wheel" + # Version may not be present for PEP 508 direct URLs + if version is not None: + wheel_version = Version(wheel.version) + assert version == wheel_version, "{!r} != {!r} for wheel {}".format( + version, wheel_version, name + ) + + if ( + cache_entry is not None + and cache_entry.persistent + and template.link is template.original_link + ): + ireq.original_link_is_in_wheel_cache = True + + super().__init__( link=link, - ireq=make_install_req_from_link(link, parent), + source_link=source_link, + ireq=ireq, factory=factory, name=name, version=version, ) - def _prepare_abstract_distribution(self): - # type: () -> AbstractDistribution - return self._factory.preparer.prepare_linked_requirement(self._ireq) + def _prepare_distribution(self) -> BaseDistribution: + preparer = self._factory.preparer + return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True) class EditableCandidate(_InstallRequirementBackedCandidate): + is_editable = True + def __init__( self, - link, # type: Link - parent, # type: InstallRequirement - factory, # type: Factory - name=None, # type: Optional[str] - version=None, # type: Optional[_BaseVersion] - ): - # type: (...) -> None - super(EditableCandidate, self).__init__( + link: Link, + template: InstallRequirement, + factory: "Factory", + name: Optional[NormalizedName] = None, + version: Optional[CandidateVersion] = None, + ) -> None: + super().__init__( link=link, - ireq=make_install_req_from_editable(link, parent), + source_link=link, + ireq=make_install_req_from_editable(link, template), factory=factory, name=name, version=version, ) - def _prepare_abstract_distribution(self): - # type: () -> AbstractDistribution + def _prepare_distribution(self) -> BaseDistribution: return self._factory.preparer.prepare_editable_requirement(self._ireq) class AlreadyInstalledCandidate(Candidate): + is_installed = True + source_link = None + def __init__( self, - dist, # type: Distribution - parent, # type: InstallRequirement - factory, # type: Factory - ): - # type: (...) -> None + dist: BaseDistribution, + template: InstallRequirement, + factory: "Factory", + ) -> None: self.dist = dist - self._ireq = make_install_req_from_dist(dist, parent) + self._ireq = _make_install_req_from_dist(dist, template) self._factory = factory # This is just logging some messages, so we can do it eagerly. # The returned dist would be exactly the same as self.dist because we - # set satisfied_by in make_install_req_from_dist. + # set satisfied_by in _make_install_req_from_dist. # TODO: Supply reason based on force_reinstall and upgrade_strategy. skip_reason = "already satisfied" factory.preparer.prepare_installed_requirement(self._ireq, skip_reason) - def __repr__(self): - # type: () -> str + def __str__(self) -> str: + return str(self.dist) + + def __repr__(self) -> str: return "{class_name}({distribution!r})".format( class_name=self.__class__.__name__, distribution=self.dist, ) - def __eq__(self, other): - # type: (Any) -> bool + def __hash__(self) -> int: + return hash((self.__class__, self.name, self.version)) + + def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): return self.name == other.name and self.version == other.version return False - # Needed for Python 2, which does not implement this by default - def __ne__(self, other): - # type: (Any) -> bool - return not self.__eq__(other) + @property + def project_name(self) -> NormalizedName: + return self.dist.canonical_name @property - def name(self): - # type: () -> str - return canonicalize_name(self.dist.project_name) + def name(self) -> str: + return self.project_name @property - def version(self): - # type: () -> _BaseVersion - return self.dist.parsed_version - - def get_dependencies(self): - # type: () -> Sequence[Requirement] - return [ - self._factory.make_requirement_from_spec(str(r), self._ireq) - for r in self.dist.requires() - ] - - def get_install_requirement(self): - # type: () -> Optional[InstallRequirement] + def version(self) -> CandidateVersion: + return self.dist.version + + @property + def is_editable(self) -> bool: + return self.dist.editable + + def format_for_error(self) -> str: + return f"{self.name} {self.version} (Installed)" + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + if not with_requires: + return + for r in self.dist.iter_dependencies(): + yield self._factory.make_requirement_from_spec(str(r), self._ireq) + + def get_install_requirement(self) -> Optional[InstallRequirement]: return None @@ -335,8 +397,8 @@ class ExtrasCandidate(Candidate): to treat it as a separate node in the dependency graph. 2. When we're getting the candidate's dependencies, a) We specify that we want the extra dependencies as well. - b) We add a dependency on the base candidate (matching the name and - version). See below for why this is needed. + b) We add a dependency on the base candidate. + See below for why this is needed. 3. We return None for the underlying InstallRequirement, as the base candidate will provide it, and we don't want to end up with duplicates. @@ -345,72 +407,93 @@ class ExtrasCandidate(Candidate): version 2.0. Having those candidates depend on foo=1.0 and foo=2.0 respectively forces the resolver to recognise that this is a conflict. """ + def __init__( self, - base, # type: BaseCandidate - extras, # type: Set[str] - ): - # type: (...) -> None + base: BaseCandidate, + extras: FrozenSet[str], + ) -> None: self.base = base self.extras = extras - def __repr__(self): - # type: () -> str + def __str__(self) -> str: + name, rest = str(self.base).split(" ", 1) + return "{}[{}] {}".format(name, ",".join(self.extras), rest) + + def __repr__(self) -> str: return "{class_name}(base={base!r}, extras={extras!r})".format( class_name=self.__class__.__name__, base=self.base, extras=self.extras, ) - def __eq__(self, other): - # type: (Any) -> bool + def __hash__(self) -> int: + return hash((self.base, self.extras)) + + def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): return self.base == other.base and self.extras == other.extras return False - # Needed for Python 2, which does not implement this by default - def __ne__(self, other): - # type: (Any) -> bool - return not self.__eq__(other) + @property + def project_name(self) -> NormalizedName: + return self.base.project_name @property - def name(self): - # type: () -> str + def name(self) -> str: """The normalised name of the project the candidate refers to""" - return format_name(self.base.name, self.extras) + return format_name(self.base.project_name, self.extras) @property - def version(self): - # type: () -> _BaseVersion + def version(self) -> CandidateVersion: return self.base.version - def get_dependencies(self): - # type: () -> Sequence[Requirement] + def format_for_error(self) -> str: + return "{} [{}]".format( + self.base.format_for_error(), ", ".join(sorted(self.extras)) + ) + + @property + def is_installed(self) -> bool: + return self.base.is_installed + + @property + def is_editable(self) -> bool: + return self.base.is_editable + + @property + def source_link(self) -> Optional[Link]: + return self.base.source_link + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: factory = self.base._factory + # Add a dependency on the exact base + # (See note 2b in the class docstring) + yield factory.make_requirement_from_candidate(self.base) + if not with_requires: + return + # The user may have specified extras that the candidate doesn't # support. We ignore any unsupported extras here. - valid_extras = self.extras.intersection(self.base.dist.extras) - invalid_extras = self.extras.difference(self.base.dist.extras) - if invalid_extras: + valid_extras = self.extras.intersection(self.base.dist.iter_provided_extras()) + invalid_extras = self.extras.difference(self.base.dist.iter_provided_extras()) + for extra in sorted(invalid_extras): logger.warning( - "Invalid extras specified in %s: %s", - self.name, - ','.join(sorted(invalid_extras)) + "%s %s does not provide the extra '%s'", + self.base.name, + self.version, + extra, ) - deps = [ - factory.make_requirement_from_spec(str(r), self.base._ireq) - for r in self.base.dist.requires(valid_extras) - ] - # Add a dependency on the exact base. - # (See note 2b in the class docstring) - spec = "{}=={}".format(self.base.name, self.base.version) - deps.append(factory.make_requirement_from_spec(spec, self.base._ireq)) - return deps + for r in self.base.dist.iter_dependencies(valid_extras): + requirement = factory.make_requirement_from_spec( + str(r), self.base._ireq, valid_extras + ) + if requirement: + yield requirement - def get_install_requirement(self): - # type: () -> Optional[InstallRequirement] + def get_install_requirement(self) -> Optional[InstallRequirement]: # We don't return anything here, because we always # depend on the base candidate, and we'll get the # install requirement from that. @@ -418,8 +501,10 @@ class ExtrasCandidate(Candidate): class RequiresPythonCandidate(Candidate): - def __init__(self, py_version_info): - # type: (Optional[Tuple[int, ...]]) -> None + is_installed = False + source_link = None + + def __init__(self, py_version_info: Optional[Tuple[int, ...]]) -> None: if py_version_info is not None: version_info = normalize_version_info(py_version_info) else: @@ -430,21 +515,26 @@ class RequiresPythonCandidate(Candidate): # only one RequiresPythonCandidate in a resolution, i.e. the host Python. # The built-in object.__eq__() and object.__ne__() do exactly what we want. + def __str__(self) -> str: + return f"Python {self._version}" + + @property + def project_name(self) -> NormalizedName: + return REQUIRES_PYTHON_IDENTIFIER + @property - def name(self): - # type: () -> str - # Avoid conflicting with the PyPI package "Python". - return "" + def name(self) -> str: + return REQUIRES_PYTHON_IDENTIFIER @property - def version(self): - # type: () -> _BaseVersion + def version(self) -> CandidateVersion: return self._version - def get_dependencies(self): - # type: () -> Sequence[Requirement] - return [] + def format_for_error(self) -> str: + return f"Python {self.version}" + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + return () - def get_install_requirement(self): - # type: () -> Optional[InstallRequirement] + def get_install_requirement(self) -> Optional[InstallRequirement]: return None diff --git a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py index 23686f76ac208b11e5c4315be7676312ccd890e5..766dc26c0f98210a7ec8f32c0c95cba94ee9d3e1 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -1,201 +1,701 @@ -from pip._vendor.packaging.utils import canonicalize_name +import contextlib +import functools +import logging +from typing import ( + TYPE_CHECKING, + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Mapping, + NamedTuple, + Optional, + Sequence, + Set, + Tuple, + TypeVar, + cast, +) + +from pip._vendor.packaging.requirements import InvalidRequirement +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.resolvelib import ResolutionImpossible +from pip._internal.cache import CacheEntry, WheelCache from pip._internal.exceptions import ( + DistributionNotFound, InstallationError, + InstallationSubprocessError, + MetadataInconsistent, UnsupportedPythonVersion, + UnsupportedWheel, ) -from pip._internal.utils.misc import get_installed_distributions -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution, get_default_environment +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import install_req_from_link_and_ireq +from pip._internal.req.req_install import ( + InstallRequirement, + check_invalid_constraint_type, +) +from pip._internal.resolution.base import InstallRequirementProvider +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.packaging import get_requirement +from pip._internal.utils.virtualenv import running_under_virtualenv +from .base import Candidate, CandidateVersion, Constraint, Requirement from .candidates import ( AlreadyInstalledCandidate, + BaseCandidate, EditableCandidate, ExtrasCandidate, LinkCandidate, RequiresPythonCandidate, + as_base_candidate, ) +from .found_candidates import FoundCandidates, IndexCandidateInfo from .requirements import ( ExplicitRequirement, RequiresPythonRequirement, SpecifierRequirement, + UnsatisfiableRequirement, ) -if MYPY_CHECK_RUNNING: - from typing import Dict, Iterator, Optional, Set, Tuple, TypeVar +if TYPE_CHECKING: + from typing import Protocol + + class ConflictCause(Protocol): + requirement: RequiresPythonRequirement + parent: Candidate - from pip._vendor.packaging.specifiers import SpecifierSet - from pip._vendor.packaging.version import _BaseVersion - from pip._vendor.pkg_resources import Distribution - from pip._vendor.resolvelib import ResolutionImpossible - from pip._internal.index.package_finder import PackageFinder - from pip._internal.models.link import Link - from pip._internal.operations.prepare import RequirementPreparer - from pip._internal.req.req_install import InstallRequirement - from pip._internal.resolution.base import InstallRequirementProvider +logger = logging.getLogger(__name__) - from .base import Candidate, Requirement - from .candidates import BaseCandidate +C = TypeVar("C") +Cache = Dict[Link, C] - C = TypeVar("C") - Cache = Dict[Link, C] +class CollectedRootRequirements(NamedTuple): + requirements: List[Requirement] + constraints: Dict[str, Constraint] + user_requested: Dict[str, int] -class Factory(object): + +class Factory: def __init__( self, - finder, # type: PackageFinder - preparer, # type: RequirementPreparer - make_install_req, # type: InstallRequirementProvider - force_reinstall, # type: bool - ignore_installed, # type: bool - ignore_requires_python, # type: bool - py_version_info=None, # type: Optional[Tuple[int, ...]] - ): - # type: (...) -> None - self.finder = finder + finder: PackageFinder, + preparer: RequirementPreparer, + make_install_req: InstallRequirementProvider, + wheel_cache: Optional[WheelCache], + use_user_site: bool, + force_reinstall: bool, + ignore_installed: bool, + ignore_requires_python: bool, + py_version_info: Optional[Tuple[int, ...]] = None, + ) -> None: + self._finder = finder self.preparer = preparer + self._wheel_cache = wheel_cache self._python_candidate = RequiresPythonCandidate(py_version_info) self._make_install_req_from_spec = make_install_req + self._use_user_site = use_user_site self._force_reinstall = force_reinstall self._ignore_requires_python = ignore_requires_python - self._link_candidate_cache = {} # type: Cache[LinkCandidate] - self._editable_candidate_cache = {} # type: Cache[EditableCandidate] + self._build_failures: Cache[InstallationError] = {} + self._link_candidate_cache: Cache[LinkCandidate] = {} + self._editable_candidate_cache: Cache[EditableCandidate] = {} + self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {} + self._extras_candidate_cache: Dict[ + Tuple[int, FrozenSet[str]], ExtrasCandidate + ] = {} if not ignore_installed: + env = get_default_environment() self._installed_dists = { - canonicalize_name(dist.project_name): dist - for dist in get_installed_distributions() + dist.canonical_name: dist + for dist in env.iter_installed_distributions(local_only=False) } else: self._installed_dists = {} + @property + def force_reinstall(self) -> bool: + return self._force_reinstall + + def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None: + if not link.is_wheel: + return + wheel = Wheel(link.filename) + if wheel.supported(self._finder.target_python.get_tags()): + return + msg = f"{link.filename} is not a supported wheel on this platform." + raise UnsupportedWheel(msg) + + def _make_extras_candidate( + self, base: BaseCandidate, extras: FrozenSet[str] + ) -> ExtrasCandidate: + cache_key = (id(base), extras) + try: + candidate = self._extras_candidate_cache[cache_key] + except KeyError: + candidate = ExtrasCandidate(base, extras) + self._extras_candidate_cache[cache_key] = candidate + return candidate + def _make_candidate_from_dist( self, - dist, # type: Distribution - extras, # type: Set[str] - parent, # type: InstallRequirement - ): - # type: (...) -> Candidate - base = AlreadyInstalledCandidate(dist, parent, factory=self) - if extras: - return ExtrasCandidate(base, extras) - return base + dist: BaseDistribution, + extras: FrozenSet[str], + template: InstallRequirement, + ) -> Candidate: + try: + base = self._installed_candidate_cache[dist.canonical_name] + except KeyError: + base = AlreadyInstalledCandidate(dist, template, factory=self) + self._installed_candidate_cache[dist.canonical_name] = base + if not extras: + return base + return self._make_extras_candidate(base, extras) def _make_candidate_from_link( self, - link, # type: Link - extras, # type: Set[str] - parent, # type: InstallRequirement - name=None, # type: Optional[str] - version=None, # type: Optional[_BaseVersion] - ): - # type: (...) -> Candidate + link: Link, + extras: FrozenSet[str], + template: InstallRequirement, + name: Optional[NormalizedName], + version: Optional[CandidateVersion], + ) -> Optional[Candidate]: # TODO: Check already installed candidate, and use it if the link and # editable flag match. - if parent.editable: + + if link in self._build_failures: + # We already tried this candidate before, and it does not build. + # Don't bother trying again. + return None + + if template.editable: if link not in self._editable_candidate_cache: - self._editable_candidate_cache[link] = EditableCandidate( - link, parent, factory=self, name=name, version=version, - ) - base = self._editable_candidate_cache[link] # type: BaseCandidate + try: + self._editable_candidate_cache[link] = EditableCandidate( + link, + template, + factory=self, + name=name, + version=version, + ) + except (InstallationSubprocessError, MetadataInconsistent) as e: + logger.warning("Discarding %s. %s", link, e) + self._build_failures[link] = e + return None + base: BaseCandidate = self._editable_candidate_cache[link] else: if link not in self._link_candidate_cache: - self._link_candidate_cache[link] = LinkCandidate( - link, parent, factory=self, name=name, version=version, - ) + try: + self._link_candidate_cache[link] = LinkCandidate( + link, + template, + factory=self, + name=name, + version=version, + ) + except (InstallationSubprocessError, MetadataInconsistent) as e: + logger.warning("Discarding %s. %s", link, e) + self._build_failures[link] = e + return None base = self._link_candidate_cache[link] - if extras: - return ExtrasCandidate(base, extras) - return base - - def iter_found_candidates(self, ireq, extras): - # type: (InstallRequirement, Set[str]) -> Iterator[Candidate] - name = canonicalize_name(ireq.req.name) - if not self._force_reinstall: - installed_dist = self._installed_dists.get(name) - else: - installed_dist = None - found = self.finder.find_best_candidate( - project_name=ireq.req.name, - specifier=ireq.req.specifier, - hashes=ireq.hashes(trust_internet=False), + if not extras: + return base + return self._make_extras_candidate(base, extras) + + def _iter_found_candidates( + self, + ireqs: Sequence[InstallRequirement], + specifier: SpecifierSet, + hashes: Hashes, + prefers_installed: bool, + incompatible_ids: Set[int], + ) -> Iterable[Candidate]: + if not ireqs: + return () + + # The InstallRequirement implementation requires us to give it a + # "template". Here we just choose the first requirement to represent + # all of them. + # Hopefully the Project model can correct this mismatch in the future. + template = ireqs[0] + assert template.req, "Candidates found on index must be PEP 508" + name = canonicalize_name(template.req.name) + + extras: FrozenSet[str] = frozenset() + for ireq in ireqs: + assert ireq.req, "Candidates found on index must be PEP 508" + specifier &= ireq.req.specifier + hashes &= ireq.hashes(trust_internet=False) + extras |= frozenset(ireq.extras) + + def _get_installed_candidate() -> Optional[Candidate]: + """Get the candidate for the currently-installed version.""" + # If --force-reinstall is set, we want the version from the index + # instead, so we "pretend" there is nothing installed. + if self._force_reinstall: + return None + try: + installed_dist = self._installed_dists[name] + except KeyError: + return None + # Don't use the installed distribution if its version does not fit + # the current dependency graph. + if not specifier.contains(installed_dist.version, prereleases=True): + return None + candidate = self._make_candidate_from_dist( + dist=installed_dist, + extras=extras, + template=template, + ) + # The candidate is a known incompatiblity. Don't use it. + if id(candidate) in incompatible_ids: + return None + return candidate + + def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]: + result = self._finder.find_best_candidate( + project_name=name, + specifier=specifier, + hashes=hashes, + ) + icans = list(result.iter_applicable()) + + # PEP 592: Yanked releases must be ignored unless only yanked + # releases can satisfy the version range. So if this is false, + # all yanked icans need to be skipped. + all_yanked = all(ican.link.is_yanked for ican in icans) + + # PackageFinder returns earlier versions first, so we reverse. + for ican in reversed(icans): + if not all_yanked and ican.link.is_yanked: + continue + func = functools.partial( + self._make_candidate_from_link, + link=ican.link, + extras=extras, + template=template, + name=name, + version=ican.version, + ) + yield ican.version, func + + return FoundCandidates( + iter_index_candidate_infos, + _get_installed_candidate(), + prefers_installed, + incompatible_ids, ) - for ican in found.iter_applicable(): - if (installed_dist is not None and - installed_dist.parsed_version == ican.version): + + def _iter_explicit_candidates_from_base( + self, + base_requirements: Iterable[Requirement], + extras: FrozenSet[str], + ) -> Iterator[Candidate]: + """Produce explicit candidates from the base given an extra-ed package. + + :param base_requirements: Requirements known to the resolver. The + requirements are guaranteed to not have extras. + :param extras: The extras to inject into the explicit requirements' + candidates. + """ + for req in base_requirements: + lookup_cand, _ = req.get_candidate_lookup() + if lookup_cand is None: # Not explicit. continue - yield self._make_candidate_from_link( - link=ican.link, - extras=extras, - parent=ireq, - name=name, - version=ican.version, + # We've stripped extras from the identifier, and should always + # get a BaseCandidate here, unless there's a bug elsewhere. + base_cand = as_base_candidate(lookup_cand) + assert base_cand is not None, "no extras here" + yield self._make_extras_candidate(base_cand, extras) + + def _iter_candidates_from_constraints( + self, + identifier: str, + constraint: Constraint, + template: InstallRequirement, + ) -> Iterator[Candidate]: + """Produce explicit candidates from constraints. + + This creates "fake" InstallRequirement objects that are basically clones + of what "should" be the template, but with original_link set to link. + """ + for link in constraint.links: + self._fail_if_link_is_unsupported_wheel(link) + candidate = self._make_candidate_from_link( + link, + extras=frozenset(), + template=install_req_from_link_and_ireq(link, template), + name=canonicalize_name(identifier), + version=None, ) + if candidate: + yield candidate - # Return installed distribution if it matches the specifier. This is - # done last so the resolver will prefer it over downloading links. - if (installed_dist is not None and - installed_dist.parsed_version in ireq.req.specifier): - yield self._make_candidate_from_dist( - dist=installed_dist, - extras=extras, - parent=ireq, + def find_candidates( + self, + identifier: str, + requirements: Mapping[str, Iterable[Requirement]], + incompatibilities: Mapping[str, Iterator[Candidate]], + constraint: Constraint, + prefers_installed: bool, + ) -> Iterable[Candidate]: + # Collect basic lookup information from the requirements. + explicit_candidates: Set[Candidate] = set() + ireqs: List[InstallRequirement] = [] + for req in requirements[identifier]: + cand, ireq = req.get_candidate_lookup() + if cand is not None: + explicit_candidates.add(cand) + if ireq is not None: + ireqs.append(ireq) + + # If the current identifier contains extras, add explicit candidates + # from entries from extra-less identifier. + with contextlib.suppress(InvalidRequirement): + parsed_requirement = get_requirement(identifier) + explicit_candidates.update( + self._iter_explicit_candidates_from_base( + requirements.get(parsed_requirement.name, ()), + frozenset(parsed_requirement.extras), + ), + ) + + # Add explicit candidates from constraints. We only do this if there are + # kown ireqs, which represent requirements not already explicit. If + # there are no ireqs, we're constraining already-explicit requirements, + # which is handled later when we return the explicit candidates. + if ireqs: + try: + explicit_candidates.update( + self._iter_candidates_from_constraints( + identifier, + constraint, + template=ireqs[0], + ), + ) + except UnsupportedWheel: + # If we're constrained to install a wheel incompatible with the + # target architecture, no candidates will ever be valid. + return () + + # Since we cache all the candidates, incompatibility identification + # can be made quicker by comparing only the id() values. + incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())} + + # If none of the requirements want an explicit candidate, we can ask + # the finder for candidates. + if not explicit_candidates: + return self._iter_found_candidates( + ireqs, + constraint.specifier, + constraint.hashes, + prefers_installed, + incompat_ids, ) - def make_requirement_from_install_req(self, ireq): - # type: (InstallRequirement) -> Requirement - if ireq.link: - # TODO: Get name and version from ireq, if possible? - # Specifically, this might be needed in "name @ URL" - # syntax - need to check where that syntax is handled. - cand = self._make_candidate_from_link( - ireq.link, extras=set(), parent=ireq, + return ( + c + for c in explicit_candidates + if id(c) not in incompat_ids + and constraint.is_satisfied_by(c) + and all(req.is_satisfied_by(c) for req in requirements[identifier]) + ) + + def _make_requirement_from_install_req( + self, ireq: InstallRequirement, requested_extras: Iterable[str] + ) -> Optional[Requirement]: + if not ireq.match_markers(requested_extras): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + ireq.name, + ireq.markers, ) - return ExplicitRequirement(cand) - return SpecifierRequirement(ireq, factory=self) + return None + if not ireq.link: + return SpecifierRequirement(ireq) + self._fail_if_link_is_unsupported_wheel(ireq.link) + cand = self._make_candidate_from_link( + ireq.link, + extras=frozenset(ireq.extras), + template=ireq, + name=canonicalize_name(ireq.name) if ireq.name else None, + version=None, + ) + if cand is None: + # There's no way we can satisfy a URL requirement if the underlying + # candidate fails to build. An unnamed URL must be user-supplied, so + # we fail eagerly. If the URL is named, an unsatisfiable requirement + # can make the resolver do the right thing, either backtrack (and + # maybe find some other requirement that's buildable) or raise a + # ResolutionImpossible eventually. + if not ireq.name: + raise self._build_failures[ireq.link] + return UnsatisfiableRequirement(canonicalize_name(ireq.name)) + return self.make_requirement_from_candidate(cand) - def make_requirement_from_spec(self, specifier, comes_from): - # type: (str, InstallRequirement) -> Requirement + def collect_root_requirements( + self, root_ireqs: List[InstallRequirement] + ) -> CollectedRootRequirements: + collected = CollectedRootRequirements([], {}, {}) + for i, ireq in enumerate(root_ireqs): + if ireq.constraint: + # Ensure we only accept valid constraints + problem = check_invalid_constraint_type(ireq) + if problem: + raise InstallationError(problem) + if not ireq.match_markers(): + continue + assert ireq.name, "Constraint must be named" + name = canonicalize_name(ireq.name) + if name in collected.constraints: + collected.constraints[name] &= ireq + else: + collected.constraints[name] = Constraint.from_ireq(ireq) + else: + req = self._make_requirement_from_install_req( + ireq, + requested_extras=(), + ) + if req is None: + continue + if ireq.user_supplied and req.name not in collected.user_requested: + collected.user_requested[req.name] = i + collected.requirements.append(req) + return collected + + def make_requirement_from_candidate( + self, candidate: Candidate + ) -> ExplicitRequirement: + return ExplicitRequirement(candidate) + + def make_requirement_from_spec( + self, + specifier: str, + comes_from: Optional[InstallRequirement], + requested_extras: Iterable[str] = (), + ) -> Optional[Requirement]: ireq = self._make_install_req_from_spec(specifier, comes_from) - return self.make_requirement_from_install_req(ireq) + return self._make_requirement_from_install_req(ireq, requested_extras) - def make_requires_python_requirement(self, specifier): - # type: (Optional[SpecifierSet]) -> Optional[Requirement] - if self._ignore_requires_python or specifier is None: + def make_requires_python_requirement( + self, + specifier: SpecifierSet, + ) -> Optional[Requirement]: + if self._ignore_requires_python: + return None + # Don't bother creating a dependency for an empty Requires-Python. + if not str(specifier): return None return RequiresPythonRequirement(specifier, self._python_candidate) - def should_reinstall(self, candidate): - # type: (Candidate) -> bool + def get_wheel_cache_entry( + self, link: Link, name: Optional[str] + ) -> Optional[CacheEntry]: + """Look up the link in the wheel cache. + + If ``preparer.require_hashes`` is True, don't use the wheel cache, + because cached wheels, always built locally, have different hashes + than the files downloaded from the index server and thus throw false + hash mismatches. Furthermore, cached wheels at present have + nondeterministic contents due to file modification times. + """ + if self._wheel_cache is None or self.preparer.require_hashes: + return None + return self._wheel_cache.get_cache_entry( + link=link, + package_name=name, + supported_tags=get_supported(), + ) + + def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]: # TODO: Are there more cases this needs to return True? Editable? - return candidate.name in self._installed_dists + dist = self._installed_dists.get(candidate.project_name) + if dist is None: # Not installed, no uninstallation required. + return None + + # We're installing into global site. The current installation must + # be uninstalled, no matter it's in global or user site, because the + # user site installation has precedence over global. + if not self._use_user_site: + return dist + + # We're installing into user site. Remove the user site installation. + if dist.in_usersite: + return dist + + # We're installing into user site, but the installed incompatible + # package is in global site. We can't uninstall that, and would let + # the new user installation to "shadow" it. But shadowing won't work + # in virtual environments, so we error out. + if running_under_virtualenv() and dist.in_site_packages: + message = ( + f"Will not install to the user site because it will lack " + f"sys.path precedence to {dist.raw_name} in {dist.location}" + ) + raise InstallationError(message) + return None def _report_requires_python_error( + self, causes: Sequence["ConflictCause"] + ) -> UnsupportedPythonVersion: + assert causes, "Requires-Python error reported with no cause" + + version = self._python_candidate.version + + if len(causes) == 1: + specifier = str(causes[0].requirement.specifier) + message = ( + f"Package {causes[0].parent.name!r} requires a different " + f"Python: {version} not in {specifier!r}" + ) + return UnsupportedPythonVersion(message) + + message = f"Packages require a different Python. {version} not in:" + for cause in causes: + package = cause.parent.format_for_error() + specifier = str(cause.requirement.specifier) + message += f"\n{specifier!r} (required by {package})" + return UnsupportedPythonVersion(message) + + def _report_single_requirement_conflict( + self, req: Requirement, parent: Optional[Candidate] + ) -> DistributionNotFound: + if parent is None: + req_disp = str(req) + else: + req_disp = f"{req} (from {parent.name})" + + cands = self._finder.find_all_candidates(req.project_name) + versions = [str(v) for v in sorted({c.version for c in cands})] + + logger.critical( + "Could not find a version that satisfies the requirement %s " + "(from versions: %s)", + req_disp, + ", ".join(versions) or "none", + ) + if str(req) == "requirements.txt": + logger.info( + "HINT: You are attempting to install a package literally " + 'named "requirements.txt" (which cannot exist). Consider ' + "using the '-r' flag to install the packages listed in " + "requirements.txt" + ) + + return DistributionNotFound(f"No matching distribution found for {req}") + + def get_installation_error( self, - requirement, # type: RequiresPythonRequirement - parent, # type: Candidate - ): - # type: (...) -> UnsupportedPythonVersion - template = ( - "Package {package!r} requires a different Python: " - "{version} not in {specifier!r}" + e: "ResolutionImpossible[Requirement, Candidate]", + constraints: Dict[str, Constraint], + ) -> InstallationError: + + assert e.causes, "Installation error reported with no cause" + + # If one of the things we can't solve is "we need Python X.Y", + # that is what we report. + requires_python_causes = [ + cause + for cause in e.causes + if isinstance(cause.requirement, RequiresPythonRequirement) + and not cause.requirement.is_satisfied_by(self._python_candidate) + ] + if requires_python_causes: + # The comprehension above makes sure all Requirement instances are + # RequiresPythonRequirement, so let's cast for convinience. + return self._report_requires_python_error( + cast("Sequence[ConflictCause]", requires_python_causes), + ) + + # Otherwise, we have a set of causes which can't all be satisfied + # at once. + + # The simplest case is when we have *one* cause that can't be + # satisfied. We just report that case. + if len(e.causes) == 1: + req, parent = e.causes[0] + if req.name not in constraints: + return self._report_single_requirement_conflict(req, parent) + + # OK, we now have a list of requirements that can't all be + # satisfied at once. + + # A couple of formatting helpers + def text_join(parts: List[str]) -> str: + if len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def describe_trigger(parent: Candidate) -> str: + ireq = parent.get_install_requirement() + if not ireq or not ireq.comes_from: + return f"{parent.name}=={parent.version}" + if isinstance(ireq.comes_from, InstallRequirement): + return str(ireq.comes_from.name) + return str(ireq.comes_from) + + triggers = set() + for req, parent in e.causes: + if parent is None: + # This is a root requirement, so we can report it directly + trigger = req.format_for_error() + else: + trigger = describe_trigger(parent) + triggers.add(trigger) + + if triggers: + info = text_join(sorted(triggers)) + else: + info = "the requested packages" + + msg = ( + "Cannot install {} because these package versions " + "have conflicting dependencies.".format(info) ) - message = template.format( - package=parent.name, - version=self._python_candidate.version, - specifier=str(requirement.specifier), + logger.critical(msg) + msg = "\nThe conflict is caused by:" + + relevant_constraints = set() + for req, parent in e.causes: + if req.name in constraints: + relevant_constraints.add(req.name) + msg = msg + "\n " + if parent: + msg = msg + f"{parent.name} {parent.version} depends on " + else: + msg = msg + "The user requested " + msg = msg + req.format_for_error() + for key in relevant_constraints: + spec = constraints[key].specifier + msg += f"\n The user requested (constraint) {key}{spec}" + + msg = ( + msg + + "\n\n" + + "To fix this you could try to:\n" + + "1. loosen the range of package versions you've specified\n" + + "2. remove package versions to allow pip attempt to solve " + + "the dependency conflict\n" ) - return UnsupportedPythonVersion(message) - def get_installation_error(self, e): - # type: (ResolutionImpossible) -> Optional[InstallationError] - for cause in e.causes: - if isinstance(cause.requirement, RequiresPythonRequirement): - return self._report_requires_python_error( - cause.requirement, - cause.parent, - ) - return None + logger.info(msg) + + return DistributionNotFound( + "ResolutionImpossible: for help visit " + "https://pip.pypa.io/en/latest/user_guide/" + "#fixing-conflicting-dependencies" + ) diff --git a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py index 5c3d210a31ac9bba868b0271edf7096fcb0083e1..85d3b315605cf57d1410425c69df2eaabfb42846 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -1,54 +1,215 @@ +import collections +import math +from typing import TYPE_CHECKING, Dict, Iterable, Iterator, Mapping, Sequence, Union + from pip._vendor.resolvelib.providers import AbstractProvider -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from .base import Candidate, Constraint, Requirement +from .candidates import REQUIRES_PYTHON_IDENTIFIER +from .factory import Factory + +if TYPE_CHECKING: + from pip._vendor.resolvelib.providers import Preference + from pip._vendor.resolvelib.resolvers import RequirementInformation + + PreferenceInformation = RequirementInformation[Requirement, Candidate] -if MYPY_CHECK_RUNNING: - from typing import Any, Optional, Sequence, Tuple, Union + _ProviderBase = AbstractProvider[Requirement, Candidate, str] +else: + _ProviderBase = AbstractProvider - from pip._internal.req.req_install import InstallRequirement +# Notes on the relationship between the provider, the factory, and the +# candidate and requirement classes. +# +# The provider is a direct implementation of the resolvelib class. Its role +# is to deliver the API that resolvelib expects. +# +# Rather than work with completely abstract "requirement" and "candidate" +# concepts as resolvelib does, pip has concrete classes implementing these two +# ideas. The API of Requirement and Candidate objects are defined in the base +# classes, but essentially map fairly directly to the equivalent provider +# methods. In particular, `find_matches` and `is_satisfied_by` are +# requirement methods, and `get_dependencies` is a candidate method. +# +# The factory is the interface to pip's internal mechanisms. It is stateless, +# and is created by the resolver and held as a property of the provider. It is +# responsible for creating Requirement and Candidate objects, and provides +# services to those objects (access to pip's finder and preparer). - from .base import Requirement, Candidate - from .factory import Factory +class PipProvider(_ProviderBase): + """Pip's provider implementation for resolvelib. + + :params constraints: A mapping of constraints specified by the user. Keys + are canonicalized project names. + :params ignore_dependencies: Whether the user specified ``--no-deps``. + :params upgrade_strategy: The user-specified upgrade strategy. + :params user_requested: A set of canonicalized package names that the user + supplied for pip to install/upgrade. + """ -class PipProvider(AbstractProvider): def __init__( self, - factory, # type: Factory - ignore_dependencies, # type: bool - ): - # type: (...) -> None + factory: Factory, + constraints: Dict[str, Constraint], + ignore_dependencies: bool, + upgrade_strategy: str, + user_requested: Dict[str, int], + ) -> None: self._factory = factory + self._constraints = constraints self._ignore_dependencies = ignore_dependencies + self._upgrade_strategy = upgrade_strategy + self._user_requested = user_requested + self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf) + + def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str: + return requirement_or_candidate.name + + def get_preference( # type: ignore + self, + identifier: str, + resolutions: Mapping[str, Candidate], + candidates: Mapping[str, Iterator[Candidate]], + information: Mapping[str, Iterable["PreferenceInformation"]], + backtrack_causes: Sequence["PreferenceInformation"], + ) -> "Preference": + """Produce a sort key for given requirement based on preference. + + The lower the return value is, the more preferred this group of + arguments is. + + Currently pip considers the followings in order: + + * Prefer if any of the known requirements is "direct", e.g. points to an + explicit URL. + * If equal, prefer if any requirement is "pinned", i.e. contains + operator ``===`` or ``==``. + * If equal, calculate an approximate "depth" and resolve requirements + closer to the user-specified requirements first. + * Order user-specified requirements by the order they are specified. + * If equal, prefers "non-free" requirements, i.e. contains at least one + operator, such as ``>=`` or ``<``. + * If equal, order alphabetically for consistency (helps debuggability). + """ + lookups = (r.get_candidate_lookup() for r, _ in information[identifier]) + candidate, ireqs = zip(*lookups) + operators = [ + specifier.operator + for specifier_set in (ireq.specifier for ireq in ireqs if ireq) + for specifier in specifier_set + ] + + direct = candidate is not None + pinned = any(op[:2] == "==" for op in operators) + unfree = bool(operators) + + try: + requested_order: Union[int, float] = self._user_requested[identifier] + except KeyError: + requested_order = math.inf + parent_depths = ( + self._known_depths[parent.name] if parent is not None else 0.0 + for _, parent in information[identifier] + ) + inferred_depth = min(d for d in parent_depths) + 1.0 + else: + inferred_depth = 1.0 + self._known_depths[identifier] = inferred_depth - def get_install_requirement(self, c): - # type: (Candidate) -> Optional[InstallRequirement] - return c.get_install_requirement() + requested_order = self._user_requested.get(identifier, math.inf) - def identify(self, dependency): - # type: (Union[Requirement, Candidate]) -> str - return dependency.name + # Requires-Python has only one candidate and the check is basically + # free, so we always do it first to avoid needless work if it fails. + requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER - def get_preference( + # HACK: Setuptools have a very long and solid backward compatibility + # track record, and extremely few projects would request a narrow, + # non-recent version range of it since that would break a lot things. + # (Most projects specify it only to request for an installer feature, + # which does not work, but that's another topic.) Intentionally + # delaying Setuptools helps reduce branches the resolver has to check. + # This serves as a temporary fix for issues like "apache-airlfow[all]" + # while we work on "proper" branch pruning techniques. + delay_this = identifier == "setuptools" + + # Prefer the causes of backtracking on the assumption that the problem + # resolving the dependency tree is related to the failures that caused + # the backtracking + backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes) + + return ( + not requires_python, + delay_this, + not direct, + not pinned, + not backtrack_cause, + inferred_depth, + requested_order, + not unfree, + identifier, + ) + + def _get_constraint(self, identifier: str) -> Constraint: + if identifier in self._constraints: + return self._constraints[identifier] + + # HACK: Theoratically we should check whether this identifier is a valid + # "NAME[EXTRAS]" format, and parse out the name part with packaging or + # some regular expression. But since pip's resolver only spits out + # three kinds of identifiers: normalized PEP 503 names, normalized names + # plus extras, and Requires-Python, we can cheat a bit here. + name, open_bracket, _ = identifier.partition("[") + if open_bracket and name in self._constraints: + return self._constraints[name] + + return Constraint.empty() + + def find_matches( self, - resolution, # type: Optional[Candidate] - candidates, # type: Sequence[Candidate] - information # type: Sequence[Tuple[Requirement, Candidate]] - ): - # type: (...) -> Any - # Use the "usual" value for now - return len(candidates) - - def find_matches(self, requirement): - # type: (Requirement) -> Sequence[Candidate] - return requirement.find_matches() - - def is_satisfied_by(self, requirement, candidate): - # type: (Requirement, Candidate) -> bool + identifier: str, + requirements: Mapping[str, Iterator[Requirement]], + incompatibilities: Mapping[str, Iterator[Candidate]], + ) -> Iterable[Candidate]: + def _eligible_for_upgrade(name: str) -> bool: + """Are upgrades allowed for this project? + + This checks the upgrade strategy, and whether the project was one + that the user specified in the command line, in order to decide + whether we should upgrade if there's a newer version available. + + (Note that we don't need access to the `--upgrade` flag, because + an upgrade strategy of "to-satisfy-only" means that `--upgrade` + was not specified). + """ + if self._upgrade_strategy == "eager": + return True + elif self._upgrade_strategy == "only-if-needed": + return name in self._user_requested + return False + + return self._factory.find_candidates( + identifier=identifier, + requirements=requirements, + constraint=self._get_constraint(identifier), + prefers_installed=(not _eligible_for_upgrade(identifier)), + incompatibilities=incompatibilities, + ) + + def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool: return requirement.is_satisfied_by(candidate) - def get_dependencies(self, candidate): - # type: (Candidate) -> Sequence[Requirement] - if self._ignore_dependencies: - return [] - return candidate.get_dependencies() + def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]: + with_requires = not self._ignore_dependencies + return [r for r in candidate.iter_dependencies(with_requires) if r is not None] + + @staticmethod + def is_backtrack_cause( + identifier: str, backtrack_causes: Sequence["PreferenceInformation"] + ) -> bool: + for backtrack_cause in backtrack_causes: + if identifier == backtrack_cause.requirement.name: + return True + if backtrack_cause.parent and identifier == backtrack_cause.parent.name: + return True + return False diff --git a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py index d2e4479b08494d2d2bb703e2d1fdfb5cd786cfc2..c19f83c172c46ed38cdee45031b25fa3f72a846d 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -1,119 +1,166 @@ -from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.req.req_install import InstallRequirement -from .base import Requirement, format_name - -if MYPY_CHECK_RUNNING: - from typing import Sequence - - from pip._vendor.packaging.specifiers import SpecifierSet - - from pip._internal.req.req_install import InstallRequirement - - from .base import Candidate - from .factory import Factory +from .base import Candidate, CandidateLookup, Requirement, format_name class ExplicitRequirement(Requirement): - def __init__(self, candidate): - # type: (Candidate) -> None + def __init__(self, candidate: Candidate) -> None: self.candidate = candidate - def __repr__(self): - # type: () -> str + def __str__(self) -> str: + return str(self.candidate) + + def __repr__(self) -> str: return "{class_name}({candidate!r})".format( class_name=self.__class__.__name__, candidate=self.candidate, ) @property - def name(self): - # type: () -> str + def project_name(self) -> NormalizedName: + # No need to canonicalise - the candidate did this + return self.candidate.project_name + + @property + def name(self) -> str: # No need to canonicalise - the candidate did this return self.candidate.name - def find_matches(self): - # type: () -> Sequence[Candidate] - return [self.candidate] + def format_for_error(self) -> str: + return self.candidate.format_for_error() - def is_satisfied_by(self, candidate): - # type: (Candidate) -> bool + def get_candidate_lookup(self) -> CandidateLookup: + return self.candidate, None + + def is_satisfied_by(self, candidate: Candidate) -> bool: return candidate == self.candidate class SpecifierRequirement(Requirement): - def __init__(self, ireq, factory): - # type: (InstallRequirement, Factory) -> None + def __init__(self, ireq: InstallRequirement) -> None: assert ireq.link is None, "This is a link, not a specifier" self._ireq = ireq - self._factory = factory - self.extras = ireq.req.extras + self._extras = frozenset(ireq.extras) - def __str__(self): - # type: () -> str + def __str__(self) -> str: return str(self._ireq.req) - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "{class_name}({requirement!r})".format( class_name=self.__class__.__name__, requirement=str(self._ireq.req), ) @property - def name(self): - # type: () -> str - canonical_name = canonicalize_name(self._ireq.req.name) - return format_name(canonical_name, self.extras) - - def find_matches(self): - # type: () -> Sequence[Candidate] - it = self._factory.iter_found_candidates(self._ireq, self.extras) - return list(it) - - def is_satisfied_by(self, candidate): - # type: (Candidate) -> bool - assert candidate.name == self.name, \ - "Internal issue: Candidate is not for this requirement " \ - " {} vs {}".format(candidate.name, self.name) + def project_name(self) -> NormalizedName: + assert self._ireq.req, "Specifier-backed ireq is always PEP 508" + return canonicalize_name(self._ireq.req.name) + + @property + def name(self) -> str: + return format_name(self.project_name, self._extras) + + def format_for_error(self) -> str: + + # Convert comma-separated specifiers into "A, B, ..., F and G" + # This makes the specifier a bit more "human readable", without + # risking a change in meaning. (Hopefully! Not all edge cases have + # been checked) + parts = [s.strip() for s in str(self).split(",")] + if len(parts) == 0: + return "" + elif len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def get_candidate_lookup(self) -> CandidateLookup: + return None, self._ireq + + def is_satisfied_by(self, candidate: Candidate) -> bool: + assert candidate.name == self.name, ( + f"Internal issue: Candidate is not for this requirement " + f"{candidate.name} vs {self.name}" + ) # We can safely always allow prereleases here since PackageFinder # already implements the prerelease logic, and would have filtered out # prerelease candidates if the user does not expect them. + assert self._ireq.req, "Specifier-backed ireq is always PEP 508" spec = self._ireq.req.specifier return spec.contains(candidate.version, prereleases=True) class RequiresPythonRequirement(Requirement): - """A requirement representing Requires-Python metadata. - """ - def __init__(self, specifier, match): - # type: (SpecifierSet, Candidate) -> None + """A requirement representing Requires-Python metadata.""" + + def __init__(self, specifier: SpecifierSet, match: Candidate) -> None: self.specifier = specifier self._candidate = match - def __repr__(self): - # type: () -> str + def __str__(self) -> str: + return f"Python {self.specifier}" + + def __repr__(self) -> str: return "{class_name}({specifier!r})".format( class_name=self.__class__.__name__, specifier=str(self.specifier), ) @property - def name(self): - # type: () -> str + def project_name(self) -> NormalizedName: + return self._candidate.project_name + + @property + def name(self) -> str: return self._candidate.name - def find_matches(self): - # type: () -> Sequence[Candidate] - if self._candidate.version in self.specifier: - return [self._candidate] - return [] + def format_for_error(self) -> str: + return str(self) - def is_satisfied_by(self, candidate): - # type: (Candidate) -> bool + def get_candidate_lookup(self) -> CandidateLookup: + if self.specifier.contains(self._candidate.version, prereleases=True): + return self._candidate, None + return None, None + + def is_satisfied_by(self, candidate: Candidate) -> bool: assert candidate.name == self._candidate.name, "Not Python candidate" # We can safely always allow prereleases here since PackageFinder # already implements the prerelease logic, and would have filtered out # prerelease candidates if the user does not expect them. return self.specifier.contains(candidate.version, prereleases=True) + + +class UnsatisfiableRequirement(Requirement): + """A requirement that cannot be satisfied.""" + + def __init__(self, name: NormalizedName) -> None: + self._name = name + + def __str__(self) -> str: + return f"{self._name} (unavailable)" + + def __repr__(self) -> str: + return "{class_name}({name!r})".format( + class_name=self.__class__.__name__, + name=str(self._name), + ) + + @property + def project_name(self) -> NormalizedName: + return self._name + + @property + def name(self) -> str: + return self._name + + def format_for_error(self) -> str: + return str(self) + + def get_candidate_lookup(self) -> CandidateLookup: + return None, None + + def is_satisfied_by(self, candidate: Candidate) -> bool: + return False diff --git a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py index cba5a496508d414f954d36d320d89c33b1300e1e..12f967020241ba1ad0c1e00326df3412dfa28c07 100644 --- a/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py +++ b/venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -1,155 +1,187 @@ import functools import logging +import os +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast -from pip._vendor import six from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible from pip._vendor.resolvelib import Resolver as RLResolver +from pip._vendor.resolvelib.structs import DirectedGraph -from pip._internal.exceptions import InstallationError +from pip._internal.cache import WheelCache +from pip._internal.index.package_finder import PackageFinder +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_set import RequirementSet -from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider from pip._internal.resolution.resolvelib.provider import PipProvider -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.resolution.resolvelib.reporter import ( + PipDebuggingReporter, + PipReporter, +) +from .base import Candidate, Requirement from .factory import Factory -if MYPY_CHECK_RUNNING: - from typing import Dict, List, Optional, Tuple +if TYPE_CHECKING: + from pip._vendor.resolvelib.resolvers import Result as RLResult - from pip._vendor.resolvelib.resolvers import Result - - from pip._internal.cache import WheelCache - from pip._internal.index.package_finder import PackageFinder - from pip._internal.operations.prepare import RequirementPreparer - from pip._internal.req.req_install import InstallRequirement - from pip._internal.resolution.base import InstallRequirementProvider + Result = RLResult[Requirement, Candidate, str] logger = logging.getLogger(__name__) class Resolver(BaseResolver): + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + def __init__( self, - preparer, # type: RequirementPreparer - finder, # type: PackageFinder - wheel_cache, # type: Optional[WheelCache] - make_install_req, # type: InstallRequirementProvider - use_user_site, # type: bool - ignore_dependencies, # type: bool - ignore_installed, # type: bool - ignore_requires_python, # type: bool - force_reinstall, # type: bool - upgrade_strategy, # type: str - py_version_info=None, # type: Optional[Tuple[int, ...]] + preparer: RequirementPreparer, + finder: PackageFinder, + wheel_cache: Optional[WheelCache], + make_install_req: InstallRequirementProvider, + use_user_site: bool, + ignore_dependencies: bool, + ignore_installed: bool, + ignore_requires_python: bool, + force_reinstall: bool, + upgrade_strategy: str, + py_version_info: Optional[Tuple[int, ...]] = None, ): - super(Resolver, self).__init__() + super().__init__() + assert upgrade_strategy in self._allowed_strategies + self.factory = Factory( finder=finder, preparer=preparer, make_install_req=make_install_req, + wheel_cache=wheel_cache, + use_user_site=use_user_site, force_reinstall=force_reinstall, ignore_installed=ignore_installed, ignore_requires_python=ignore_requires_python, py_version_info=py_version_info, ) self.ignore_dependencies = ignore_dependencies - self._result = None # type: Optional[Result] - - def resolve(self, root_reqs, check_supported_wheels): - # type: (List[InstallRequirement], bool) -> RequirementSet - - # FIXME: Implement constraints. - if any(r.constraint for r in root_reqs): - raise InstallationError("Constraints are not yet supported.") + self.upgrade_strategy = upgrade_strategy + self._result: Optional[Result] = None + def resolve( + self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + ) -> RequirementSet: + collected = self.factory.collect_root_requirements(root_reqs) provider = PipProvider( factory=self.factory, + constraints=collected.constraints, ignore_dependencies=self.ignore_dependencies, + upgrade_strategy=self.upgrade_strategy, + user_requested=collected.user_requested, + ) + if "PIP_RESOLVER_DEBUG" in os.environ: + reporter: BaseReporter = PipDebuggingReporter() + else: + reporter = PipReporter() + resolver: RLResolver[Requirement, Candidate, str] = RLResolver( + provider, + reporter, ) - reporter = BaseReporter() - resolver = RLResolver(provider, reporter) - - requirements = [ - self.factory.make_requirement_from_install_req(r) - for r in root_reqs - ] try: - self._result = resolver.resolve(requirements) + try_to_avoid_resolution_too_deep = 2000000 + result = self._result = resolver.resolve( + collected.requirements, max_rounds=try_to_avoid_resolution_too_deep + ) except ResolutionImpossible as e: - error = self.factory.get_installation_error(e) - if not error: - # TODO: This needs fixing, we need to look at the - # factory.get_installation_error infrastructure, as that - # doesn't really allow for the logger.critical calls I'm - # using here. - for req, parent in e.causes: - logger.critical( - "Could not find a version that satisfies " + - "the requirement " + - str(req) + - ("" if parent is None else " (from {})".format( - parent.name - )) - ) - raise InstallationError( - "No matching distribution found for " + - ", ".join([r.name for r, _ in e.causes]) - ) - raise - six.raise_from(error, e) + error = self.factory.get_installation_error( + cast("ResolutionImpossible[Requirement, Candidate]", e), + collected.constraints, + ) + raise error from e req_set = RequirementSet(check_supported_wheels=check_supported_wheels) - for candidate in self._result.mapping.values(): - ireq = provider.get_install_requirement(candidate) + for candidate in result.mapping.values(): + ireq = candidate.get_install_requirement() if ireq is None: continue - ireq.should_reinstall = self.factory.should_reinstall(candidate) + + # Check if there is already an installation under the same name, + # and set a flag for later stages to uninstall it, if needed. + installed_dist = self.factory.get_dist_to_uninstall(candidate) + if installed_dist is None: + # There is no existing installation -- nothing to uninstall. + ireq.should_reinstall = False + elif self.factory.force_reinstall: + # The --force-reinstall flag is set -- reinstall. + ireq.should_reinstall = True + elif installed_dist.version != candidate.version: + # The installation is different in version -- reinstall. + ireq.should_reinstall = True + elif candidate.is_editable or installed_dist.editable: + # The incoming distribution is editable, or different in + # editable-ness to installation -- reinstall. + ireq.should_reinstall = True + elif candidate.source_link and candidate.source_link.is_file: + # The incoming distribution is under file:// + if candidate.source_link.is_wheel: + # is a local wheel -- do nothing. + logger.info( + "%s is already installed with the same version as the " + "provided wheel. Use --force-reinstall to force an " + "installation of the wheel.", + ireq.name, + ) + continue + + # is a local sdist or path -- reinstall + ireq.should_reinstall = True + else: + continue + + link = candidate.source_link + if link and link.is_yanked: + # The reason can contain non-ASCII characters, Unicode + # is required for Python 2. + msg = ( + "The candidate selected for download or install is a " + "yanked version: {name!r} candidate (version {version} " + "at {link})\nReason for being yanked: {reason}" + ).format( + name=candidate.name, + version=candidate.version, + link=link, + reason=link.yanked_reason or "", + ) + logger.warning(msg) + req_set.add_named_requirement(ireq) + reqs = req_set.all_requirements + self.factory.preparer.prepare_linked_requirements_more(reqs) return req_set - def get_installation_order(self, req_set): - # type: (RequirementSet) -> List[InstallRequirement] - """Create a list that orders given requirements for installation. + def get_installation_order( + self, req_set: RequirementSet + ) -> List[InstallRequirement]: + """Get order for installation of requirements in RequirementSet. - The returned list should contain all requirements in ``req_set``, - so the caller can loop through it and have a requirement installed - before the requiring thing. + The returned list contains a requirement before another that depends on + it. This helps ensure that the environment is kept consistent as they + get installed one-by-one. - The current implementation walks the resolved dependency graph, and - make sure every node has a greater "weight" than all its parents. + The current implementation creates a topological ordering of the + dependency graph, while breaking any cycles in the graph at arbitrary + points. We make no guarantees about where the cycle would be broken, + other than they would be broken. """ assert self._result is not None, "must call resolve() first" - weights = {} # type: Dict[Optional[str], int] graph = self._result.graph - key_count = len(self._result.mapping) + 1 # Packages plus sentinal. - while len(weights) < key_count: - progressed = False - for key in graph: - if key in weights: - continue - parents = list(graph.iter_parents(key)) - if not all(p in weights for p in parents): - continue - if parents: - weight = max(weights[p] for p in parents) + 1 - else: - weight = 0 - weights[key] = weight - progressed = True - - # FIXME: This check will fail if there are unbreakable cycles. - # Implement something to forcifully break them up to continue. - if not progressed: - raise InstallationError( - "Could not determine installation order due to cicular " - "dependency." - ) + weights = get_topological_weights( + graph, + expected_node_count=len(self._result.mapping) + 1, + ) sorted_items = sorted( req_set.requirements.items(), @@ -159,11 +191,56 @@ class Resolver(BaseResolver): return [ireq for _, ireq in sorted_items] +def get_topological_weights( + graph: "DirectedGraph[Optional[str]]", expected_node_count: int +) -> Dict[Optional[str], int]: + """Assign weights to each node based on how "deep" they are. + + This implementation may change at any point in the future without prior + notice. + + We take the length for the longest path to any node from root, ignoring any + paths that contain a single node twice (i.e. cycles). This is done through + a depth-first search through the graph, while keeping track of the path to + the node. + + Cycles in the graph result would result in node being revisited while also + being it's own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. + """ + path: Set[Optional[str]] = set() + weights: Dict[Optional[str], int] = {} + + def visit(node: Optional[str]) -> None: + if node in path: + # We hit a cycle, so we'll break it here. + return + + # Time to visit the children! + path.add(node) + for child in graph.iter_children(node): + visit(child) + path.remove(node) + + last_known_parent_count = weights.get(node, 0) + weights[node] = max(last_known_parent_count, len(path)) + + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + + # Sanity checks + assert weights[None] == 0 + assert len(weights) == expected_node_count + + return weights + + def _req_set_item_sorter( - item, # type: Tuple[str, InstallRequirement] - weights, # type: Dict[Optional[str], int] -): - # type: (...) -> Tuple[int, str] + item: Tuple[str, InstallRequirement], + weights: Dict[Optional[str], int], +) -> Tuple[int, str]: """Key function used to sort install requirements for installation. Based on the "weight" mapping calculated in ``get_installation_order()``. diff --git a/venv/Lib/site-packages/pip/_internal/self_outdated_check.py b/venv/Lib/site-packages/pip/_internal/self_outdated_check.py index 8fc3c594acf96eb8dee7e69c9d835e16cd45cec3..72f70fc2597d4f841958962606e141ba0066c12a 100644 --- a/venv/Lib/site-packages/pip/_internal/self_outdated_check.py +++ b/venv/Lib/site-packages/pip/_internal/self_outdated_check.py @@ -1,43 +1,21 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import datetime import hashlib import json import logging +import optparse import os.path import sys +from typing import Any, Dict -from pip._vendor import pkg_resources -from pip._vendor.packaging import version as packaging_version -from pip._vendor.six import ensure_binary +from pip._vendor.packaging.version import parse as parse_version from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder -from pip._internal.models.search_scope import SearchScope +from pip._internal.metadata import get_default_environment from pip._internal.models.selection_prefs import SelectionPreferences -from pip._internal.utils.filesystem import ( - adjacent_tmp_file, - check_path_owner, - replace, -) -from pip._internal.utils.misc import ( - ensure_dir, - get_installed_version, - redact_auth_from_url, -) -from pip._internal.utils.packaging import get_installer -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - import optparse - from optparse import Values - from typing import Any, Dict, Text, Union - - from pip._internal.network.session import PipSession - +from pip._internal.network.session import PipSession +from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace +from pip._internal.utils.misc import ensure_dir SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" @@ -45,48 +23,15 @@ SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" logger = logging.getLogger(__name__) -def make_link_collector( - session, # type: PipSession - options, # type: Values - suppress_no_index=False, # type: bool -): - # type: (...) -> LinkCollector - """ - :param session: The Session to use to make requests. - :param suppress_no_index: Whether to ignore the --no-index option - when constructing the SearchScope object. - """ - index_urls = [options.index_url] + options.extra_index_urls - if options.no_index and not suppress_no_index: - logger.debug( - 'Ignoring indexes: %s', - ','.join(redact_auth_from_url(url) for url in index_urls), - ) - index_urls = [] - - # Make sure find_links is a list before passing to create(). - find_links = options.find_links or [] - - search_scope = SearchScope.create( - find_links=find_links, index_urls=index_urls, - ) - - link_collector = LinkCollector(session=session, search_scope=search_scope) - - return link_collector - - -def _get_statefile_name(key): - # type: (Union[str, Text]) -> str - key_bytes = ensure_binary(key) +def _get_statefile_name(key: str) -> str: + key_bytes = key.encode() name = hashlib.sha224(key_bytes).hexdigest() return name -class SelfCheckState(object): - def __init__(self, cache_dir): - # type: (str) -> None - self.state = {} # type: Dict[str, Any] +class SelfCheckState: + def __init__(self, cache_dir: str) -> None: + self.state: Dict[str, Any] = {} self.statefile_path = None # Try to load the existing state @@ -95,19 +40,18 @@ class SelfCheckState(object): cache_dir, "selfcheck", _get_statefile_name(self.key) ) try: - with open(self.statefile_path) as statefile: + with open(self.statefile_path, encoding="utf-8") as statefile: self.state = json.load(statefile) - except (IOError, ValueError, KeyError): + except (OSError, ValueError, KeyError): # Explicitly suppressing exceptions, since we don't want to # error out if the cache file is invalid. pass @property - def key(self): + def key(self) -> str: return sys.prefix - def save(self, pypi_version, current_time): - # type: (str, datetime.datetime) -> None + def save(self, pypi_version: str, current_time: datetime.datetime) -> None: # If we do not have a path to cache in, don't bother saving. if not self.statefile_path: return @@ -131,7 +75,7 @@ class SelfCheckState(object): text = json.dumps(state, sort_keys=True, separators=(",", ":")) with adjacent_tmp_file(self.statefile_path) as f: - f.write(ensure_binary(text)) + f.write(text.encode()) try: # Since we have a prefix-specific state file, we can just @@ -142,33 +86,28 @@ class SelfCheckState(object): pass -def was_installed_by_pip(pkg): - # type: (str) -> bool +def was_installed_by_pip(pkg: str) -> bool: """Checks whether pkg was installed by pip This is used not to display the upgrade message when pip is in fact installed by system package manager, such as dnf on Fedora. """ - try: - dist = pkg_resources.get_distribution(pkg) - return "pip" == get_installer(dist) - except pkg_resources.DistributionNotFound: - return False + dist = get_default_environment().get_distribution(pkg) + return dist is not None and "pip" == dist.installer -def pip_self_version_check(session, options): - # type: (PipSession, optparse.Values) -> None +def pip_self_version_check(session: PipSession, options: optparse.Values) -> None: """Check for an update for pip. Limit the frequency of checks to once per week. State is stored either in the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix of the pip script path. """ - installed_version = get_installed_version("pip") - if not installed_version: + installed_dist = get_default_environment().get_distribution("pip") + if not installed_dist: return - pip_version = packaging_version.parse(installed_version) + pip_version = installed_dist.version pypi_version = None try: @@ -178,8 +117,7 @@ def pip_self_version_check(session, options): # Determine if we need to refresh the state if "last_check" in state.state and "pypi_version" in state.state: last_check = datetime.datetime.strptime( - state.state["last_check"], - SELFCHECK_DATE_FMT + state.state["last_check"], SELFCHECK_DATE_FMT ) if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: pypi_version = state.state["pypi_version"] @@ -187,7 +125,7 @@ def pip_self_version_check(session, options): # Refresh the version if we need to or just see if we need to warn if pypi_version is None: # Lets use PackageFinder to see what the latest pip version is - link_collector = make_link_collector( + link_collector = LinkCollector.create( session, options=options, suppress_no_index=True, @@ -212,12 +150,12 @@ def pip_self_version_check(session, options): # save that we've performed a check state.save(pypi_version, current_time) - remote_version = packaging_version.parse(pypi_version) + remote_version = parse_version(pypi_version) local_version_is_older = ( - pip_version < remote_version and - pip_version.base_version != remote_version.base_version and - was_installed_by_pip('pip') + pip_version < remote_version + and pip_version.base_version != remote_version.base_version + and was_installed_by_pip("pip") ) # Determine if our pypi_version is older @@ -228,12 +166,14 @@ def pip_self_version_check(session, options): # command context, so be pragmatic here and suggest the command # that's always available. This does not accommodate spaces in # `sys.executable`. - pip_cmd = "{} -m pip".format(sys.executable) + pip_cmd = f"{sys.executable} -m pip" logger.warning( "You are using pip version %s; however, version %s is " "available.\nYou should consider upgrading via the " "'%s install --upgrade pip' command.", - pip_version, pypi_version, pip_cmd + pip_version, + pypi_version, + pip_cmd, ) except Exception: logger.debug( diff --git a/venv/Lib/site-packages/pip/_internal/utils/appdirs.py b/venv/Lib/site-packages/pip/_internal/utils/appdirs.py index 3989ed31c3a40a31fb2e24b6dd007e226f6393c3..16933bf8afedcbe3e9d4fcc04e5f7246228c56fc 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/appdirs.py +++ b/venv/Lib/site-packages/pip/_internal/utils/appdirs.py @@ -6,39 +6,47 @@ The intention is to rewrite current usages gradually, keeping the tests pass, and eventually drop this after all usages are changed. """ -from __future__ import absolute_import - import os +import sys +from typing import List + +from pip._vendor import platformdirs as _appdirs -from pip._vendor import appdirs as _appdirs -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +def user_cache_dir(appname: str) -> str: + return _appdirs.user_cache_dir(appname, appauthor=False) -if MYPY_CHECK_RUNNING: - from typing import List +def _macos_user_config_dir(appname: str, roaming: bool = True) -> str: + # Use ~/Application Support/pip, if the directory exists. + path = _appdirs.user_data_dir(appname, appauthor=False, roaming=roaming) + if os.path.isdir(path): + return path -def user_cache_dir(appname): - # type: (str) -> str - return _appdirs.user_cache_dir(appname, appauthor=False) + # Use a Linux-like ~/.config/pip, by default. + linux_like_path = "~/.config/" + if appname: + linux_like_path = os.path.join(linux_like_path, appname) + return os.path.expanduser(linux_like_path) -def user_config_dir(appname, roaming=True): - # type: (str, bool) -> str - path = _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) - if _appdirs.system == "darwin" and not os.path.isdir(path): - path = os.path.expanduser('~/.config/') - if appname: - path = os.path.join(path, appname) - return path + +def user_config_dir(appname: str, roaming: bool = True) -> str: + if sys.platform == "darwin": + return _macos_user_config_dir(appname, roaming) + + return _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) # for the discussion regarding site_config_dir locations # see -def site_config_dirs(appname): - # type: (str) -> List[str] +def site_config_dirs(appname: str) -> List[str]: + if sys.platform == "darwin": + return [_appdirs.site_data_dir(appname, appauthor=False, multipath=True)] + dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True) - if _appdirs.system not in ["win32", "darwin"]: - # always look in /etc directly as well - return dirval.split(os.pathsep) + ['/etc'] - return [dirval] + if sys.platform == "win32": + return [dirval] + + # Unix-y system. Look in /etc as well. + return dirval.split(os.pathsep) + ["/etc"] diff --git a/venv/Lib/site-packages/pip/_internal/utils/compat.py b/venv/Lib/site-packages/pip/_internal/utils/compat.py index d939e21fe2a86ebf5675b599e140ccf2254b5014..3f4d300cef077e698989245562375a9444d983fa 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/compat.py +++ b/venv/Lib/site-packages/pip/_internal/utils/compat.py @@ -1,166 +1,30 @@ """Stuff that differs in different Python versions and platform distributions.""" -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import, division - -import codecs -import locale import logging import os -import shutil import sys -from pip._vendor.six import PY2, text_type - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Optional, Text, Tuple, Union - -try: - import ipaddress -except ImportError: - try: - from pip._vendor import ipaddress # type: ignore - except ImportError: - import ipaddr as ipaddress # type: ignore - ipaddress.ip_address = ipaddress.IPAddress # type: ignore - ipaddress.ip_network = ipaddress.IPNetwork # type: ignore - - -__all__ = [ - "ipaddress", "uses_pycache", "console_to_str", - "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", -] +__all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"] logger = logging.getLogger(__name__) -if PY2: - import imp - - try: - cache_from_source = imp.cache_from_source # type: ignore - except AttributeError: - # does not use __pycache__ - cache_from_source = None - - uses_pycache = cache_from_source is not None -else: - uses_pycache = True - from importlib.util import cache_from_source - - -if PY2: - # In Python 2.7, backslashreplace exists - # but does not support use for decoding. - # We implement our own replace handler for this - # situation, so that we can consistently use - # backslash replacement for all versions. - def backslashreplace_decode_fn(err): - raw_bytes = (err.object[i] for i in range(err.start, err.end)) - # Python 2 gave us characters - convert to numeric bytes - raw_bytes = (ord(b) for b in raw_bytes) - return u"".join(map(u"\\x{:x}".format, raw_bytes)), err.end - codecs.register_error( - "backslashreplace_decode", - backslashreplace_decode_fn, - ) - backslashreplace_decode = "backslashreplace_decode" -else: - backslashreplace_decode = "backslashreplace" - -def has_tls(): - # type: () -> bool +def has_tls() -> bool: try: import _ssl # noqa: F401 # ignore unused + return True except ImportError: pass from pip._vendor.urllib3.util import IS_PYOPENSSL - return IS_PYOPENSSL - - -def str_to_display(data, desc=None): - # type: (Union[bytes, Text], Optional[str]) -> Text - """ - For display or logging purposes, convert a bytes object (or text) to - text (e.g. unicode in Python 2) safe for output. - - :param desc: An optional phrase describing the input data, for use in - the log message if a warning is logged. Defaults to "Bytes object". - - This function should never error out and so can take a best effort - approach. It is okay to be lossy if needed since the return value is - just for display. - We assume the data is in the locale preferred encoding. If it won't - decode properly, we warn the user but decode as best we can. - - We also ensure that the output can be safely written to standard output - without encoding errors. - """ - if isinstance(data, text_type): - return data - - # Otherwise, data is a bytes object (str in Python 2). - # First, get the encoding we assume. This is the preferred - # encoding for the locale, unless that is not found, or - # it is ASCII, in which case assume UTF-8 - encoding = locale.getpreferredencoding() - if (not encoding) or codecs.lookup(encoding).name == "ascii": - encoding = "utf-8" - - # Now try to decode the data - if we fail, warn the user and - # decode with replacement. - try: - decoded_data = data.decode(encoding) - except UnicodeDecodeError: - if desc is None: - desc = 'Bytes object' - msg_format = '{} does not appear to be encoded as %s'.format(desc) - logger.warning(msg_format, encoding) - decoded_data = data.decode(encoding, errors=backslashreplace_decode) - - # Make sure we can print the output, by encoding it to the output - # encoding with replacement of unencodable characters, and then - # decoding again. - # We use stderr's encoding because it's less likely to be - # redirected and if we don't find an encoding we skip this - # step (on the assumption that output is wrapped by something - # that won't fail). - # The double getattr is to deal with the possibility that we're - # being called in a situation where sys.__stderr__ doesn't exist, - # or doesn't have an encoding attribute. Neither of these cases - # should occur in normal pip use, but there's no harm in checking - # in case people use pip in (unsupported) unusual situations. - output_encoding = getattr(getattr(sys, "__stderr__", None), - "encoding", None) - - if output_encoding: - output_encoded = decoded_data.encode( - output_encoding, - errors="backslashreplace" - ) - decoded_data = output_encoded.decode(output_encoding) - - return decoded_data - - -def console_to_str(data): - # type: (bytes) -> Text - """Return a string, safe for output, of subprocess output. - """ - return str_to_display(data, desc='Subprocess output') + return IS_PYOPENSSL -def get_path_uid(path): - # type: (str) -> int +def get_path_uid(path: str) -> int: """ Return path's uid. @@ -172,7 +36,7 @@ def get_path_uid(path): :raises OSError: When path is a symlink or can't be read. """ - if hasattr(os, 'O_NOFOLLOW'): + if hasattr(os, "O_NOFOLLOW"): fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) file_uid = os.fstat(fd).st_uid os.close(fd) @@ -183,26 +47,10 @@ def get_path_uid(path): file_uid = os.stat(path).st_uid else: # raise OSError for parity with os.O_NOFOLLOW above - raise OSError( - "{} is a symlink; Will not return uid for symlinks".format( - path) - ) + raise OSError(f"{path} is a symlink; Will not return uid for symlinks") return file_uid -def expanduser(path): - # type: (str) -> str - """ - Expand ~ and ~user constructions. - - Includes a workaround for https://bugs.python.org/issue14768 - """ - expanded = os.path.expanduser(path) - if path.startswith('~/') and expanded.startswith('//'): - expanded = expanded[1:] - return expanded - - # packages in the stdlib that may have installation metadata, but should not be # considered 'installed'. this theoretically could be determined based on # dist.location (py27:`sysconfig.get_paths()['stdlib']`, @@ -212,59 +60,4 @@ stdlib_pkgs = {"python", "wsgiref", "argparse"} # windows detection, covers cpython and ironpython -WINDOWS = (sys.platform.startswith("win") or - (sys.platform == 'cli' and os.name == 'nt')) - - -def samefile(file1, file2): - # type: (str, str) -> bool - """Provide an alternative for os.path.samefile on Windows/Python2""" - if hasattr(os.path, 'samefile'): - return os.path.samefile(file1, file2) - else: - path1 = os.path.normcase(os.path.abspath(file1)) - path2 = os.path.normcase(os.path.abspath(file2)) - return path1 == path2 - - -if hasattr(shutil, 'get_terminal_size'): - def get_terminal_size(): - # type: () -> Tuple[int, int] - """ - Returns a tuple (x, y) representing the width(x) and the height(y) - in characters of the terminal window. - """ - return tuple(shutil.get_terminal_size()) # type: ignore -else: - def get_terminal_size(): - # type: () -> Tuple[int, int] - """ - Returns a tuple (x, y) representing the width(x) and the height(y) - in characters of the terminal window. - """ - def ioctl_GWINSZ(fd): - try: - import fcntl - import termios - import struct - cr = struct.unpack_from( - 'hh', - fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') - ) - except Exception: - return None - if cr == (0, 0): - return None - return cr - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - if not cr: - if sys.platform != "win32": - try: - fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) - os.close(fd) - except Exception: - pass - if not cr: - cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) - return int(cr[1]), int(cr[0]) +WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") diff --git a/venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py b/venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py index 47d04f078c13a6cdb8282ebc789cad5a5d14637f..b6ed9a78e552806cb23d8ac48ada6d41db5b4de5 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py +++ b/venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py @@ -1,12 +1,11 @@ """Generate and work with PEP 425 Compatibility Tags. """ -from __future__ import absolute_import - -import logging import re +from typing import List, Optional, Tuple from pip._vendor.packaging.tags import ( + PythonVersion, Tag, compatible_tags, cpython_tags, @@ -16,26 +15,15 @@ from pip._vendor.packaging.tags import ( mac_platforms, ) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Tuple - - from pip._vendor.packaging.tags import PythonVersion +_osx_arch_pat = re.compile(r"(.+)_(\d+)_(\d+)_(.+)") -logger = logging.getLogger(__name__) -_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') - - -def version_info_to_nodot(version_info): - # type: (Tuple[int, ...]) -> str +def version_info_to_nodot(version_info: Tuple[int, ...]) -> str: # Only use up to the first two numbers. - return ''.join(map(str, version_info[:2])) + return "".join(map(str, version_info[:2])) -def _mac_platforms(arch): - # type: (str) -> List[str] +def _mac_platforms(arch: str) -> List[str]: match = _osx_arch_pat.match(arch) if match: name, major, minor, actual_arch = match.groups() @@ -46,7 +34,7 @@ def _mac_platforms(arch): # actual prefix provided by the user in case they provided # something like "macosxcustom_". It may be good to remove # this as undocumented or deprecate it in the future. - '{}_{}'.format(name, arch[len('macosx_'):]) + "{}_{}".format(name, arch[len("macosx_") :]) for arch in mac_platforms(mac_version, actual_arch) ] else: @@ -55,91 +43,99 @@ def _mac_platforms(arch): return arches -def _custom_manylinux_platforms(arch): - # type: (str) -> List[str] +def _custom_manylinux_platforms(arch: str) -> List[str]: arches = [arch] - arch_prefix, arch_sep, arch_suffix = arch.partition('_') - if arch_prefix == 'manylinux2014': + arch_prefix, arch_sep, arch_suffix = arch.partition("_") + if arch_prefix == "manylinux2014": # manylinux1/manylinux2010 wheels run on most manylinux2014 systems # with the exception of wheels depending on ncurses. PEP 599 states # manylinux1/manylinux2010 wheels should be considered # manylinux2014 wheels: # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels - if arch_suffix in {'i686', 'x86_64'}: - arches.append('manylinux2010' + arch_sep + arch_suffix) - arches.append('manylinux1' + arch_sep + arch_suffix) - elif arch_prefix == 'manylinux2010': + if arch_suffix in {"i686", "x86_64"}: + arches.append("manylinux2010" + arch_sep + arch_suffix) + arches.append("manylinux1" + arch_sep + arch_suffix) + elif arch_prefix == "manylinux2010": # manylinux1 wheels run on most manylinux2010 systems with the # exception of wheels depending on ncurses. PEP 571 states # manylinux1 wheels should be considered manylinux2010 wheels: # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels - arches.append('manylinux1' + arch_sep + arch_suffix) + arches.append("manylinux1" + arch_sep + arch_suffix) return arches -def _get_custom_platforms(arch): - # type: (str) -> List[str] - arch_prefix, arch_sep, arch_suffix = arch.partition('_') - if arch.startswith('macosx'): +def _get_custom_platforms(arch: str) -> List[str]: + arch_prefix, arch_sep, arch_suffix = arch.partition("_") + if arch.startswith("macosx"): arches = _mac_platforms(arch) - elif arch_prefix in ['manylinux2014', 'manylinux2010']: + elif arch_prefix in ["manylinux2014", "manylinux2010"]: arches = _custom_manylinux_platforms(arch) else: arches = [arch] return arches -def _get_python_version(version): - # type: (str) -> PythonVersion +def _expand_allowed_platforms(platforms: Optional[List[str]]) -> Optional[List[str]]: + if not platforms: + return None + + seen = set() + result = [] + + for p in platforms: + if p in seen: + continue + additions = [c for c in _get_custom_platforms(p) if c not in seen] + seen.update(additions) + result.extend(additions) + + return result + + +def _get_python_version(version: str) -> PythonVersion: if len(version) > 1: return int(version[0]), int(version[1:]) else: return (int(version[0]),) -def _get_custom_interpreter(implementation=None, version=None): - # type: (Optional[str], Optional[str]) -> str +def _get_custom_interpreter( + implementation: Optional[str] = None, version: Optional[str] = None +) -> str: if implementation is None: implementation = interpreter_name() if version is None: version = interpreter_version() - return "{}{}".format(implementation, version) + return f"{implementation}{version}" def get_supported( - version=None, # type: Optional[str] - platform=None, # type: Optional[str] - impl=None, # type: Optional[str] - abi=None # type: Optional[str] -): - # type: (...) -> List[Tag] + version: Optional[str] = None, + platforms: Optional[List[str]] = None, + impl: Optional[str] = None, + abis: Optional[List[str]] = None, +) -> List[Tag]: """Return a list of supported tags for each version specified in `versions`. :param version: a string version, of the form "33" or "32", or None. The version will be assumed to support our ABI. - :param platform: specify the exact platform you want valid + :param platform: specify a list of platforms you want valid tags for, or None. If None, use the local system platform. :param impl: specify the exact implementation you want valid tags for, or None. If None, use the local interpreter impl. - :param abi: specify the exact abi you want valid + :param abis: specify a list of abis you want valid tags for, or None. If None, use the local interpreter abi. """ - supported = [] # type: List[Tag] + supported: List[Tag] = [] - python_version = None # type: Optional[PythonVersion] + python_version: Optional[PythonVersion] = None if version is not None: python_version = _get_python_version(version) interpreter = _get_custom_interpreter(impl, version) - abis = None # type: Optional[List[str]] - if abi is not None: - abis = [abi] - - platforms = None # type: Optional[List[str]] - if platform is not None: - platforms = _get_custom_platforms(platform) + platforms = _expand_allowed_platforms(platforms) is_cpython = (impl or interpreter_name()) == "cp" if is_cpython: diff --git a/venv/Lib/site-packages/pip/_internal/utils/deprecation.py b/venv/Lib/site-packages/pip/_internal/utils/deprecation.py index 2f20cfd49d32f0bbab7b4719eb2dbdca971b751a..72bd6f25a554b303d0bf5028145cf3a5c71b3e06 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/deprecation.py +++ b/venv/Lib/site-packages/pip/_internal/utils/deprecation.py @@ -2,22 +2,13 @@ A module that implements tooling to enable easy warnings about deprecations. """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import warnings +from typing import Any, Optional, TextIO, Type, Union from pip._vendor.packaging.version import parse -from pip import __version__ as current_version -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Any, Optional - +from pip import __version__ as current_version # NOTE: tests patch this name. DEPRECATION_MSG_PREFIX = "DEPRECATION: " @@ -26,29 +17,31 @@ class PipDeprecationWarning(Warning): pass -_original_showwarning = None # type: Any +_original_showwarning: Any = None # Warnings <-> Logging Integration -def _showwarning(message, category, filename, lineno, file=None, line=None): +def _showwarning( + message: Union[Warning, str], + category: Type[Warning], + filename: str, + lineno: int, + file: Optional[TextIO] = None, + line: Optional[str] = None, +) -> None: if file is not None: if _original_showwarning is not None: - _original_showwarning( - message, category, filename, lineno, file, line, - ) + _original_showwarning(message, category, filename, lineno, file, line) elif issubclass(category, PipDeprecationWarning): # We use a specially named logger which will handle all of the # deprecation messages for pip. logger = logging.getLogger("pip._internal.deprecations") logger.warning(message) else: - _original_showwarning( - message, category, filename, lineno, file, line, - ) + _original_showwarning(message, category, filename, lineno, file, line) -def install_warning_logger(): - # type: () -> None +def install_warning_logger() -> None: # Enable our Deprecation Warnings warnings.simplefilter("default", PipDeprecationWarning, append=True) @@ -59,46 +52,69 @@ def install_warning_logger(): warnings.showwarning = _showwarning -def deprecated(reason, replacement, gone_in, issue=None): - # type: (str, Optional[str], Optional[str], Optional[int]) -> None +def deprecated( + *, + reason: str, + replacement: Optional[str], + gone_in: Optional[str], + feature_flag: Optional[str] = None, + issue: Optional[int] = None, +) -> None: """Helper to deprecate existing functionality. reason: Textual reason shown to the user about why this functionality has - been deprecated. + been deprecated. Should be a complete sentence. replacement: Textual suggestion shown to the user about what alternative functionality they can use. gone_in: The version of pip does this functionality should get removed in. - Raises errors if pip's current version is greater than or equal to + Raises an error if pip's current version is greater than or equal to this. + feature_flag: + Command-line flag of the form --use-feature={feature_flag} for testing + upcoming functionality. issue: Issue number on the tracker that would serve as a useful place for users to find related discussion and provide feedback. - - Always pass replacement, gone_in and issue as keyword arguments for clarity - at the call site. """ - # Construct a nice message. - # This is eagerly formatted as we want it to get logged as if someone - # typed this entire message out. - sentences = [ - (reason, DEPRECATION_MSG_PREFIX + "{}"), - (gone_in, "pip {} will remove support for this functionality."), - (replacement, "A possible replacement is {}."), - (issue, ( - "You can find discussion regarding this at " - "https://github.com/pypa/pip/issues/{}." - )), + # Determine whether or not the feature is already gone in this version. + is_gone = gone_in is not None and parse(current_version) >= parse(gone_in) + + message_parts = [ + (reason, f"{DEPRECATION_MSG_PREFIX}{{}}"), + ( + gone_in, + "pip {} will enforce this behaviour change." + if not is_gone + else "Since pip {}, this is no longer supported.", + ), + ( + replacement, + "A possible replacement is {}.", + ), + ( + feature_flag, + "You can use the flag --use-feature={} to test the upcoming behaviour." + if not is_gone + else None, + ), + ( + issue, + "Discussion can be found at https://github.com/pypa/pip/issues/{}", + ), ] + message = " ".join( - template.format(val) for val, template in sentences if val is not None + format_str.format(value) + for value, format_str in message_parts + if format_str is not None and value is not None ) - # Raise as an error if it has to be removed. - if gone_in is not None and parse(current_version) >= parse(gone_in): + # Raise as an error if this behaviour is deprecated. + if is_gone: raise PipDeprecationWarning(message) warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py b/venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py index f1fe209e91153e768264cb20b24c557faae7a059..0e8e5e1608b911e789a3d346ebe48aa7cc54b79e 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py +++ b/venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py @@ -1,34 +1,12 @@ -import logging +from typing import Optional -from pip._internal.models.direct_url import ( - DIRECT_URL_METADATA_NAME, - ArchiveInfo, - DirectUrl, - DirectUrlValidationError, - DirInfo, - VcsInfo, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo +from pip._internal.models.link import Link +from pip._internal.utils.urls import path_to_url from pip._internal.vcs import vcs -try: - from json import JSONDecodeError -except ImportError: - # PY2 - JSONDecodeError = ValueError # type: ignore -if MYPY_CHECK_RUNNING: - from typing import Optional - - from pip._internal.models.link import Link - - from pip._vendor.pkg_resources import Distribution - -logger = logging.getLogger(__name__) - - -def direct_url_as_pep440_direct_reference(direct_url, name): - # type: (DirectUrl, str) -> str +def direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> str: """Convert a DirectUrl to a pip requirement string.""" direct_url.validate() # if invalid, this is a pip bug requirement = name + " @ " @@ -43,10 +21,6 @@ def direct_url_as_pep440_direct_reference(direct_url, name): fragments.append(direct_url.info.hash) else: assert isinstance(direct_url.info, DirInfo) - # pip should never reach this point for editables, since - # pip freeze inspects the editable project location to produce - # the requirement string - assert not direct_url.info.editable requirement += direct_url.url if direct_url.subdirectory: fragments.append("subdirectory=" + direct_url.subdirectory) @@ -55,13 +29,21 @@ def direct_url_as_pep440_direct_reference(direct_url, name): return requirement -def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False): - # type: (Link, Optional[str], bool) -> DirectUrl +def direct_url_for_editable(source_dir: str) -> DirectUrl: + return DirectUrl( + url=path_to_url(source_dir), + info=DirInfo(editable=True), + ) + + +def direct_url_from_link( + link: Link, source_dir: Optional[str] = None, link_is_in_wheel_cache: bool = False +) -> DirectUrl: if link.is_vcs: vcs_backend = vcs.get_backend_for_scheme(link.scheme) assert vcs_backend - url, requested_revision, _ = ( - vcs_backend.get_url_rev_and_auth(link.url_without_fragment) + url, requested_revision, _ = vcs_backend.get_url_rev_and_auth( + link.url_without_fragment ) # For VCS links, we need to find out and add commit_id. if link_is_in_wheel_cache: @@ -97,34 +79,9 @@ def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False): hash = None hash_name = link.hash_name if hash_name: - hash = "{}={}".format(hash_name, link.hash) + hash = f"{hash_name}={link.hash}" return DirectUrl( url=link.url_without_fragment, info=ArchiveInfo(hash=hash), subdirectory=link.subdirectory_fragment, ) - - -def dist_get_direct_url(dist): - # type: (Distribution) -> Optional[DirectUrl] - """Obtain a DirectUrl from a pkg_resource.Distribution. - - Returns None if the distribution has no `direct_url.json` metadata, - or if `direct_url.json` is invalid. - """ - if not dist.has_metadata(DIRECT_URL_METADATA_NAME): - return None - try: - return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME)) - except ( - DirectUrlValidationError, - JSONDecodeError, - UnicodeDecodeError - ) as e: - logger.warning( - "Error parsing %s for %s: %s", - DIRECT_URL_METADATA_NAME, - dist.project_name, - e, - ) - return None diff --git a/venv/Lib/site-packages/pip/_internal/utils/distutils_args.py b/venv/Lib/site-packages/pip/_internal/utils/distutils_args.py index e38e402d7330778385f65a440b5b39f7bcbdedb3..e4aa5b827f66a5002df612738623be69206bc54c 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/distutils_args.py +++ b/venv/Lib/site-packages/pip/_internal/utils/distutils_args.py @@ -1,11 +1,6 @@ from distutils.errors import DistutilsArgError from distutils.fancy_getopt import FancyGetopt - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Dict, List - +from typing import Dict, List _options = [ ("exec-prefix=", None, ""), @@ -27,8 +22,7 @@ _options = [ _distutils_getopt = FancyGetopt(_options) # type: ignore -def parse_distutils_args(args): - # type: (List[str]) -> Dict[str, str] +def parse_distutils_args(args: List[str]) -> Dict[str, str]: """Parse provided arguments, returning an object that has the matched arguments. diff --git a/venv/Lib/site-packages/pip/_internal/utils/encoding.py b/venv/Lib/site-packages/pip/_internal/utils/encoding.py index ab4d4b98e3e1bca6f28db1ae114e48933a36be4e..1c73f6c9a5d4c30a16f2b6ca875e0c75ece1dfc1 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/encoding.py +++ b/venv/Lib/site-packages/pip/_internal/utils/encoding.py @@ -1,41 +1,35 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import codecs import locale import re import sys +from typing import List, Tuple -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Tuple, Text - -BOMS = [ - (codecs.BOM_UTF8, 'utf-8'), - (codecs.BOM_UTF16, 'utf-16'), - (codecs.BOM_UTF16_BE, 'utf-16-be'), - (codecs.BOM_UTF16_LE, 'utf-16-le'), - (codecs.BOM_UTF32, 'utf-32'), - (codecs.BOM_UTF32_BE, 'utf-32-be'), - (codecs.BOM_UTF32_LE, 'utf-32-le'), -] # type: List[Tuple[bytes, Text]] +BOMS: List[Tuple[bytes, str]] = [ + (codecs.BOM_UTF8, "utf-8"), + (codecs.BOM_UTF16, "utf-16"), + (codecs.BOM_UTF16_BE, "utf-16-be"), + (codecs.BOM_UTF16_LE, "utf-16-le"), + (codecs.BOM_UTF32, "utf-32"), + (codecs.BOM_UTF32_BE, "utf-32-be"), + (codecs.BOM_UTF32_LE, "utf-32-le"), +] -ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') +ENCODING_RE = re.compile(br"coding[:=]\s*([-\w.]+)") -def auto_decode(data): - # type: (bytes) -> Text +def auto_decode(data: bytes) -> str: """Check a bytes string for a BOM to correctly detect the encoding Fallback to locale.getpreferredencoding(False) like open() on Python3""" for bom, encoding in BOMS: if data.startswith(bom): - return data[len(bom):].decode(encoding) + return data[len(bom) :].decode(encoding) # Lets check the first two lines as in PEP263 - for line in data.split(b'\n')[:2]: - if line[0:1] == b'#' and ENCODING_RE.search(line): - encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + for line in data.split(b"\n")[:2]: + if line[0:1] == b"#" and ENCODING_RE.search(line): + result = ENCODING_RE.search(line) + assert result is not None + encoding = result.groups()[0].decode("ascii") return data.decode(encoding) return data.decode( locale.getpreferredencoding(False) or sys.getdefaultencoding(), diff --git a/venv/Lib/site-packages/pip/_internal/utils/entrypoints.py b/venv/Lib/site-packages/pip/_internal/utils/entrypoints.py index befd01c890184c74534bfefa1abd2376f234ac42..1504a12916b10c2de007d0ac0e1a3531ac79f8a7 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/entrypoints.py +++ b/venv/Lib/site-packages/pip/_internal/utils/entrypoints.py @@ -1,14 +1,10 @@ import sys +from typing import List, Optional from pip._internal.cli.main import main -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Optional, List - -def _wrapper(args=None): - # type: (Optional[List[str]]) -> int +def _wrapper(args: Optional[List[str]] = None) -> int: """Central wrapper for all old entrypoints. Historically pip has had several entrypoints defined. Because of issues diff --git a/venv/Lib/site-packages/pip/_internal/utils/filesystem.py b/venv/Lib/site-packages/pip/_internal/utils/filesystem.py index 437a7fd14824bc40b046baaa74eab4a5bc3234f3..b7e6191abe6b4b10888071e959146e52519bf132 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/filesystem.py +++ b/venv/Lib/site-packages/pip/_internal/utils/filesystem.py @@ -1,4 +1,3 @@ -import errno import fnmatch import os import os.path @@ -8,28 +7,15 @@ import stat import sys from contextlib import contextmanager from tempfile import NamedTemporaryFile +from typing import Any, BinaryIO, Iterator, List, Union, cast -# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is -# why we ignore the type on this import. -from pip._vendor.retrying import retry # type: ignore -from pip._vendor.six import PY2 +from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed from pip._internal.utils.compat import get_path_uid from pip._internal.utils.misc import format_size -from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast -if MYPY_CHECK_RUNNING: - from typing import Any, BinaryIO, Iterator, List, Union - class NamedTemporaryFileResult(BinaryIO): - @property - def file(self): - # type: () -> BinaryIO - pass - - -def check_path_owner(path): - # type: (str) -> bool +def check_path_owner(path: str) -> bool: # If we don't have a way to check the effective uid of this process, then # we'll just assume that we own the directory. if sys.platform == "win32" or not hasattr(os, "geteuid"): @@ -56,8 +42,7 @@ def check_path_owner(path): return False # assume we don't own the path -def copy2_fixed(src, dest): - # type: (str, str) -> None +def copy2_fixed(src: str, dest: str) -> None: """Wrap shutil.copy2() but map errors copying socket files to SpecialFileError as expected. @@ -65,7 +50,7 @@ def copy2_fixed(src, dest): """ try: shutil.copy2(src, dest) - except (OSError, IOError): + except OSError: for f in [src, dest]: try: is_socket_file = is_socket(f) @@ -75,20 +60,17 @@ def copy2_fixed(src, dest): pass else: if is_socket_file: - raise shutil.SpecialFileError( - "`{f}` is a socket".format(**locals())) + raise shutil.SpecialFileError(f"`{f}` is a socket") raise -def is_socket(path): - # type: (str) -> bool +def is_socket(path: str) -> bool: return stat.S_ISSOCK(os.lstat(path).st_mode) @contextmanager -def adjacent_tmp_file(path, **kwargs): - # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] +def adjacent_tmp_file(path: str, **kwargs: Any) -> Iterator[BinaryIO]: """Return a file-like object pointing to a tmp file next to path. The file is created securely and is ensured to be written to disk @@ -101,37 +83,26 @@ def adjacent_tmp_file(path, **kwargs): delete=False, dir=os.path.dirname(path), prefix=os.path.basename(path), - suffix='.tmp', - **kwargs + suffix=".tmp", + **kwargs, ) as f: - result = cast('NamedTemporaryFileResult', f) + result = cast(BinaryIO, f) try: yield result finally: - result.file.flush() - os.fsync(result.file.fileno()) + result.flush() + os.fsync(result.fileno()) -_replace_retry = retry(stop_max_delay=1000, wait_fixed=250) +# Tenacity raises RetryError by default, explicitly raise the original exception +_replace_retry = retry(reraise=True, stop=stop_after_delay(1), wait=wait_fixed(0.25)) -if PY2: - @_replace_retry - def replace(src, dest): - # type: (str, str) -> None - try: - os.rename(src, dest) - except OSError: - os.remove(dest) - os.rename(src, dest) - -else: - replace = _replace_retry(os.replace) +replace = _replace_retry(os.replace) # test_writable_dir and _test_writable_dir_win are copied from Flit, # with the author's agreement to also place them under pip's license. -def test_writable_dir(path): - # type: (str) -> bool +def test_writable_dir(path: str) -> bool: """Check if a directory is writable. Uses os.access() on POSIX, tries creating files on Windows. @@ -143,72 +114,62 @@ def test_writable_dir(path): break # Should never get here, but infinite loops are bad path = parent - if os.name == 'posix': + if os.name == "posix": return os.access(path, os.W_OK) return _test_writable_dir_win(path) -def _test_writable_dir_win(path): - # type: (str) -> bool +def _test_writable_dir_win(path: str) -> bool: # os.access doesn't work on Windows: http://bugs.python.org/issue2528 # and we can't use tempfile: http://bugs.python.org/issue22107 - basename = 'accesstest_deleteme_fishfingers_custard_' - alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789' - for i in range(10): - name = basename + ''.join(random.choice(alphabet) for _ in range(6)) + basename = "accesstest_deleteme_fishfingers_custard_" + alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" + for _ in range(10): + name = basename + "".join(random.choice(alphabet) for _ in range(6)) file = os.path.join(path, name) try: fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL) - # Python 2 doesn't support FileExistsError and PermissionError. - except OSError as e: - # exception FileExistsError - if e.errno == errno.EEXIST: - continue - # exception PermissionError - if e.errno == errno.EPERM or e.errno == errno.EACCES: - # This could be because there's a directory with the same name. - # But it's highly unlikely there's a directory called that, - # so we'll assume it's because the parent dir is not writable. - return False - raise + except FileExistsError: + pass + except PermissionError: + # This could be because there's a directory with the same name. + # But it's highly unlikely there's a directory called that, + # so we'll assume it's because the parent dir is not writable. + # This could as well be because the parent dir is not readable, + # due to non-privileged user access. + return False else: os.close(fd) os.unlink(file) return True # This should never be reached - raise EnvironmentError( - 'Unexpected condition testing for writable directory' - ) + raise OSError("Unexpected condition testing for writable directory") -def find_files(path, pattern): - # type: (str, str) -> List[str] +def find_files(path: str, pattern: str) -> List[str]: """Returns a list of absolute paths of files beneath path, recursively, with filenames which match the UNIX-style shell glob pattern.""" - result = [] # type: List[str] - for root, dirs, files in os.walk(path): + result: List[str] = [] + for root, _, files in os.walk(path): matches = fnmatch.filter(files, pattern) result.extend(os.path.join(root, f) for f in matches) return result -def file_size(path): - # type: (str) -> Union[int, float] +def file_size(path: str) -> Union[int, float]: # If it's a symlink, return 0. if os.path.islink(path): return 0 return os.path.getsize(path) -def format_file_size(path): - # type: (str) -> str +def format_file_size(path: str) -> str: return format_size(file_size(path)) -def directory_size(path): - # type: (str) -> Union[int, float] +def directory_size(path: str) -> Union[int, float]: size = 0.0 for root, _dirs, files in os.walk(path): for filename in files: @@ -217,6 +178,5 @@ def directory_size(path): return size -def format_directory_size(path): - # type: (str) -> str +def format_directory_size(path: str) -> str: return format_size(directory_size(path)) diff --git a/venv/Lib/site-packages/pip/_internal/utils/filetypes.py b/venv/Lib/site-packages/pip/_internal/utils/filetypes.py index daa0ca771b77a32bf498d07803f5bffc34b1abf9..5948570178f3e6e79d1ff574241d09d4d8ed78de 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/filetypes.py +++ b/venv/Lib/site-packages/pip/_internal/utils/filetypes.py @@ -1,16 +1,27 @@ """Filetype information. """ -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Tuple +from typing import Tuple -WHEEL_EXTENSION = '.whl' -BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') # type: Tuple[str, ...] -XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', - '.tar.lz', '.tar.lzma') # type: Tuple[str, ...] -ZIP_EXTENSIONS = ('.zip', WHEEL_EXTENSION) # type: Tuple[str, ...] -TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') # type: Tuple[str, ...] -ARCHIVE_EXTENSIONS = ( - ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS +from pip._internal.utils.misc import splitext + +WHEEL_EXTENSION = ".whl" +BZ2_EXTENSIONS: Tuple[str, ...] = (".tar.bz2", ".tbz") +XZ_EXTENSIONS: Tuple[str, ...] = ( + ".tar.xz", + ".txz", + ".tlz", + ".tar.lz", + ".tar.lzma", ) +ZIP_EXTENSIONS: Tuple[str, ...] = (".zip", WHEEL_EXTENSION) +TAR_EXTENSIONS: Tuple[str, ...] = (".tar.gz", ".tgz", ".tar") +ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS + + +def is_archive_file(name: str) -> bool: + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False diff --git a/venv/Lib/site-packages/pip/_internal/utils/glibc.py b/venv/Lib/site-packages/pip/_internal/utils/glibc.py index 361042441384693dbeeb9424c78dedf3bdbb8a3d..7bd3c20681d865cb4fa42617cf939b5512c7663f 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/glibc.py +++ b/venv/Lib/site-packages/pip/_internal/utils/glibc.py @@ -1,25 +1,17 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False -from __future__ import absolute_import - import os import sys - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Optional, Tuple +from typing import Optional, Tuple -def glibc_version_string(): - # type: () -> Optional[str] +def glibc_version_string() -> Optional[str]: "Returns glibc version string, or None if not using glibc." return glibc_version_string_confstr() or glibc_version_string_ctypes() -def glibc_version_string_confstr(): - # type: () -> Optional[str] +def glibc_version_string_confstr() -> Optional[str]: "Primary implementation of glibc_version_string using os.confstr." # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely # to be broken or missing. This strategy is used in the standard library @@ -36,8 +28,7 @@ def glibc_version_string_confstr(): return version -def glibc_version_string_ctypes(): - # type: () -> Optional[str] +def glibc_version_string_ctypes() -> Optional[str]: "Fallback implementation of glibc_version_string using ctypes." try: @@ -84,8 +75,7 @@ def glibc_version_string_ctypes(): # versions that was generated by pip 8.1.2 and earlier is useless and # misleading. Solution: instead of using platform, use our code that actually # works. -def libc_ver(): - # type: () -> Tuple[str, str] +def libc_ver() -> Tuple[str, str]: """Try to determine the glibc version Returns a tuple of strings (lib, version) which default to empty strings diff --git a/venv/Lib/site-packages/pip/_internal/utils/hashes.py b/venv/Lib/site-packages/pip/_internal/utils/hashes.py index 396cf82e753beaae65ee9eb1dfa451febcb3284a..82eb035a06e65b6224ac65f9b7a19ace4ceb5ec5 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/hashes.py +++ b/venv/Lib/site-packages/pip/_internal/utils/hashes.py @@ -1,67 +1,73 @@ -from __future__ import absolute_import - import hashlib +from typing import TYPE_CHECKING, BinaryIO, Dict, Iterator, List -from pip._vendor.six import iteritems, iterkeys, itervalues - -from pip._internal.exceptions import ( - HashMismatch, - HashMissing, - InstallationError, -) +from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError from pip._internal.utils.misc import read_chunks -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import ( - Dict, List, BinaryIO, NoReturn, Iterator - ) - from pip._vendor.six import PY3 - if PY3: - from hashlib import _Hash - else: - from hashlib import _hash as _Hash +if TYPE_CHECKING: + from hashlib import _Hash + + # NoReturn introduced in 3.6.2; imported only for type checking to maintain + # pip compatibility with older patch versions of Python 3.6 + from typing import NoReturn # The recommended hash algo of the moment. Change this whenever the state of # the art changes; it won't hurt backward compatibility. -FAVORITE_HASH = 'sha256' +FAVORITE_HASH = "sha256" # Names of hashlib algorithms allowed by the --hash option and ``pip hash`` # Currently, those are the ones at least as collision-resistant as sha256. -STRONG_HASHES = ['sha256', 'sha384', 'sha512'] +STRONG_HASHES = ["sha256", "sha384", "sha512"] -class Hashes(object): +class Hashes: """A wrapper that builds multiple hashes at once and checks them against known-good values """ - def __init__(self, hashes=None): - # type: (Dict[str, List[str]]) -> None + + def __init__(self, hashes: Dict[str, List[str]] = None) -> None: """ :param hashes: A dict of algorithm names pointing to lists of allowed hex digests """ - self._allowed = {} if hashes is None else hashes + allowed = {} + if hashes is not None: + for alg, keys in hashes.items(): + # Make sure values are always sorted (to ease equality checks) + allowed[alg] = sorted(keys) + self._allowed = allowed + + def __and__(self, other: "Hashes") -> "Hashes": + if not isinstance(other, Hashes): + return NotImplemented + + # If either of the Hashes object is entirely empty (i.e. no hash + # specified at all), all hashes from the other object are allowed. + if not other: + return self + if not self: + return other + + # Otherwise only hashes that present in both objects are allowed. + new = {} + for alg, values in other._allowed.items(): + if alg not in self._allowed: + continue + new[alg] = [v for v in values if v in self._allowed[alg]] + return Hashes(new) @property - def digest_count(self): - # type: () -> int + def digest_count(self) -> int: return sum(len(digests) for digests in self._allowed.values()) - def is_hash_allowed( - self, - hash_name, # type: str - hex_digest, # type: str - ): - # type: (...) -> bool + def is_hash_allowed(self, hash_name: str, hex_digest: str) -> bool: """Return whether the given hex digest is allowed.""" return hex_digest in self._allowed.get(hash_name, []) - def check_against_chunks(self, chunks): - # type: (Iterator[bytes]) -> None + def check_against_chunks(self, chunks: Iterator[bytes]) -> None: """Check good hashes against ones built from iterable of chunks of data. @@ -69,29 +75,25 @@ class Hashes(object): """ gots = {} - for hash_name in iterkeys(self._allowed): + for hash_name in self._allowed.keys(): try: gots[hash_name] = hashlib.new(hash_name) except (ValueError, TypeError): - raise InstallationError( - 'Unknown hash name: {}'.format(hash_name) - ) + raise InstallationError(f"Unknown hash name: {hash_name}") for chunk in chunks: - for hash in itervalues(gots): + for hash in gots.values(): hash.update(chunk) - for hash_name, got in iteritems(gots): + for hash_name, got in gots.items(): if got.hexdigest() in self._allowed[hash_name]: return self._raise(gots) - def _raise(self, gots): - # type: (Dict[str, _Hash]) -> NoReturn + def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn": raise HashMismatch(self._allowed, gots) - def check_against_file(self, file): - # type: (BinaryIO) -> None + def check_against_file(self, file: BinaryIO) -> None: """Check good hashes against a file-like object Raise HashMismatch if none match. @@ -99,19 +101,29 @@ class Hashes(object): """ return self.check_against_chunks(read_chunks(file)) - def check_against_path(self, path): - # type: (str) -> None - with open(path, 'rb') as file: + def check_against_path(self, path: str) -> None: + with open(path, "rb") as file: return self.check_against_file(file) - def __nonzero__(self): - # type: () -> bool + def __bool__(self) -> bool: """Return whether I know any known-good hashes.""" return bool(self._allowed) - def __bool__(self): - # type: () -> bool - return self.__nonzero__() + def __eq__(self, other: object) -> bool: + if not isinstance(other, Hashes): + return NotImplemented + return self._allowed == other._allowed + + def __hash__(self) -> int: + return hash( + ",".join( + sorted( + ":".join((alg, digest)) + for alg, digest_list in self._allowed.items() + for digest in digest_list + ) + ) + ) class MissingHashes(Hashes): @@ -121,13 +133,12 @@ class MissingHashes(Hashes): exception showing it to the user. """ - def __init__(self): - # type: () -> None + + def __init__(self) -> None: """Don't offer the ``hashes`` kwarg.""" # Pass our favorite hash in to generate a "gotten hash". With the # empty list, it will never match, so an error will always raise. - super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + super().__init__(hashes={FAVORITE_HASH: []}) - def _raise(self, gots): - # type: (Dict[str, _Hash]) -> NoReturn + def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn": raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py b/venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py index 5b93b1d6730518ec49afe78bdfbe74407825d8ee..276aa79bb81356cdca73af0a5851b448707784a4 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py +++ b/venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py @@ -10,8 +10,7 @@ old to handle TLSv1.2. import sys -def inject_securetransport(): - # type: () -> None +def inject_securetransport() -> None: # Only relevant on macOS if sys.platform != "darwin": return @@ -22,7 +21,7 @@ def inject_securetransport(): return # Checks for OpenSSL 1.0.1 - if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100f: + if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100F: return try: diff --git a/venv/Lib/site-packages/pip/_internal/utils/logging.py b/venv/Lib/site-packages/pip/_internal/utils/logging.py index 9a017cf7e333609f5f718177b7b40b21bfe1aed7..a4b828a84e112e9511c040bc1e0271c72416068d 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/logging.py +++ b/venv/Lib/site-packages/pip/_internal/utils/logging.py @@ -1,18 +1,13 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import contextlib import errno import logging import logging.handlers import os import sys -from logging import Filter, getLogger - -from pip._vendor.six import PY2 +from logging import Filter +from typing import IO, Any, Callable, Iterator, Optional, TextIO, Type, cast +from pip._internal.utils._log import VERBOSE, getLogger from pip._internal.utils.compat import WINDOWS from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX from pip._internal.utils.misc import ensure_dir @@ -24,81 +19,38 @@ except ImportError: try: - # Use "import as" and set colorama in the else clause to avoid mypy - # errors and get the following correct revealed type for colorama: - # `Union[_importlib_modulespec.ModuleType, None]` - # Otherwise, we get an error like the following in the except block: - # > Incompatible types in assignment (expression has type "None", - # variable has type Module) - # TODO: eliminate the need to use "import as" once mypy addresses some - # of its issues with conditional imports. Here is an umbrella issue: - # https://github.com/python/mypy/issues/1297 - from pip._vendor import colorama as _colorama + from pip._vendor import colorama # Lots of different errors can come from this, including SystemError and # ImportError. except Exception: colorama = None -else: - # Import Fore explicitly rather than accessing below as colorama.Fore - # to avoid the following error running mypy: - # > Module has no attribute "Fore" - # TODO: eliminate the need to import Fore once mypy addresses some of its - # issues with conditional imports. This particular case could be an - # instance of the following issue (but also see the umbrella issue above): - # https://github.com/python/mypy/issues/3500 - from pip._vendor.colorama import Fore - - colorama = _colorama _log_state = threading.local() -subprocess_logger = getLogger('pip.subprocessor') +subprocess_logger = getLogger("pip.subprocessor") class BrokenStdoutLoggingError(Exception): """ Raised if BrokenPipeError occurs for the stdout stream while logging. """ - pass -# BrokenPipeError does not exist in Python 2 and, in addition, manifests -# differently in Windows and non-Windows. -if WINDOWS: - # In Windows, a broken pipe can show up as EINVAL rather than EPIPE: +def _is_broken_pipe_error(exc_class: Type[BaseException], exc: BaseException) -> bool: + if exc_class is BrokenPipeError: + return True + + # On Windows, a broken pipe can show up as EINVAL rather than EPIPE: # https://bugs.python.org/issue19612 # https://bugs.python.org/issue30418 - if PY2: - def _is_broken_pipe_error(exc_class, exc): - """See the docstring for non-Windows Python 3 below.""" - return (exc_class is IOError and - exc.errno in (errno.EINVAL, errno.EPIPE)) - else: - # In Windows, a broken pipe IOError became OSError in Python 3. - def _is_broken_pipe_error(exc_class, exc): - """See the docstring for non-Windows Python 3 below.""" - return ((exc_class is BrokenPipeError) or # noqa: F821 - (exc_class is OSError and - exc.errno in (errno.EINVAL, errno.EPIPE))) -elif PY2: - def _is_broken_pipe_error(exc_class, exc): - """See the docstring for non-Windows Python 3 below.""" - return (exc_class is IOError and exc.errno == errno.EPIPE) -else: - # Then we are in the non-Windows Python 3 case. - def _is_broken_pipe_error(exc_class, exc): - """ - Return whether an exception is a broken pipe error. + if not WINDOWS: + return False - Args: - exc_class: an exception class. - exc: an exception instance. - """ - return (exc_class is BrokenPipeError) # noqa: F821 + return isinstance(exc, OSError) and exc.errno in (errno.EINVAL, errno.EPIPE) @contextlib.contextmanager -def indent_log(num=2): +def indent_log(num: int = 2) -> Iterator[None]: """ A context manager which will cause the log output to be indented for any log messages emitted inside it. @@ -112,63 +64,65 @@ def indent_log(num=2): _log_state.indentation -= num -def get_indentation(): - return getattr(_log_state, 'indentation', 0) +def get_indentation() -> int: + return getattr(_log_state, "indentation", 0) class IndentingFormatter(logging.Formatter): - - def __init__(self, *args, **kwargs): + default_time_format = "%Y-%m-%dT%H:%M:%S" + + def __init__( + self, + *args: Any, + add_timestamp: bool = False, + **kwargs: Any, + ) -> None: """ A logging.Formatter that obeys the indent_log() context manager. :param add_timestamp: A bool indicating output lines should be prefixed with their record's timestamp. """ - self.add_timestamp = kwargs.pop("add_timestamp", False) - super(IndentingFormatter, self).__init__(*args, **kwargs) + self.add_timestamp = add_timestamp + super().__init__(*args, **kwargs) - def get_message_start(self, formatted, levelno): + def get_message_start(self, formatted: str, levelno: int) -> str: """ Return the start of the formatted log message (not counting the prefix to add to each line). """ if levelno < logging.WARNING: - return '' + return "" if formatted.startswith(DEPRECATION_MSG_PREFIX): # Then the message already has a prefix. We don't want it to # look like "WARNING: DEPRECATION: ...." - return '' + return "" if levelno < logging.ERROR: - return 'WARNING: ' + return "WARNING: " - return 'ERROR: ' + return "ERROR: " - def format(self, record): + def format(self, record: logging.LogRecord) -> str: """ Calls the standard formatter, but will indent all of the log message lines by our current indentation level. """ - formatted = super(IndentingFormatter, self).format(record) + formatted = super().format(record) message_start = self.get_message_start(formatted, record.levelno) formatted = message_start + formatted - prefix = '' + prefix = "" if self.add_timestamp: - # TODO: Use Formatter.default_time_format after dropping PY2. - t = self.formatTime(record, "%Y-%m-%dT%H:%M:%S") - prefix = '{t},{record.msecs:03.0f} '.format(**locals()) + prefix = f"{self.formatTime(record)} " prefix += " " * get_indentation() - formatted = "".join([ - prefix + line - for line in formatted.splitlines(True) - ]) + formatted = "".join([prefix + line for line in formatted.splitlines(True)]) return formatted -def _color_wrap(*colors): - def wrapped(inp): +def _color_wrap(*colors: str) -> Callable[[str], str]: + def wrapped(inp: str) -> str: return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped @@ -178,36 +132,38 @@ class ColorizedStreamHandler(logging.StreamHandler): if colorama: COLORS = [ # This needs to be in order from highest logging level to lowest. - (logging.ERROR, _color_wrap(Fore.RED)), - (logging.WARNING, _color_wrap(Fore.YELLOW)), + (logging.ERROR, _color_wrap(colorama.Fore.RED)), + (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)), ] else: COLORS = [] - def __init__(self, stream=None, no_color=None): - logging.StreamHandler.__init__(self, stream) + def __init__(self, stream: Optional[TextIO] = None, no_color: bool = None) -> None: + super().__init__(stream) self._no_color = no_color if WINDOWS and colorama: self.stream = colorama.AnsiToWin32(self.stream) - def _using_stdout(self): + def _using_stdout(self) -> bool: """ Return whether the handler is using sys.stdout. """ if WINDOWS and colorama: # Then self.stream is an AnsiToWin32 object. - return self.stream.wrapped is sys.stdout + stream = cast(colorama.AnsiToWin32, self.stream) + return stream.wrapped is sys.stdout return self.stream is sys.stdout - def should_color(self): + def should_color(self) -> bool: # Don't colorize things if we do not have colorama or if told not to if not colorama or self._no_color: return False real_stream = ( - self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + self.stream + if not isinstance(self.stream, colorama.AnsiToWin32) else self.stream.wrapped ) @@ -222,8 +178,8 @@ class ColorizedStreamHandler(logging.StreamHandler): # If anything else we should not color it return False - def format(self, record): - msg = logging.StreamHandler.format(self, record) + def format(self, record: logging.LogRecord) -> str: + msg = super().format(record) if self.should_color(): for level, color in self.COLORS: @@ -234,32 +190,34 @@ class ColorizedStreamHandler(logging.StreamHandler): return msg # The logging module says handleError() can be customized. - def handleError(self, record): + def handleError(self, record: logging.LogRecord) -> None: exc_class, exc = sys.exc_info()[:2] # If a broken pipe occurred while calling write() or flush() on the # stdout stream in logging's Handler.emit(), then raise our special # exception so we can handle it in main() instead of logging the # broken pipe error and continuing. - if (exc_class and self._using_stdout() and - _is_broken_pipe_error(exc_class, exc)): + if ( + exc_class + and exc + and self._using_stdout() + and _is_broken_pipe_error(exc_class, exc) + ): raise BrokenStdoutLoggingError() - return super(ColorizedStreamHandler, self).handleError(record) + return super().handleError(record) class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): - - def _open(self): + def _open(self) -> IO[Any]: ensure_dir(os.path.dirname(self.baseFilename)) - return logging.handlers.RotatingFileHandler._open(self) + return super()._open() class MaxLevelFilter(Filter): - - def __init__(self, level): + def __init__(self, level: int) -> None: self.level = level - def filter(self, record): + def filter(self, record: logging.LogRecord) -> bool: return record.levelno < self.level @@ -269,31 +227,33 @@ class ExcludeLoggerFilter(Filter): A logging Filter that excludes records from a logger (or its children). """ - def filter(self, record): + def filter(self, record: logging.LogRecord) -> bool: # The base Filter class allows only records from a logger (or its # children). - return not super(ExcludeLoggerFilter, self).filter(record) + return not super().filter(record) -def setup_logging(verbosity, no_color, user_log_file): +def setup_logging(verbosity: int, no_color: bool, user_log_file: Optional[str]) -> int: """Configures and sets up all of the logging Returns the requested logging level, as its integer value. """ # Determine the level to be logging at. - if verbosity >= 1: - level = "DEBUG" + if verbosity >= 2: + level_number = logging.DEBUG + elif verbosity == 1: + level_number = VERBOSE elif verbosity == -1: - level = "WARNING" + level_number = logging.WARNING elif verbosity == -2: - level = "ERROR" + level_number = logging.ERROR elif verbosity <= -3: - level = "CRITICAL" + level_number = logging.CRITICAL else: - level = "INFO" + level_number = logging.INFO - level_number = getattr(logging, level) + level = logging.getLevelName(level_number) # The "root" logger should match the "console" level *unless* we also need # to log to a user log file. @@ -322,78 +282,77 @@ def setup_logging(verbosity, no_color, user_log_file): ["user_log"] if include_user_log else [] ) - logging.config.dictConfig({ - "version": 1, - "disable_existing_loggers": False, - "filters": { - "exclude_warnings": { - "()": "pip._internal.utils.logging.MaxLevelFilter", - "level": logging.WARNING, - }, - "restrict_to_subprocess": { - "()": "logging.Filter", - "name": subprocess_logger.name, - }, - "exclude_subprocess": { - "()": "pip._internal.utils.logging.ExcludeLoggerFilter", - "name": subprocess_logger.name, + logging.config.dictConfig( + { + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + "restrict_to_subprocess": { + "()": "logging.Filter", + "name": subprocess_logger.name, + }, + "exclude_subprocess": { + "()": "pip._internal.utils.logging.ExcludeLoggerFilter", + "name": subprocess_logger.name, + }, }, - }, - "formatters": { - "indent": { - "()": IndentingFormatter, - "format": "%(message)s", + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + "indent_with_timestamp": { + "()": IndentingFormatter, + "format": "%(message)s", + "add_timestamp": True, + }, }, - "indent_with_timestamp": { - "()": IndentingFormatter, - "format": "%(message)s", - "add_timestamp": True, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_subprocess", "exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["exclude_subprocess"], + "formatter": "indent", + }, + # A handler responsible for logging to the console messages + # from the "subprocessor" logger. + "console_subprocess": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["restrict_to_subprocess"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "encoding": "utf-8", + "delay": True, + "formatter": "indent_with_timestamp", + }, }, - }, - "handlers": { - "console": { - "level": level, - "class": handler_classes["stream"], - "no_color": no_color, - "stream": log_streams["stdout"], - "filters": ["exclude_subprocess", "exclude_warnings"], - "formatter": "indent", + "root": { + "level": root_level, + "handlers": handlers, }, - "console_errors": { - "level": "WARNING", - "class": handler_classes["stream"], - "no_color": no_color, - "stream": log_streams["stderr"], - "filters": ["exclude_subprocess"], - "formatter": "indent", - }, - # A handler responsible for logging to the console messages - # from the "subprocessor" logger. - "console_subprocess": { - "level": level, - "class": handler_classes["stream"], - "no_color": no_color, - "stream": log_streams["stderr"], - "filters": ["restrict_to_subprocess"], - "formatter": "indent", - }, - "user_log": { - "level": "DEBUG", - "class": handler_classes["file"], - "filename": additional_log_file, - "delay": True, - "formatter": "indent_with_timestamp", - }, - }, - "root": { - "level": root_level, - "handlers": handlers, - }, - "loggers": { - "pip._vendor": { - "level": vendored_log_level - } - }, - }) + "loggers": {"pip._vendor": {"level": vendored_log_level}}, + } + ) return level_number diff --git a/venv/Lib/site-packages/pip/_internal/utils/misc.py b/venv/Lib/site-packages/pip/_internal/utils/misc.py index 09031825afa4c731cc3a1f9b0e6ba14810b1060f..d3e9053efd7d9aab5f44dc051502d2511f81f04c 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/misc.py +++ b/venv/Lib/site-packages/pip/_internal/utils/misc.py @@ -1,8 +1,5 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import import contextlib import errno @@ -15,77 +12,73 @@ import posixpath import shutil import stat import sys -from collections import deque +import urllib.parse +from io import StringIO +from itertools import filterfalse, tee, zip_longest +from types import TracebackType +from typing import ( + Any, + BinaryIO, + Callable, + ContextManager, + Iterable, + Iterator, + List, + Optional, + TextIO, + Tuple, + Type, + TypeVar, + cast, +) -from pip._vendor import pkg_resources -# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is -# why we ignore the type on this import. -from pip._vendor.retrying import retry # type: ignore -from pip._vendor.six import PY2, text_type -from pip._vendor.six.moves import input, map, zip_longest -from pip._vendor.six.moves.urllib import parse as urllib_parse -from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote +from pip._vendor.pkg_resources import Distribution +from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed from pip import __version__ from pip._internal.exceptions import CommandError -from pip._internal.locations import ( - get_major_minor_version, - site_packages, - user_site, -) -from pip._internal.utils.compat import ( - WINDOWS, - expanduser, - stdlib_pkgs, - str_to_display, -) -from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast -from pip._internal.utils.virtualenv import ( - running_under_virtualenv, - virtualenv_no_global, -) - -if PY2: - from io import BytesIO as StringIO -else: - from io import StringIO - -if MYPY_CHECK_RUNNING: - from typing import ( - Any, AnyStr, Container, Iterable, Iterator, List, Optional, Text, - Tuple, Union, - ) - from pip._vendor.pkg_resources import Distribution - - VersionInfo = Tuple[int, int, int] - - -__all__ = ['rmtree', 'display_path', 'backup_dir', - 'ask', 'splitext', - 'format_size', 'is_installable_dir', - 'normalize_path', - 'renames', 'get_prog', - 'captured_stdout', 'ensure_dir', - 'get_installed_version', 'remove_auth_from_url'] +from pip._internal.locations import get_major_minor_version, site_packages, user_site +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.egg_link import egg_link_path_from_location +from pip._internal.utils.virtualenv import running_under_virtualenv + +__all__ = [ + "rmtree", + "display_path", + "backup_dir", + "ask", + "splitext", + "format_size", + "is_installable_dir", + "normalize_path", + "renames", + "get_prog", + "captured_stdout", + "ensure_dir", + "remove_auth_from_url", +] logger = logging.getLogger(__name__) +T = TypeVar("T") +ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType] +VersionInfo = Tuple[int, int, int] +NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]] + -def get_pip_version(): - # type: () -> str +def get_pip_version() -> str: pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..") pip_pkg_dir = os.path.abspath(pip_pkg_dir) - return ( - 'pip {} from {} (python {})'.format( - __version__, pip_pkg_dir, get_major_minor_version(), - ) + return "pip {} from {} (python {})".format( + __version__, + pip_pkg_dir, + get_major_minor_version(), ) -def normalize_version_info(py_version_info): - # type: (Tuple[int, ...]) -> Tuple[int, int, int] +def normalize_version_info(py_version_info: Tuple[int, ...]) -> Tuple[int, int, int]: """ Convert a tuple of ints representing a Python version to one of length three. @@ -101,11 +94,10 @@ def normalize_version_info(py_version_info): elif len(py_version_info) > 3: py_version_info = py_version_info[:3] - return cast('VersionInfo', py_version_info) + return cast("VersionInfo", py_version_info) -def ensure_dir(path): - # type: (AnyStr) -> None +def ensure_dir(path: str) -> None: """os.path.makedirs without EEXIST.""" try: os.makedirs(path) @@ -115,34 +107,32 @@ def ensure_dir(path): raise -def get_prog(): - # type: () -> str +def get_prog() -> str: try: prog = os.path.basename(sys.argv[0]) - if prog in ('__main__.py', '-c'): - return "{} -m pip".format(sys.executable) + if prog in ("__main__.py", "-c"): + return f"{sys.executable} -m pip" else: return prog except (AttributeError, TypeError, IndexError): pass - return 'pip' + return "pip" # Retry every half second for up to 3 seconds -@retry(stop_max_delay=3000, wait_fixed=500) -def rmtree(dir, ignore_errors=False): - # type: (str, bool) -> None - shutil.rmtree(dir, ignore_errors=ignore_errors, - onerror=rmtree_errorhandler) +# Tenacity raises RetryError by default, explicitly raise the original exception +@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5)) +def rmtree(dir: str, ignore_errors: bool = False) -> None: + shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler) -def rmtree_errorhandler(func, path, exc_info): +def rmtree_errorhandler(func: Callable[..., Any], path: str, exc_info: ExcInfo) -> None: """On Windows, the files in .svn are read-only, so when rmtree() tries to remove them, an exception is thrown. We catch that here, remove the read-only attribute, and hopefully continue without problems.""" try: has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE) - except (IOError, OSError): + except OSError: # it's equivalent to os.path.exists return @@ -156,55 +146,16 @@ def rmtree_errorhandler(func, path, exc_info): raise -def path_to_display(path): - # type: (Optional[Union[str, Text]]) -> Optional[Text] - """ - Convert a bytes (or text) path to text (unicode in Python 2) for display - and logging purposes. - - This function should never error out. Also, this function is mainly needed - for Python 2 since in Python 3 str paths are already text. - """ - if path is None: - return None - if isinstance(path, text_type): - return path - # Otherwise, path is a bytes object (str in Python 2). - try: - display_path = path.decode(sys.getfilesystemencoding(), 'strict') - except UnicodeDecodeError: - # Include the full bytes to make troubleshooting easier, even though - # it may not be very human readable. - if PY2: - # Convert the bytes to a readable str representation using - # repr(), and then convert the str to unicode. - # Also, we add the prefix "b" to the repr() return value both - # to make the Python 2 output look like the Python 3 output, and - # to signal to the user that this is a bytes representation. - display_path = str_to_display('b{!r}'.format(path)) - else: - # Silence the "F821 undefined name 'ascii'" flake8 error since - # in Python 3 ascii() is a built-in. - display_path = ascii(path) # noqa: F821 - - return display_path - - -def display_path(path): - # type: (Union[str, Text]) -> str +def display_path(path: str) -> str: """Gives the display value for a given path, making it relative to cwd if possible.""" path = os.path.normcase(os.path.abspath(path)) - if sys.version_info[0] == 2: - path = path.decode(sys.getfilesystemencoding(), 'replace') - path = path.encode(sys.getdefaultencoding(), 'replace') if path.startswith(os.getcwd() + os.path.sep): - path = '.' + path[len(os.getcwd()):] + path = "." + path[len(os.getcwd()) :] return path -def backup_dir(dir, ext='.bak'): - # type: (str, str) -> str +def backup_dir(dir: str, ext: str = ".bak") -> str: """Figure out the name of a directory to back up the given dir to (adding .bak, .bak2, etc)""" n = 1 @@ -215,26 +166,22 @@ def backup_dir(dir, ext='.bak'): return dir + extension -def ask_path_exists(message, options): - # type: (str, Iterable[str]) -> str - for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): +def ask_path_exists(message: str, options: Iterable[str]) -> str: + for action in os.environ.get("PIP_EXISTS_ACTION", "").split(): if action in options: return action return ask(message, options) -def _check_no_input(message): - # type: (str) -> None +def _check_no_input(message: str) -> None: """Raise an error if no input is allowed.""" - if os.environ.get('PIP_NO_INPUT'): + if os.environ.get("PIP_NO_INPUT"): raise Exception( - 'No input was expected ($PIP_NO_INPUT set); question: {}'.format( - message) + f"No input was expected ($PIP_NO_INPUT set); question: {message}" ) -def ask(message, options): - # type: (str, Iterable[str]) -> str +def ask(message: str, options: Iterable[str]) -> str: """Ask the message interactively, with the given possible responses""" while 1: _check_no_input(message) @@ -242,41 +189,53 @@ def ask(message, options): response = response.strip().lower() if response not in options: print( - 'Your response ({!r}) was not one of the expected responses: ' - '{}'.format(response, ', '.join(options)) + "Your response ({!r}) was not one of the expected responses: " + "{}".format(response, ", ".join(options)) ) else: return response -def ask_input(message): - # type: (str) -> str +def ask_input(message: str) -> str: """Ask for input interactively.""" _check_no_input(message) return input(message) -def ask_password(message): - # type: (str) -> str +def ask_password(message: str) -> str: """Ask for a password interactively.""" _check_no_input(message) return getpass.getpass(message) -def format_size(bytes): - # type: (float) -> str +def strtobool(val: str) -> int: + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = val.lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return 1 + elif val in ("n", "no", "f", "false", "off", "0"): + return 0 + else: + raise ValueError(f"invalid truth value {val!r}") + + +def format_size(bytes: float) -> str: if bytes > 1000 * 1000: - return '{:.1f} MB'.format(bytes / 1000.0 / 1000) + return "{:.1f} MB".format(bytes / 1000.0 / 1000) elif bytes > 10 * 1000: - return '{} kB'.format(int(bytes / 1000)) + return "{} kB".format(int(bytes / 1000)) elif bytes > 1000: - return '{:.1f} kB'.format(bytes / 1000.0) + return "{:.1f} kB".format(bytes / 1000.0) else: - return '{} bytes'.format(int(bytes)) + return "{} bytes".format(int(bytes)) -def tabulate(rows): - # type: (Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]] +def tabulate(rows: Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]]: """Return a list of formatted rows and a list of column sizes. For example:: @@ -285,27 +244,29 @@ def tabulate(rows): (['foobar 2000', '3735928559'], [10, 4]) """ rows = [tuple(map(str, row)) for row in rows] - sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue='')] + sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue="")] table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows] return table, sizes -def is_installable_dir(path): - # type: (str) -> bool - """Is path is a directory containing setup.py or pyproject.toml? +def is_installable_dir(path: str) -> bool: + """Is path is a directory containing pyproject.toml or setup.py? + + If pyproject.toml exists, this is a PEP 517 project. Otherwise we look for + a legacy setuptools layout by identifying setup.py. We don't check for the + setup.cfg because using it without setup.py is only available for PEP 517 + projects, which are already covered by the pyproject.toml check. """ if not os.path.isdir(path): return False - setup_py = os.path.join(path, 'setup.py') - if os.path.isfile(setup_py): + if os.path.isfile(os.path.join(path, "pyproject.toml")): return True - pyproject_toml = os.path.join(path, 'pyproject.toml') - if os.path.isfile(pyproject_toml): + if os.path.isfile(os.path.join(path, "setup.py")): return True return False -def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): +def read_chunks(file: BinaryIO, size: int = io.DEFAULT_BUFFER_SIZE) -> Iterator[bytes]: """Yield pieces of data from a file-like object until EOF.""" while True: chunk = file.read(size) @@ -314,13 +275,12 @@ def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): yield chunk -def normalize_path(path, resolve_symlinks=True): - # type: (str, bool) -> str +def normalize_path(path: str, resolve_symlinks: bool = True) -> str: """ Convert a path to its canonical, case-normalized, absolute version. """ - path = expanduser(path) + path = os.path.expanduser(path) if resolve_symlinks: path = os.path.realpath(path) else: @@ -328,18 +288,16 @@ def normalize_path(path, resolve_symlinks=True): return os.path.normcase(path) -def splitext(path): - # type: (str) -> Tuple[str, str] +def splitext(path: str) -> Tuple[str, str]: """Like os.path.splitext, but take off .tar too""" base, ext = posixpath.splitext(path) - if base.lower().endswith('.tar'): + if base.lower().endswith(".tar"): ext = base[-4:] + ext base = base[:-4] return base, ext -def renames(old, new): - # type: (str, str) -> None +def renames(old: str, new: str) -> None: """Like os.renames(), but handles renaming across devices.""" # Implementation borrowed from os.renames(). head, tail = os.path.split(new) @@ -356,8 +314,7 @@ def renames(old, new): pass -def is_local(path): - # type: (str) -> bool +def is_local(path: str) -> bool: """ Return True if path is within sys.prefix, if we're running in a virtualenv. @@ -371,8 +328,7 @@ def is_local(path): return path.startswith(normalize_path(sys.prefix)) -def dist_is_local(dist): - # type: (Distribution) -> bool +def dist_is_local(dist: Distribution) -> bool: """ Return True if given Distribution object is installed locally (i.e. within current virtualenv). @@ -383,16 +339,14 @@ def dist_is_local(dist): return is_local(dist_location(dist)) -def dist_in_usersite(dist): - # type: (Distribution) -> bool +def dist_in_usersite(dist: Distribution) -> bool: """ Return True if given Distribution is installed in user site. """ return dist_location(dist).startswith(normalize_path(user_site)) -def dist_in_site_packages(dist): - # type: (Distribution) -> bool +def dist_in_site_packages(dist: Distribution) -> bool: """ Return True if given Distribution is installed in sysconfig.get_python_lib(). @@ -400,124 +354,24 @@ def dist_in_site_packages(dist): return dist_location(dist).startswith(normalize_path(site_packages)) -def dist_is_editable(dist): - # type: (Distribution) -> bool - """ - Return True if given Distribution is an editable install. - """ - for path_item in sys.path: - egg_link = os.path.join(path_item, dist.project_name + '.egg-link') - if os.path.isfile(egg_link): - return True - return False - - -def get_installed_distributions( - local_only=True, # type: bool - skip=stdlib_pkgs, # type: Container[str] - include_editables=True, # type: bool - editables_only=False, # type: bool - user_only=False, # type: bool - paths=None # type: Optional[List[str]] -): - # type: (...) -> List[Distribution] - """ - Return a list of installed Distribution objects. - - If ``local_only`` is True (default), only return installations - local to the current virtualenv, if in a virtualenv. - - ``skip`` argument is an iterable of lower-case project names to - ignore; defaults to stdlib_pkgs - - If ``include_editables`` is False, don't report editables. - - If ``editables_only`` is True , only report editables. +def get_distribution(req_name: str) -> Optional[Distribution]: + """Given a requirement name, return the installed Distribution object. - If ``user_only`` is True , only report installations in the user - site directory. + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. - If ``paths`` is set, only report the distributions present at the - specified list of locations. + Left for compatibility until direct pkg_resources uses are refactored out. """ - if paths: - working_set = pkg_resources.WorkingSet(paths) - else: - working_set = pkg_resources.working_set + from pip._internal.metadata import get_default_environment + from pip._internal.metadata.pkg_resources import Distribution as _Dist - if local_only: - local_test = dist_is_local - else: - def local_test(d): - return True - - if include_editables: - def editable_test(d): - return True - else: - def editable_test(d): - return not dist_is_editable(d) - - if editables_only: - def editables_only_test(d): - return dist_is_editable(d) - else: - def editables_only_test(d): - return True - - if user_only: - user_test = dist_in_usersite - else: - def user_test(d): - return True - - return [d for d in working_set - if local_test(d) and - d.key not in skip and - editable_test(d) and - editables_only_test(d) and - user_test(d) - ] - - -def egg_link_path(dist): - # type: (Distribution) -> Optional[str] - """ - Return the path for the .egg-link file if it exists, otherwise, None. - - There's 3 scenarios: - 1) not in a virtualenv - try to find in site.USER_SITE, then site_packages - 2) in a no-global virtualenv - try to find in site_packages - 3) in a yes-global virtualenv - try to find in site_packages, then site.USER_SITE - (don't look in global location) - - For #1 and #3, there could be odd cases, where there's an egg-link in 2 - locations. - - This method will just return the first one found. - """ - sites = [] - if running_under_virtualenv(): - sites.append(site_packages) - if not virtualenv_no_global() and user_site: - sites.append(user_site) - else: - if user_site: - sites.append(user_site) - sites.append(site_packages) - - for site in sites: - egglink = os.path.join(site, dist.project_name) + '.egg-link' - if os.path.isfile(egglink): - return egglink - return None + dist = get_default_environment().get_distribution(req_name) + if dist is None: + return None + return cast(_Dist, dist)._dist -def dist_location(dist): - # type: (Distribution) -> str +def dist_location(dist: Distribution) -> str: """ Get the site-packages location of this distribution. Generally this is dist.location, except in the case of develop-installed @@ -526,51 +380,33 @@ def dist_location(dist): The returned location is normalized (in particular, with symlinks removed). """ - egg_link = egg_link_path(dist) + egg_link = egg_link_path_from_location(dist.project_name) if egg_link: return normalize_path(egg_link) return normalize_path(dist.location) -def write_output(msg, *args): - # type: (str, str) -> None +def write_output(msg: Any, *args: Any) -> None: logger.info(msg, *args) -class FakeFile(object): - """Wrap a list of lines in an object with readline() to make - ConfigParser happy.""" - def __init__(self, lines): - self._gen = (l for l in lines) - - def readline(self): - try: - try: - return next(self._gen) - except NameError: - return self._gen.next() - except StopIteration: - return '' - - def __iter__(self): - return self._gen - - class StreamWrapper(StringIO): + orig_stream: TextIO = None @classmethod - def from_stream(cls, orig_stream): + def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper": cls.orig_stream = orig_stream return cls() # compileall.compile_dir() needs stdout.encoding to print to stdout + # https://github.com/python/mypy/issues/4125 @property - def encoding(self): + def encoding(self): # type: ignore return self.orig_stream.encoding @contextlib.contextmanager -def captured_output(stream_name): +def captured_output(stream_name: str) -> Iterator[StreamWrapper]: """Return a context manager used by captured_stdout/stdin/stderr that temporarily replaces the sys stream *stream_name* with a StringIO. @@ -584,7 +420,7 @@ def captured_output(stream_name): setattr(sys, stream_name, orig_stdout) -def captured_stdout(): +def captured_stdout() -> ContextManager[StreamWrapper]: """Capture the output of sys.stdout: with captured_stdout() as stdout: @@ -593,131 +429,85 @@ def captured_stdout(): Taken from Lib/support/__init__.py in the CPython repo. """ - return captured_output('stdout') + return captured_output("stdout") -def captured_stderr(): +def captured_stderr() -> ContextManager[StreamWrapper]: """ See captured_stdout(). """ - return captured_output('stderr') - - -class cached_property(object): - """A property that is only computed once per instance and then replaces - itself with an ordinary attribute. Deleting the attribute resets the - property. - - Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 - """ - - def __init__(self, func): - self.__doc__ = getattr(func, '__doc__') - self.func = func - - def __get__(self, obj, cls): - if obj is None: - # We're being accessed from the class itself, not from an object - return self - value = obj.__dict__[self.func.__name__] = self.func(obj) - return value - - -def get_installed_version(dist_name, working_set=None): - """Get the installed version of dist_name avoiding pkg_resources cache""" - # Create a requirement that we'll look for inside of setuptools. - req = pkg_resources.Requirement.parse(dist_name) - - if working_set is None: - # We want to avoid having this cached, so we need to construct a new - # working set each time. - working_set = pkg_resources.WorkingSet() - - # Get the installed distribution from our working set - dist = working_set.find(req) - - # Check to see if we got an installed distribution or not, if we did - # we want to return it's version. - return dist.version if dist else None - - -def consume(iterator): - """Consume an iterable at C speed.""" - deque(iterator, maxlen=0) + return captured_output("stderr") # Simulates an enum -def enum(*sequential, **named): +def enum(*sequential: Any, **named: Any) -> Type[Any]: enums = dict(zip(sequential, range(len(sequential))), **named) reverse = {value: key for key, value in enums.items()} - enums['reverse_mapping'] = reverse - return type('Enum', (), enums) + enums["reverse_mapping"] = reverse + return type("Enum", (), enums) -def build_netloc(host, port): - # type: (str, Optional[int]) -> str +def build_netloc(host: str, port: Optional[int]) -> str: """ Build a netloc from a host-port pair """ if port is None: return host - if ':' in host: + if ":" in host: # Only wrap host with square brackets when it is IPv6 - host = '[{}]'.format(host) - return '{}:{}'.format(host, port) + host = f"[{host}]" + return f"{host}:{port}" -def build_url_from_netloc(netloc, scheme='https'): - # type: (str, str) -> str +def build_url_from_netloc(netloc: str, scheme: str = "https") -> str: """ Build a full URL from a netloc. """ - if netloc.count(':') >= 2 and '@' not in netloc and '[' not in netloc: + if netloc.count(":") >= 2 and "@" not in netloc and "[" not in netloc: # It must be a bare IPv6 address, so wrap it with brackets. - netloc = '[{}]'.format(netloc) - return '{}://{}'.format(scheme, netloc) + netloc = f"[{netloc}]" + return f"{scheme}://{netloc}" -def parse_netloc(netloc): - # type: (str) -> Tuple[str, Optional[int]] +def parse_netloc(netloc: str) -> Tuple[str, Optional[int]]: """ Return the host-port pair from a netloc. """ url = build_url_from_netloc(netloc) - parsed = urllib_parse.urlparse(url) + parsed = urllib.parse.urlparse(url) return parsed.hostname, parsed.port -def split_auth_from_netloc(netloc): +def split_auth_from_netloc(netloc: str) -> NetlocTuple: """ Parse out and remove the auth information from a netloc. Returns: (netloc, (username, password)). """ - if '@' not in netloc: + if "@" not in netloc: return netloc, (None, None) # Split from the right because that's how urllib.parse.urlsplit() # behaves if more than one @ is present (which can be checked using # the password attribute of urlsplit()'s return value). - auth, netloc = netloc.rsplit('@', 1) - if ':' in auth: + auth, netloc = netloc.rsplit("@", 1) + pw: Optional[str] = None + if ":" in auth: # Split from the left because that's how urllib.parse.urlsplit() # behaves if more than one : is present (which again can be checked # using the password attribute of the return value) - user_pass = auth.split(':', 1) + user, pw = auth.split(":", 1) else: - user_pass = auth, None + user, pw = auth, None - user_pass = tuple( - None if x is None else urllib_unquote(x) for x in user_pass - ) + user = urllib.parse.unquote(user) + if pw is not None: + pw = urllib.parse.unquote(pw) - return netloc, user_pass + return netloc, (user, pw) -def redact_netloc(netloc): - # type: (str) -> str +def redact_netloc(netloc: str) -> str: """ Replace the sensitive data in a netloc with "****", if it exists. @@ -729,17 +519,19 @@ def redact_netloc(netloc): if user is None: return netloc if password is None: - user = '****' - password = '' + user = "****" + password = "" else: - user = urllib_parse.quote(user) - password = ':****' - return '{user}{password}@{netloc}'.format(user=user, - password=password, - netloc=netloc) + user = urllib.parse.quote(user) + password = ":****" + return "{user}{password}@{netloc}".format( + user=user, password=password, netloc=netloc + ) -def _transform_url(url, transform_netloc): +def _transform_url( + url: str, transform_netloc: Callable[[str], Tuple[Any, ...]] +) -> Tuple[str, NetlocTuple]: """Transform and replace netloc in a url. transform_netloc is a function taking the netloc and returning a @@ -749,26 +541,23 @@ def _transform_url(url, transform_netloc): Returns a tuple containing the transformed url as item 0 and the original tuple returned by transform_netloc as item 1. """ - purl = urllib_parse.urlsplit(url) + purl = urllib.parse.urlsplit(url) netloc_tuple = transform_netloc(purl.netloc) # stripped url - url_pieces = ( - purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment - ) - surl = urllib_parse.urlunsplit(url_pieces) - return surl, netloc_tuple + url_pieces = (purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment) + surl = urllib.parse.urlunsplit(url_pieces) + return surl, cast("NetlocTuple", netloc_tuple) -def _get_netloc(netloc): +def _get_netloc(netloc: str) -> NetlocTuple: return split_auth_from_netloc(netloc) -def _redact_netloc(netloc): +def _redact_netloc(netloc: str) -> Tuple[str]: return (redact_netloc(netloc),) -def split_auth_netloc_from_url(url): - # type: (str) -> Tuple[str, str, Tuple[str, str]] +def split_auth_netloc_from_url(url: str) -> Tuple[str, str, Tuple[str, str]]: """ Parse a url into separate netloc, auth, and url with no auth. @@ -778,68 +567,49 @@ def split_auth_netloc_from_url(url): return url_without_auth, netloc, auth -def remove_auth_from_url(url): - # type: (str) -> str +def remove_auth_from_url(url: str) -> str: """Return a copy of url with 'username:password@' removed.""" # username/pass params are passed to subversion through flags # and are not recognized in the url. return _transform_url(url, _get_netloc)[0] -def redact_auth_from_url(url): - # type: (str) -> str +def redact_auth_from_url(url: str) -> str: """Replace the password in a given url with ****.""" return _transform_url(url, _redact_netloc)[0] -class HiddenText(object): - def __init__( - self, - secret, # type: str - redacted, # type: str - ): - # type: (...) -> None +class HiddenText: + def __init__(self, secret: str, redacted: str) -> None: self.secret = secret self.redacted = redacted - def __repr__(self): - # type: (...) -> str - return ''.format(str(self)) + def __repr__(self) -> str: + return "".format(str(self)) - def __str__(self): - # type: (...) -> str + def __str__(self) -> str: return self.redacted # This is useful for testing. - def __eq__(self, other): - # type: (Any) -> bool + def __eq__(self, other: Any) -> bool: if type(self) != type(other): return False # The string being used for redaction doesn't also have to match, # just the raw, original string. - return (self.secret == other.secret) + return self.secret == other.secret - # We need to provide an explicit __ne__ implementation for Python 2. - # TODO: remove this when we drop PY2 support. - def __ne__(self, other): - # type: (Any) -> bool - return not self == other +def hide_value(value: str) -> HiddenText: + return HiddenText(value, redacted="****") -def hide_value(value): - # type: (str) -> HiddenText - return HiddenText(value, redacted='****') - -def hide_url(url): - # type: (str) -> HiddenText +def hide_url(url: str) -> HiddenText: redacted = redact_auth_from_url(url) return HiddenText(url, redacted=redacted) -def protect_pip_from_modification_on_windows(modifying_pip): - # type: (bool) -> None +def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None: """Protection of pip.exe from modification on Windows On Windows, any operation modifying pip should be run as: @@ -848,48 +618,41 @@ def protect_pip_from_modification_on_windows(modifying_pip): pip_names = [ "pip.exe", "pip{}.exe".format(sys.version_info[0]), - "pip{}.{}.exe".format(*sys.version_info[:2]) + "pip{}.{}.exe".format(*sys.version_info[:2]), ] # See https://github.com/pypa/pip/issues/1299 for more discussion should_show_use_python_msg = ( - modifying_pip and - WINDOWS and - os.path.basename(sys.argv[0]) in pip_names + modifying_pip and WINDOWS and os.path.basename(sys.argv[0]) in pip_names ) if should_show_use_python_msg: - new_command = [ - sys.executable, "-m", "pip" - ] + sys.argv[1:] + new_command = [sys.executable, "-m", "pip"] + sys.argv[1:] raise CommandError( - 'To modify pip, please run the following command:\n{}' - .format(" ".join(new_command)) + "To modify pip, please run the following command:\n{}".format( + " ".join(new_command) + ) ) -def is_console_interactive(): - # type: () -> bool - """Is this console interactive? - """ +def is_console_interactive() -> bool: + """Is this console interactive?""" return sys.stdin is not None and sys.stdin.isatty() -def hash_file(path, blocksize=1 << 20): - # type: (str, int) -> Tuple[Any, int] - """Return (hash, length) for path using hashlib.sha256() - """ +def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]: + """Return (hash, length) for path using hashlib.sha256()""" h = hashlib.sha256() length = 0 - with open(path, 'rb') as f: + with open(path, "rb") as f: for block in read_chunks(f, size=blocksize): length += len(block) h.update(block) return h, length -def is_wheel_installed(): +def is_wheel_installed() -> bool: """ Return whether the wheel package is installed. """ @@ -901,8 +664,7 @@ def is_wheel_installed(): return True -def pairwise(iterable): - # type: (Iterable[Any]) -> Iterator[Tuple[Any, Any]] +def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]: """ Return paired elements. @@ -911,3 +673,17 @@ def pairwise(iterable): """ iterable = iter(iterable) return zip_longest(iterable, iterable) + + +def partition( + pred: Callable[[T], bool], + iterable: Iterable[T], +) -> Tuple[Iterable[T], Iterable[T]]: + """ + Use a predicate to partition entries into false entries and true entries, + like + + partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + """ + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) diff --git a/venv/Lib/site-packages/pip/_internal/utils/models.py b/venv/Lib/site-packages/pip/_internal/utils/models.py index 29e1441153b63446220a5e1867e691183e0d22d7..b6bb21a8b26680b38c3af8278ed139b6628356c5 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/models.py +++ b/venv/Lib/site-packages/pip/_internal/utils/models.py @@ -1,41 +1,38 @@ """Utilities for defining models """ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False import operator +from typing import Any, Callable, Type -class KeyBasedCompareMixin(object): - """Provides comparison capabilities that is based on a key - """ +class KeyBasedCompareMixin: + """Provides comparison capabilities that is based on a key""" - def __init__(self, key, defining_class): + __slots__ = ["_compare_key", "_defining_class"] + + def __init__(self, key: Any, defining_class: Type["KeyBasedCompareMixin"]) -> None: self._compare_key = key self._defining_class = defining_class - def __hash__(self): + def __hash__(self) -> int: return hash(self._compare_key) - def __lt__(self, other): + def __lt__(self, other: Any) -> bool: return self._compare(other, operator.__lt__) - def __le__(self, other): + def __le__(self, other: Any) -> bool: return self._compare(other, operator.__le__) - def __gt__(self, other): + def __gt__(self, other: Any) -> bool: return self._compare(other, operator.__gt__) - def __ge__(self, other): + def __ge__(self, other: Any) -> bool: return self._compare(other, operator.__ge__) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return self._compare(other, operator.__eq__) - def __ne__(self, other): - return self._compare(other, operator.__ne__) - - def _compare(self, other, method): + def _compare(self, other: Any, method: Callable[[Any, Any], bool]) -> bool: if not isinstance(other, self._defining_class): return NotImplemented diff --git a/venv/Lib/site-packages/pip/_internal/utils/packaging.py b/venv/Lib/site-packages/pip/_internal/utils/packaging.py index 68aa86edbf012c68ceadbe67e21e5d6c9ebbc0ab..f100473e647196099d5ee4dc3e66fcc4a1837a9f 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/packaging.py +++ b/venv/Lib/site-packages/pip/_internal/utils/packaging.py @@ -1,26 +1,23 @@ -from __future__ import absolute_import - +import functools import logging +from email.message import Message from email.parser import FeedParser +from typing import Optional, Tuple from pip._vendor import pkg_resources from pip._vendor.packaging import specifiers, version +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.pkg_resources import Distribution from pip._internal.exceptions import NoneMetadataError from pip._internal.utils.misc import display_path -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Optional, Tuple - from email.message import Message - from pip._vendor.pkg_resources import Distribution - logger = logging.getLogger(__name__) -def check_requires_python(requires_python, version_info): - # type: (Optional[str], Tuple[int, ...]) -> bool +def check_requires_python( + requires_python: Optional[str], version_info: Tuple[int, ...] +) -> bool: """ Check if the given Python version matches a "Requires-Python" specifier. @@ -37,26 +34,26 @@ def check_requires_python(requires_python, version_info): return True requires_python_specifier = specifiers.SpecifierSet(requires_python) - python_version = version.parse('.'.join(map(str, version_info))) + python_version = version.parse(".".join(map(str, version_info))) return python_version in requires_python_specifier -def get_metadata(dist): - # type: (Distribution) -> Message +def get_metadata(dist: Distribution) -> Message: """ :raises NoneMetadataError: if the distribution reports `has_metadata()` True but `get_metadata()` returns None. """ - metadata_name = 'METADATA' - if (isinstance(dist, pkg_resources.DistInfoDistribution) and - dist.has_metadata(metadata_name)): + metadata_name = "METADATA" + if isinstance(dist, pkg_resources.DistInfoDistribution) and dist.has_metadata( + metadata_name + ): metadata = dist.get_metadata(metadata_name) - elif dist.has_metadata('PKG-INFO'): - metadata_name = 'PKG-INFO' + elif dist.has_metadata("PKG-INFO"): + metadata_name = "PKG-INFO" metadata = dist.get_metadata(metadata_name) else: logger.warning("No metadata found in %s", display_path(dist.location)) - metadata = '' + metadata = "" if metadata is None: raise NoneMetadataError(dist, metadata_name) @@ -68,27 +65,20 @@ def get_metadata(dist): return feed_parser.close() -def get_requires_python(dist): - # type: (pkg_resources.Distribution) -> Optional[str] - """ - Return the "Requires-Python" metadata for a distribution, or None - if not present. - """ - pkg_info_dict = get_metadata(dist) - requires_python = pkg_info_dict.get('Requires-Python') - - if requires_python is not None: - # Convert to a str to satisfy the type checker, since requires_python - # can be a Header object. - requires_python = str(requires_python) - - return requires_python - - -def get_installer(dist): - # type: (Distribution) -> str - if dist.has_metadata('INSTALLER'): - for line in dist.get_metadata_lines('INSTALLER'): +def get_installer(dist: Distribution) -> str: + if dist.has_metadata("INSTALLER"): + for line in dist.get_metadata_lines("INSTALLER"): if line.strip(): return line.strip() - return '' + return "" + + +@functools.lru_cache(maxsize=512) +def get_requirement(req_string: str) -> Requirement: + """Construct a packaging.Requirement object with caching""" + # Parsing requirement strings is expensive, and is also expected to happen + # with a low diversity of different arguments (at least relative the number + # constructed). This method adds a cache to requirement object creation to + # minimize repeated parsing of the same string to construct equivalent + # Requirement objects. + return Requirement(req_string) diff --git a/venv/Lib/site-packages/pip/_internal/utils/pkg_resources.py b/venv/Lib/site-packages/pip/_internal/utils/pkg_resources.py index 0bc129acc6ab582eb087be7ee186c554dc5feba1..bd846aa97bcea66f0d113c235218d5c56ec95c8f 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/pkg_resources.py +++ b/venv/Lib/site-packages/pip/_internal/utils/pkg_resources.py @@ -1,44 +1,33 @@ -from pip._vendor.pkg_resources import yield_lines -from pip._vendor.six import ensure_str +from typing import Dict, Iterable, List -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._vendor.pkg_resources import yield_lines -if MYPY_CHECK_RUNNING: - from typing import Dict, Iterable, List +class DictMetadata: + """IMetadataProvider that reads metadata files from a dictionary.""" -class DictMetadata(object): - """IMetadataProvider that reads metadata files from a dictionary. - """ - def __init__(self, metadata): - # type: (Dict[str, bytes]) -> None + def __init__(self, metadata: Dict[str, bytes]) -> None: self._metadata = metadata - def has_metadata(self, name): - # type: (str) -> bool + def has_metadata(self, name: str) -> bool: return name in self._metadata - def get_metadata(self, name): - # type: (str) -> str + def get_metadata(self, name: str) -> str: try: - return ensure_str(self._metadata[name]) + return self._metadata[name].decode() except UnicodeDecodeError as e: # Mirrors handling done in pkg_resources.NullProvider. - e.reason += " in {} file".format(name) + e.reason += f" in {name} file" raise - def get_metadata_lines(self, name): - # type: (str) -> Iterable[str] + def get_metadata_lines(self, name: str) -> Iterable[str]: return yield_lines(self.get_metadata(name)) - def metadata_isdir(self, name): - # type: (str) -> bool + def metadata_isdir(self, name: str) -> bool: return False - def metadata_listdir(self, name): - # type: (str) -> List[str] + def metadata_listdir(self, name: str) -> List[str]: return [] - def run_script(self, script_name, namespace): - # type: (str, str) -> None + def run_script(self, script_name: str, namespace: str) -> None: pass diff --git a/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py b/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py index 2a664b00703768bca8034bb0b514caccfd6883ba..9d65ceba4ab041d65c07b3501f4eabe2f5654843 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py +++ b/venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py @@ -1,9 +1,5 @@ import sys - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional, Sequence +from typing import List, Optional, Sequence # Shim to wrap setup.py invocation with setuptools # @@ -12,21 +8,22 @@ if MYPY_CHECK_RUNNING: # invoking via the shim. This avoids e.g. the following manifest_maker # warning: "warning: manifest_maker: standard file '-c' not found". _SETUPTOOLS_SHIM = ( - "import sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};" - "f=getattr(tokenize, 'open', open)(__file__);" - "code=f.read().replace('\\r\\n', '\\n');" + "import io, os, sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};" + "f = getattr(tokenize, 'open', open)(__file__) " + "if os.path.exists(__file__) " + "else io.StringIO('from setuptools import setup; setup()');" + "code = f.read().replace('\\r\\n', '\\n');" "f.close();" "exec(compile(code, __file__, 'exec'))" ) def make_setuptools_shim_args( - setup_py_path, # type: str - global_options=None, # type: Sequence[str] - no_user_config=False, # type: bool - unbuffered_output=False # type: bool -): - # type: (...) -> List[str] + setup_py_path: str, + global_options: Sequence[str] = None, + no_user_config: bool = False, + unbuffered_output: bool = False, +) -> List[str]: """ Get setuptools command arguments with shim wrapped setup file invocation. @@ -48,20 +45,17 @@ def make_setuptools_shim_args( def make_setuptools_bdist_wheel_args( - setup_py_path, # type: str - global_options, # type: Sequence[str] - build_options, # type: Sequence[str] - destination_dir, # type: str -): - # type: (...) -> List[str] + setup_py_path: str, + global_options: Sequence[str], + build_options: Sequence[str], + destination_dir: str, +) -> List[str]: # NOTE: Eventually, we'd want to also -S to the flags here, when we're # isolating. Currently, it breaks Python in virtualenvs, because it # relies on site.py to find parts of the standard library outside the # virtualenv. args = make_setuptools_shim_args( - setup_py_path, - global_options=global_options, - unbuffered_output=True + setup_py_path, global_options=global_options, unbuffered_output=True ) args += ["bdist_wheel", "-d", destination_dir] args += build_options @@ -69,29 +63,25 @@ def make_setuptools_bdist_wheel_args( def make_setuptools_clean_args( - setup_py_path, # type: str - global_options, # type: Sequence[str] -): - # type: (...) -> List[str] + setup_py_path: str, + global_options: Sequence[str], +) -> List[str]: args = make_setuptools_shim_args( - setup_py_path, - global_options=global_options, - unbuffered_output=True + setup_py_path, global_options=global_options, unbuffered_output=True ) args += ["clean", "--all"] return args def make_setuptools_develop_args( - setup_py_path, # type: str - global_options, # type: Sequence[str] - install_options, # type: Sequence[str] - no_user_config, # type: bool - prefix, # type: Optional[str] - home, # type: Optional[str] - use_user_site, # type: bool -): - # type: (...) -> List[str] + setup_py_path: str, + global_options: Sequence[str], + install_options: Sequence[str], + no_user_config: bool, + prefix: Optional[str], + home: Optional[str], + use_user_site: bool, +) -> List[str]: assert not (use_user_site and prefix) args = make_setuptools_shim_args( @@ -107,7 +97,7 @@ def make_setuptools_develop_args( if prefix: args += ["--prefix", prefix] if home is not None: - args += ["--home", home] + args += ["--install-dir", home] if use_user_site: args += ["--user", "--prefix="] @@ -116,14 +106,11 @@ def make_setuptools_develop_args( def make_setuptools_egg_info_args( - setup_py_path, # type: str - egg_info_dir, # type: Optional[str] - no_user_config, # type: bool -): - # type: (...) -> List[str] - args = make_setuptools_shim_args( - setup_py_path, no_user_config=no_user_config - ) + setup_py_path: str, + egg_info_dir: Optional[str], + no_user_config: bool, +) -> List[str]: + args = make_setuptools_shim_args(setup_py_path, no_user_config=no_user_config) args += ["egg_info"] @@ -134,19 +121,18 @@ def make_setuptools_egg_info_args( def make_setuptools_install_args( - setup_py_path, # type: str - global_options, # type: Sequence[str] - install_options, # type: Sequence[str] - record_filename, # type: str - root, # type: Optional[str] - prefix, # type: Optional[str] - header_dir, # type: Optional[str] - home, # type: Optional[str] - use_user_site, # type: bool - no_user_config, # type: bool - pycompile # type: bool -): - # type: (...) -> List[str] + setup_py_path: str, + global_options: Sequence[str], + install_options: Sequence[str], + record_filename: str, + root: Optional[str], + prefix: Optional[str], + header_dir: Optional[str], + home: Optional[str], + use_user_site: bool, + no_user_config: bool, + pycompile: bool, +) -> List[str]: assert not (use_user_site and prefix) assert not (use_user_site and root) @@ -154,7 +140,7 @@ def make_setuptools_install_args( setup_py_path, global_options=global_options, no_user_config=no_user_config, - unbuffered_output=True + unbuffered_output=True, ) args += ["install", "--record", record_filename] args += ["--single-version-externally-managed"] diff --git a/venv/Lib/site-packages/pip/_internal/utils/subprocess.py b/venv/Lib/site-packages/pip/_internal/utils/subprocess.py index 55c82daea7c3aac42f8082bda1a13db562183115..f6e8b219a6ff7941a0ed348e95af7ead9be86c06 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/subprocess.py +++ b/venv/Lib/site-packages/pip/_internal/utils/subprocess.py @@ -1,38 +1,40 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -from __future__ import absolute_import - import logging import os +import shlex import subprocess - -from pip._vendor.six.moves import shlex_quote +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Iterable, + List, + Mapping, + Optional, + Union, +) from pip._internal.cli.spinners import SpinnerInterface, open_spinner -from pip._internal.exceptions import InstallationError -from pip._internal.utils.compat import console_to_str, str_to_display -from pip._internal.utils.logging import subprocess_logger -from pip._internal.utils.misc import HiddenText, path_to_display -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.utils.logging import VERBOSE, subprocess_logger +from pip._internal.utils.misc import HiddenText -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Callable, Iterable, List, Mapping, Optional, Text, Union, - ) +if TYPE_CHECKING: + # Literal was introduced in Python 3.8. + # + # TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7. + from typing import Literal - CommandArgs = List[Union[str, HiddenText]] +CommandArgs = List[Union[str, HiddenText]] -LOG_DIVIDER = '----------------------------------------' +LOG_DIVIDER = "----------------------------------------" -def make_command(*args): - # type: (Union[str, HiddenText, CommandArgs]) -> CommandArgs +def make_command(*args: Union[str, HiddenText, CommandArgs]) -> CommandArgs: """ Create a CommandArgs object. """ - command_args = [] # type: CommandArgs + command_args: CommandArgs = [] for arg in args: # Check for list instead of CommandArgs since CommandArgs is # only known during type-checking. @@ -45,8 +47,7 @@ def make_command(*args): return command_args -def format_command_args(args): - # type: (Union[List[str], CommandArgs]) -> str +def format_command_args(args: Union[List[str], CommandArgs]) -> str: """ Format command arguments for display. """ @@ -55,29 +56,25 @@ def format_command_args(args): # this can trigger a UnicodeDecodeError in Python 2 if the argument # has type unicode and includes a non-ascii character. (The type # checker doesn't ensure the annotations are correct in all cases.) - return ' '.join( - shlex_quote(str(arg)) if isinstance(arg, HiddenText) - else shlex_quote(arg) for arg in args + return " ".join( + shlex.quote(str(arg)) if isinstance(arg, HiddenText) else shlex.quote(arg) + for arg in args ) -def reveal_command_args(args): - # type: (Union[List[str], CommandArgs]) -> List[str] +def reveal_command_args(args: Union[List[str], CommandArgs]) -> List[str]: """ Return the arguments in their raw, unredacted form. """ - return [ - arg.secret if isinstance(arg, HiddenText) else arg for arg in args - ] + return [arg.secret if isinstance(arg, HiddenText) else arg for arg in args] def make_subprocess_output_error( - cmd_args, # type: Union[List[str], CommandArgs] - cwd, # type: Optional[str] - lines, # type: List[Text] - exit_status, # type: int -): - # type: (...) -> Text + cmd_args: Union[List[str], CommandArgs], + cwd: Optional[str], + lines: List[str], + exit_status: int, +) -> str: """ Create and return the error message to use to log a subprocess error with command output. @@ -85,27 +82,21 @@ def make_subprocess_output_error( :param lines: A list of lines, each ending with a newline. """ command = format_command_args(cmd_args) - # Convert `command` and `cwd` to text (unicode in Python 2) so we can use - # them as arguments in the unicode format string below. This avoids - # "UnicodeDecodeError: 'ascii' codec can't decode byte ..." in Python 2 - # if either contains a non-ascii character. - command_display = str_to_display(command, desc='command bytes') - cwd_display = path_to_display(cwd) # We know the joined output value ends in a newline. - output = ''.join(lines) + output = "".join(lines) msg = ( # Use a unicode string to avoid "UnicodeEncodeError: 'ascii' # codec can't encode character ..." in Python 2 when a format # argument (e.g. `output`) has a non-ascii character. - u'Command errored out with exit status {exit_status}:\n' - ' command: {command_display}\n' - ' cwd: {cwd_display}\n' - 'Complete output ({line_count} lines):\n{output}{divider}' + "Command errored out with exit status {exit_status}:\n" + " command: {command_display}\n" + " cwd: {cwd_display}\n" + "Complete output ({line_count} lines):\n{output}{divider}" ).format( exit_status=exit_status, - command_display=command_display, - cwd_display=cwd_display, + command_display=command, + cwd_display=cwd, line_count=len(lines), output=output, divider=LOG_DIVIDER, @@ -114,18 +105,18 @@ def make_subprocess_output_error( def call_subprocess( - cmd, # type: Union[List[str], CommandArgs] - show_stdout=False, # type: bool - cwd=None, # type: Optional[str] - on_returncode='raise', # type: str - extra_ok_returncodes=None, # type: Optional[Iterable[int]] - command_desc=None, # type: Optional[str] - extra_environ=None, # type: Optional[Mapping[str, Any]] - unset_environ=None, # type: Optional[Iterable[str]] - spinner=None, # type: Optional[SpinnerInterface] - log_failed_cmd=True # type: Optional[bool] -): - # type: (...) -> Text + cmd: Union[List[str], CommandArgs], + show_stdout: bool = False, + cwd: Optional[str] = None, + on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", + extra_ok_returncodes: Optional[Iterable[int]] = None, + command_desc: Optional[str] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + unset_environ: Optional[Iterable[str]] = None, + spinner: Optional[SpinnerInterface] = None, + log_failed_cmd: Optional[bool] = True, + stdout_only: Optional[bool] = False, +) -> str: """ Args: show_stdout: if true, use INFO to log the subprocess's stderr and @@ -135,6 +126,9 @@ def call_subprocess( unset_environ: an iterable of environment variable names to unset prior to calling subprocess.Popen(). log_failed_cmd: if false, failed commands are not logged, only raised. + stdout_only: if true, return only stdout, else return both. When true, + logging of both stdout and stderr occurs when the subprocess has + terminated, else logging occurs as subprocess output is produced. """ if extra_ok_returncodes is None: extra_ok_returncodes = [] @@ -160,10 +154,10 @@ def call_subprocess( log_subprocess = subprocess_logger.info used_level = logging.INFO else: - # Then log the subprocess output using DEBUG. This also ensures + # Then log the subprocess output using VERBOSE. This also ensures # it will be logged to the log file (aka user_log), if enabled. - log_subprocess = subprocess_logger.debug - used_level = logging.DEBUG + log_subprocess = subprocess_logger.verbose + used_level = VERBOSE # Whether the subprocess will be visible in the console. showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level @@ -185,45 +179,68 @@ def call_subprocess( proc = subprocess.Popen( # Convert HiddenText objects to the underlying str. reveal_command_args(cmd), - stderr=subprocess.STDOUT, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, cwd=cwd, env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT if not stdout_only else subprocess.PIPE, + cwd=cwd, + env=env, + errors="backslashreplace", ) - proc.stdin.close() except Exception as exc: if log_failed_cmd: subprocess_logger.critical( - "Error %s while executing command %s", exc, command_desc, + "Error %s while executing command %s", + exc, + command_desc, ) raise all_output = [] - while True: - # The "line" value is a unicode string in Python 2. - line = console_to_str(proc.stdout.readline()) - if not line: - break - line = line.rstrip() - all_output.append(line + '\n') + if not stdout_only: + assert proc.stdout + assert proc.stdin + proc.stdin.close() + # In this mode, stdout and stderr are in the same pipe. + while True: + line: str = proc.stdout.readline() + if not line: + break + line = line.rstrip() + all_output.append(line + "\n") - # Show the line immediately. - log_subprocess(line) - # Update the spinner. - if use_spinner: - spinner.spin() - try: - proc.wait() - finally: - if proc.stdout: - proc.stdout.close() - proc_had_error = ( - proc.returncode and proc.returncode not in extra_ok_returncodes - ) + # Show the line immediately. + log_subprocess(line) + # Update the spinner. + if use_spinner: + assert spinner + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + output = "".join(all_output) + else: + # In this mode, stdout and stderr are in different pipes. + # We must use communicate() which is the only safe way to read both. + out, err = proc.communicate() + # log line by line to preserve pip log indenting + for out_line in out.splitlines(): + log_subprocess(out_line) + all_output.append(out) + for err_line in err.splitlines(): + log_subprocess(err_line) + all_output.append(err) + output = out + + proc_had_error = proc.returncode and proc.returncode not in extra_ok_returncodes if use_spinner: + assert spinner if proc_had_error: spinner.finish("error") else: spinner.finish("done") if proc_had_error: - if on_returncode == 'raise': + if on_returncode == "raise": if not showing_subprocess and log_failed_cmd: # Then the subprocess streams haven't been logged to the # console yet. @@ -234,26 +251,22 @@ def call_subprocess( exit_status=proc.returncode, ) subprocess_logger.error(msg) - exc_msg = ( - 'Command errored out with exit status {}: {} ' - 'Check the logs for full command output.' - ).format(proc.returncode, command_desc) - raise InstallationError(exc_msg) - elif on_returncode == 'warn': + raise InstallationSubprocessError(proc.returncode, command_desc) + elif on_returncode == "warn": subprocess_logger.warning( - 'Command "{}" had error code {} in {}'.format( - command_desc, proc.returncode, cwd) + 'Command "%s" had error code %s in %s', + command_desc, + proc.returncode, + cwd, ) - elif on_returncode == 'ignore': + elif on_returncode == "ignore": pass else: - raise ValueError('Invalid value: on_returncode={!r}'.format( - on_returncode)) - return ''.join(all_output) + raise ValueError(f"Invalid value: on_returncode={on_returncode!r}") + return output -def runner_with_spinner_message(message): - # type: (str) -> Callable[..., None] +def runner_with_spinner_message(message: str) -> Callable[..., None]: """Provide a subprocess_runner that shows a spinner message. Intended for use with for pep517's Pep517HookCaller. Thus, the runner has @@ -261,11 +274,10 @@ def runner_with_spinner_message(message): """ def runner( - cmd, # type: List[str] - cwd=None, # type: Optional[str] - extra_environ=None # type: Optional[Mapping[str, Any]] - ): - # type: (...) -> None + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + ) -> None: with open_spinner(message) as spinner: call_subprocess( cmd, diff --git a/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py b/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py index 201ba6d9811ea51cd6e669c1dd5455d83d2c9d05..442679a758b7b4b965ce3e615a243a7a6d318b4e 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py +++ b/venv/Lib/site-packages/pip/_internal/utils/temp_dir.py @@ -1,25 +1,17 @@ -from __future__ import absolute_import - import errno import itertools import logging import os.path import tempfile -from contextlib import contextmanager - -from pip._vendor.contextlib2 import ExitStack +from contextlib import ExitStack, contextmanager +from typing import Any, Dict, Iterator, Optional, TypeVar, Union from pip._internal.utils.misc import enum, rmtree -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Any, Dict, Iterator, Optional, TypeVar, Union - - _T = TypeVar('_T', bound='TempDirectory') - logger = logging.getLogger(__name__) +_T = TypeVar("_T", bound="TempDirectory") + # Kinds of temporary directories. Only needed for ones that are # globally-managed. @@ -30,12 +22,11 @@ tempdir_kinds = enum( ) -_tempdir_manager = None # type: Optional[ExitStack] +_tempdir_manager: Optional[ExitStack] = None @contextmanager -def global_tempdir_manager(): - # type: () -> Iterator[None] +def global_tempdir_manager() -> Iterator[None]: global _tempdir_manager with ExitStack() as stack: old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack @@ -45,35 +36,30 @@ def global_tempdir_manager(): _tempdir_manager = old_tempdir_manager -class TempDirectoryTypeRegistry(object): - """Manages temp directory behavior - """ +class TempDirectoryTypeRegistry: + """Manages temp directory behavior""" - def __init__(self): - # type: () -> None - self._should_delete = {} # type: Dict[str, bool] + def __init__(self) -> None: + self._should_delete: Dict[str, bool] = {} - def set_delete(self, kind, value): - # type: (str, bool) -> None + def set_delete(self, kind: str, value: bool) -> None: """Indicate whether a TempDirectory of the given kind should be auto-deleted. """ self._should_delete[kind] = value - def get_delete(self, kind): - # type: (str) -> bool + def get_delete(self, kind: str) -> bool: """Get configured auto-delete flag for a given TempDirectory type, default True. """ return self._should_delete.get(kind, True) -_tempdir_registry = None # type: Optional[TempDirectoryTypeRegistry] +_tempdir_registry: Optional[TempDirectoryTypeRegistry] = None @contextmanager -def tempdir_registry(): - # type: () -> Iterator[TempDirectoryTypeRegistry] +def tempdir_registry() -> Iterator[TempDirectoryTypeRegistry]: """Provides a scoped global tempdir registry that can be used to dictate whether directories should be deleted. """ @@ -86,14 +72,14 @@ def tempdir_registry(): _tempdir_registry = old_tempdir_registry -class _Default(object): +class _Default: pass _default = _Default() -class TempDirectory(object): +class TempDirectory: """Helper class that owns and cleans up a temporary directory. This class can be used as a context manager or as an OO representation of a @@ -116,12 +102,12 @@ class TempDirectory(object): def __init__( self, - path=None, # type: Optional[str] - delete=_default, # type: Union[bool, None, _Default] - kind="temp", # type: str - globally_managed=False, # type: bool + path: Optional[str] = None, + delete: Union[bool, None, _Default] = _default, + kind: str = "temp", + globally_managed: bool = False, ): - super(TempDirectory, self).__init__() + super().__init__() if delete is _default: if path is not None: @@ -133,6 +119,8 @@ class TempDirectory(object): # tempdir_registry says. delete = None + # The only time we specify path is in for editables where it + # is the value of the --src option. if path is None: path = self._create(kind) @@ -146,23 +134,17 @@ class TempDirectory(object): _tempdir_manager.enter_context(self) @property - def path(self): - # type: () -> str - assert not self._deleted, ( - "Attempted to access deleted path: {}".format(self._path) - ) + def path(self) -> str: + assert not self._deleted, f"Attempted to access deleted path: {self._path}" return self._path - def __repr__(self): - # type: () -> str - return "<{} {!r}>".format(self.__class__.__name__, self.path) + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.path!r}>" - def __enter__(self): - # type: (_T) -> _T + def __enter__(self: _T) -> _T: return self - def __exit__(self, exc, value, tb): - # type: (Any, Any, Any) -> None + def __exit__(self, exc: Any, value: Any, tb: Any) -> None: if self.delete is not None: delete = self.delete elif _tempdir_registry: @@ -173,27 +155,22 @@ class TempDirectory(object): if delete: self.cleanup() - def _create(self, kind): - # type: (str) -> str - """Create a temporary directory and store its path in self.path - """ + def _create(self, kind: str) -> str: + """Create a temporary directory and store its path in self.path""" # We realpath here because some systems have their default tmpdir # symlinked to another directory. This tends to confuse build # scripts, so we canonicalize the path by traversing potential # symlinks here. - path = os.path.realpath( - tempfile.mkdtemp(prefix="pip-{}-".format(kind)) - ) - logger.debug("Created temporary directory: {}".format(path)) + path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-")) + logger.debug("Created temporary directory: %s", path) return path - def cleanup(self): - # type: () -> None - """Remove the temporary directory created and reset state - """ + def cleanup(self) -> None: + """Remove the temporary directory created and reset state""" self._deleted = True - if os.path.exists(self._path): - rmtree(self._path) + if not os.path.exists(self._path): + return + rmtree(self._path) class AdjacentTempDirectory(TempDirectory): @@ -210,6 +187,7 @@ class AdjacentTempDirectory(TempDirectory): (when used as a contextmanager) """ + # The characters that may be used to name the temp directory # We always prepend a ~ and then rotate through these until # a usable name is found. @@ -217,14 +195,12 @@ class AdjacentTempDirectory(TempDirectory): # with leading '-' and invalid metadata LEADING_CHARS = "-~.=%0123456789" - def __init__(self, original, delete=None): - # type: (str, Optional[bool]) -> None - self.original = original.rstrip('/\\') - super(AdjacentTempDirectory, self).__init__(delete=delete) + def __init__(self, original: str, delete: Optional[bool] = None) -> None: + self.original = original.rstrip("/\\") + super().__init__(delete=delete) @classmethod - def _generate_names(cls, name): - # type: (str) -> Iterator[str] + def _generate_names(cls, name: str) -> Iterator[str]: """Generates a series of temporary names. The algorithm replaces the leading characters in the name @@ -234,21 +210,22 @@ class AdjacentTempDirectory(TempDirectory): """ for i in range(1, len(name)): for candidate in itertools.combinations_with_replacement( - cls.LEADING_CHARS, i - 1): - new_name = '~' + ''.join(candidate) + name[i:] + cls.LEADING_CHARS, i - 1 + ): + new_name = "~" + "".join(candidate) + name[i:] if new_name != name: yield new_name # If we make it this far, we will have to make a longer name for i in range(len(cls.LEADING_CHARS)): for candidate in itertools.combinations_with_replacement( - cls.LEADING_CHARS, i): - new_name = '~' + ''.join(candidate) + name + cls.LEADING_CHARS, i + ): + new_name = "~" + "".join(candidate) + name if new_name != name: yield new_name - def _create(self, kind): - # type: (str) -> str + def _create(self, kind: str) -> str: root, name = os.path.split(self.original) for candidate in self._generate_names(name): path = os.path.join(root, candidate) @@ -263,9 +240,7 @@ class AdjacentTempDirectory(TempDirectory): break else: # Final fallback on the default behavior. - path = os.path.realpath( - tempfile.mkdtemp(prefix="pip-{}-".format(kind)) - ) + path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-")) - logger.debug("Created temporary directory: {}".format(path)) + logger.debug("Created temporary directory: %s", path) return path diff --git a/venv/Lib/site-packages/pip/_internal/utils/typing.py b/venv/Lib/site-packages/pip/_internal/utils/typing.py deleted file mode 100644 index 8505a29b15d5f8a3565a52796c4e39cc6b826ffc..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_internal/utils/typing.py +++ /dev/null @@ -1,38 +0,0 @@ -"""For neatly implementing static typing in pip. - -`mypy` - the static type analysis tool we use - uses the `typing` module, which -provides core functionality fundamental to mypy's functioning. - -Generally, `typing` would be imported at runtime and used in that fashion - -it acts as a no-op at runtime and does not have any run-time overhead by -design. - -As it turns out, `typing` is not vendorable - it uses separate sources for -Python 2/Python 3. Thus, this codebase can not expect it to be present. -To work around this, mypy allows the typing import to be behind a False-y -optional to prevent it from running at runtime and type-comments can be used -to remove the need for the types to be accessible directly during runtime. - -This module provides the False-y guard in a nicely named fashion so that a -curious maintainer can reach here to read this. - -In pip, all static-typing related imports should be guarded as follows: - - from pip._internal.utils.typing import MYPY_CHECK_RUNNING - - if MYPY_CHECK_RUNNING: - from typing import ... - -Ref: https://github.com/python/mypy/issues/3216 -""" - -MYPY_CHECK_RUNNING = False - - -if MYPY_CHECK_RUNNING: - from typing import cast -else: - # typing's cast() is needed at runtime, but we don't want to import typing. - # Thus, we use a dummy no-op version, which we tell mypy to ignore. - def cast(type_, value): # type: ignore - return value diff --git a/venv/Lib/site-packages/pip/_internal/utils/unpacking.py b/venv/Lib/site-packages/pip/_internal/utils/unpacking.py index 7252dc217bfaece6fedbaf835cecbb2a06cdcbb0..5f63f974b95d35c8a5aa8c2966591a97242e2bea 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/unpacking.py +++ b/venv/Lib/site-packages/pip/_internal/utils/unpacking.py @@ -1,18 +1,14 @@ """Utilities related archives. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import os import shutil import stat import tarfile import zipfile +from typing import Iterable, List, Optional +from zipfile import ZipInfo from pip._internal.exceptions import InstallationError from pip._internal.utils.filetypes import ( @@ -22,11 +18,6 @@ from pip._internal.utils.filetypes import ( ZIP_EXTENSIONS, ) from pip._internal.utils.misc import ensure_dir -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import Iterable, List, Optional, Text, Union - logger = logging.getLogger(__name__) @@ -35,43 +26,40 @@ SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS try: import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS except ImportError: - logger.debug('bz2 module is not available') + logger.debug("bz2 module is not available") try: # Only for Python 3.3+ import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS except ImportError: - logger.debug('lzma module is not available') + logger.debug("lzma module is not available") -def current_umask(): +def current_umask() -> int: """Get the current umask which involves having to set it temporarily.""" mask = os.umask(0) os.umask(mask) return mask -def split_leading_dir(path): - # type: (Union[str, Text]) -> List[Union[str, Text]] - path = path.lstrip('/').lstrip('\\') - if ( - '/' in path and ( - ('\\' in path and path.find('/') < path.find('\\')) or - '\\' not in path - ) +def split_leading_dir(path: str) -> List[str]: + path = path.lstrip("/").lstrip("\\") + if "/" in path and ( + ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path ): - return path.split('/', 1) - elif '\\' in path: - return path.split('\\', 1) + return path.split("/", 1) + elif "\\" in path: + return path.split("\\", 1) else: - return [path, ''] + return [path, ""] -def has_leading_dir(paths): - # type: (Iterable[Union[str, Text]]) -> bool +def has_leading_dir(paths: Iterable[str]) -> bool: """Returns true if all the paths have the same leading path name (i.e., everything is in one subdirectory in an archive)""" common_prefix = None @@ -86,8 +74,7 @@ def has_leading_dir(paths): return True -def is_within_directory(directory, target): - # type: ((Union[str, Text]), (Union[str, Text])) -> bool +def is_within_directory(directory: str, target: str) -> bool: """ Return true if the absolute path of target is within the directory """ @@ -98,8 +85,22 @@ def is_within_directory(directory, target): return prefix == abs_directory -def unzip_file(filename, location, flatten=True): - # type: (str, str, bool) -> None +def set_extracted_file_to_default_mode_plus_executable(path: str) -> None: + """ + Make file present at path have execute for user/group/world + (chmod +x) is no-op on windows per python docs + """ + os.chmod(path, (0o777 & ~current_umask() | 0o111)) + + +def zip_item_is_executable(info: ZipInfo) -> bool: + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + return bool(mode and stat.S_ISREG(mode) and mode & 0o111) + + +def unzip_file(filename: str, location: str, flatten: bool = True) -> None: """ Unzip the file (with path `filename`) to the destination `location`. All files are written based on system defaults and umask (i.e. permissions are @@ -109,7 +110,7 @@ def unzip_file(filename, location, flatten=True): no-ops per the python docs. """ ensure_dir(location) - zipfp = open(filename, 'rb') + zipfp = open(filename, "rb") try: zip = zipfile.ZipFile(zipfp, allowZip64=True) leading = has_leading_dir(zip.namelist()) and flatten @@ -122,11 +123,11 @@ def unzip_file(filename, location, flatten=True): dir = os.path.dirname(fn) if not is_within_directory(location, fn): message = ( - 'The zip file ({}) has a file ({}) trying to install ' - 'outside target directory ({})' + "The zip file ({}) has a file ({}) trying to install " + "outside target directory ({})" ) raise InstallationError(message.format(filename, fn, location)) - if fn.endswith('/') or fn.endswith('\\'): + if fn.endswith("/") or fn.endswith("\\"): # A directory ensure_dir(fn) else: @@ -135,23 +136,17 @@ def unzip_file(filename, location, flatten=True): # chunk of memory for the file's content fp = zip.open(name) try: - with open(fn, 'wb') as destfp: + with open(fn, "wb") as destfp: shutil.copyfileobj(fp, destfp) finally: fp.close() - mode = info.external_attr >> 16 - # if mode and regular file and any execute permissions for - # user/group/world? - if mode and stat.S_ISREG(mode) and mode & 0o111: - # make dest file have execute for user/group/world - # (chmod +x) no-op on windows per python docs - os.chmod(fn, (0o777 - current_umask() | 0o111)) + if zip_item_is_executable(info): + set_extracted_file_to_default_mode_plus_executable(fn) finally: zipfp.close() -def untar_file(filename, location): - # type: (str, str) -> None +def untar_file(filename: str, location: str) -> None: """ Untar the file (with path `filename`) to the destination `location`. All files are written based on system defaults and umask (i.e. permissions @@ -161,38 +156,34 @@ def untar_file(filename, location): no-ops per the python docs. """ ensure_dir(location) - if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): - mode = 'r:gz' + if filename.lower().endswith(".gz") or filename.lower().endswith(".tgz"): + mode = "r:gz" elif filename.lower().endswith(BZ2_EXTENSIONS): - mode = 'r:bz2' + mode = "r:bz2" elif filename.lower().endswith(XZ_EXTENSIONS): - mode = 'r:xz' - elif filename.lower().endswith('.tar'): - mode = 'r' + mode = "r:xz" + elif filename.lower().endswith(".tar"): + mode = "r" else: logger.warning( - 'Cannot determine compression type for file %s', filename, + "Cannot determine compression type for file %s", + filename, ) - mode = 'r:*' - tar = tarfile.open(filename, mode) + mode = "r:*" + tar = tarfile.open(filename, mode, encoding="utf-8") try: - leading = has_leading_dir([ - member.name for member in tar.getmembers() - ]) + leading = has_leading_dir([member.name for member in tar.getmembers()]) for member in tar.getmembers(): fn = member.name if leading: - # https://github.com/python/mypy/issues/1174 - fn = split_leading_dir(fn)[1] # type: ignore + fn = split_leading_dir(fn)[1] path = os.path.join(location, fn) if not is_within_directory(location, path): message = ( - 'The tar file ({}) has a file ({}) trying to install ' - 'outside target directory ({})' - ) - raise InstallationError( - message.format(filename, path, location) + "The tar file ({}) has a file ({}) trying to install " + "outside target directory ({})" ) + raise InstallationError(message.format(filename, path, location)) if member.isdir(): ensure_dir(path) elif member.issym(): @@ -203,8 +194,10 @@ def untar_file(filename, location): # Some corrupt tar files seem to produce this # (specifically bad symlinks) logger.warning( - 'In the tar file %s the member %s is invalid: %s', - filename, member.name, exc, + "In the tar file %s the member %s is invalid: %s", + filename, + member.name, + exc, ) continue else: @@ -214,59 +207,52 @@ def untar_file(filename, location): # Some corrupt tar files seem to produce this # (specifically bad symlinks) logger.warning( - 'In the tar file %s the member %s is invalid: %s', - filename, member.name, exc, + "In the tar file %s the member %s is invalid: %s", + filename, + member.name, + exc, ) continue ensure_dir(os.path.dirname(path)) - with open(path, 'wb') as destfp: + assert fp is not None + with open(path, "wb") as destfp: shutil.copyfileobj(fp, destfp) fp.close() # Update the timestamp (useful for cython compiled files) - # https://github.com/python/typeshed/issues/2673 - tar.utime(member, path) # type: ignore + tar.utime(member, path) # member have any execute permissions for user/group/world? if member.mode & 0o111: - # make dest file have execute for user/group/world - # no-op on windows per python docs - os.chmod(path, (0o777 - current_umask() | 0o111)) + set_extracted_file_to_default_mode_plus_executable(path) finally: tar.close() def unpack_file( - filename, # type: str - location, # type: str - content_type=None, # type: Optional[str] -): - # type: (...) -> None + filename: str, + location: str, + content_type: Optional[str] = None, +) -> None: filename = os.path.realpath(filename) if ( - content_type == 'application/zip' or - filename.lower().endswith(ZIP_EXTENSIONS) or - zipfile.is_zipfile(filename) + content_type == "application/zip" + or filename.lower().endswith(ZIP_EXTENSIONS) + or zipfile.is_zipfile(filename) ): - unzip_file( - filename, - location, - flatten=not filename.endswith('.whl') - ) + unzip_file(filename, location, flatten=not filename.endswith(".whl")) elif ( - content_type == 'application/x-gzip' or - tarfile.is_tarfile(filename) or - filename.lower().endswith( - TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS - ) + content_type == "application/x-gzip" + or tarfile.is_tarfile(filename) + or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS) ): untar_file(filename, location) else: # FIXME: handle? # FIXME: magic signatures? logger.critical( - 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' - 'cannot detect archive format', - filename, location, content_type, - ) - raise InstallationError( - 'Cannot determine archive format of {}'.format(location) + "Cannot unpack file %s (downloaded from %s, content-type: %s); " + "cannot detect archive format", + filename, + location, + content_type, ) + raise InstallationError(f"Cannot determine archive format of {location}") diff --git a/venv/Lib/site-packages/pip/_internal/utils/urls.py b/venv/Lib/site-packages/pip/_internal/utils/urls.py index f37bc8f90b2c0b6cb019f4d112d4ce6043ac672d..6ba2e04f350792e2c0021cf7ba7f40b25dc6cd51 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/urls.py +++ b/venv/Lib/site-packages/pip/_internal/utils/urls.py @@ -1,55 +1,62 @@ import os -import sys +import string +import urllib.parse +import urllib.request +from typing import Optional -from pip._vendor.six.moves.urllib import parse as urllib_parse -from pip._vendor.six.moves.urllib import request as urllib_request +from .compat import WINDOWS -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -if MYPY_CHECK_RUNNING: - from typing import Optional, Text, Union - - -def get_url_scheme(url): - # type: (Union[str, Text]) -> Optional[Text] - if ':' not in url: +def get_url_scheme(url: str) -> Optional[str]: + if ":" not in url: return None - return url.split(':', 1)[0].lower() + return url.split(":", 1)[0].lower() -def path_to_url(path): - # type: (Union[str, Text]) -> str +def path_to_url(path: str) -> str: """ Convert a path to a file: URL. The path will be made absolute and have quoted path parts. """ path = os.path.normpath(os.path.abspath(path)) - url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + url = urllib.parse.urljoin("file:", urllib.request.pathname2url(path)) return url -def url_to_path(url): - # type: (str) -> str +def url_to_path(url: str) -> str: """ Convert a file: URL to a path. """ - assert url.startswith('file:'), ( - "You can only turn file: urls into filenames (not {url!r})" - .format(**locals())) + assert url.startswith( + "file:" + ), f"You can only turn file: urls into filenames (not {url!r})" - _, netloc, path, _, _ = urllib_parse.urlsplit(url) + _, netloc, path, _, _ = urllib.parse.urlsplit(url) - if not netloc or netloc == 'localhost': + if not netloc or netloc == "localhost": # According to RFC 8089, same as empty authority. - netloc = '' - elif sys.platform == 'win32': + netloc = "" + elif WINDOWS: # If we have a UNC path, prepend UNC share notation. - netloc = '\\\\' + netloc + netloc = "\\\\" + netloc else: raise ValueError( - 'non-local file URIs are not supported on this platform: {url!r}' - .format(**locals()) + f"non-local file URIs are not supported on this platform: {url!r}" ) - path = urllib_request.url2pathname(netloc + path) + path = urllib.request.url2pathname(netloc + path) + + # On Windows, urlsplit parses the path as something like "/C:/Users/foo". + # This creates issues for path-related functions like io.open(), so we try + # to detect and strip the leading slash. + if ( + WINDOWS + and not netloc # Not UNC. + and len(path) >= 3 + and path[0] == "/" # Leading slash to strip. + and path[1] in string.ascii_letters # Drive letter. + and path[2:4] in (":", ":/") # Colon + end of string, or colon + absolute path. + ): + path = path[1:] + return path diff --git a/venv/Lib/site-packages/pip/_internal/utils/virtualenv.py b/venv/Lib/site-packages/pip/_internal/utils/virtualenv.py index 596a69a7dad6f3270648e179559b759471c2a4f2..c926db4c33215425c838ebf3de02a204f7863e50 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/virtualenv.py +++ b/venv/Lib/site-packages/pip/_internal/utils/virtualenv.py @@ -1,15 +1,9 @@ -from __future__ import absolute_import - import logging import os import re import site import sys - -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from typing import List, Optional +from typing import List, Optional logger = logging.getLogger(__name__) _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( @@ -17,8 +11,7 @@ _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( ) -def _running_under_venv(): - # type: () -> bool +def _running_under_venv() -> bool: """Checks if sys.base_prefix and sys.prefix match. This handles PEP 405 compliant virtual environments. @@ -26,39 +19,36 @@ def _running_under_venv(): return sys.prefix != getattr(sys, "base_prefix", sys.prefix) -def _running_under_regular_virtualenv(): - # type: () -> bool +def _running_under_regular_virtualenv() -> bool: """Checks if sys.real_prefix is set. This handles virtual environments created with pypa's virtualenv. """ # pypa/virtualenv case - return hasattr(sys, 'real_prefix') + return hasattr(sys, "real_prefix") -def running_under_virtualenv(): - # type: () -> bool - """Return True if we're running inside a virtualenv, False otherwise. - """ +def running_under_virtualenv() -> bool: + """Return True if we're running inside a virtualenv, False otherwise.""" return _running_under_venv() or _running_under_regular_virtualenv() -def _get_pyvenv_cfg_lines(): - # type: () -> Optional[List[str]] +def _get_pyvenv_cfg_lines() -> Optional[List[str]]: """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines Returns None, if it could not read/access the file. """ - pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg') + pyvenv_cfg_file = os.path.join(sys.prefix, "pyvenv.cfg") try: - with open(pyvenv_cfg_file) as f: + # Although PEP 405 does not specify, the built-in venv module always + # writes with UTF-8. (pypa/pip#8717) + with open(pyvenv_cfg_file, encoding="utf-8") as f: return f.read().splitlines() # avoids trailing newlines - except IOError: + except OSError: return None -def _no_global_under_venv(): - # type: () -> bool +def _no_global_under_venv() -> bool: """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion PEP 405 specifies that when system site-packages are not supposed to be @@ -82,13 +72,12 @@ def _no_global_under_venv(): for line in cfg_lines: match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line) - if match is not None and match.group('value') == 'false': + if match is not None and match.group("value") == "false": return True return False -def _no_global_under_regular_virtualenv(): - # type: () -> bool +def _no_global_under_regular_virtualenv() -> bool: """Check if "no-global-site-packages.txt" exists beside site.py This mirrors logic in pypa/virtualenv for determining whether system @@ -96,15 +85,14 @@ def _no_global_under_regular_virtualenv(): """ site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) no_global_site_packages_file = os.path.join( - site_mod_dir, 'no-global-site-packages.txt', + site_mod_dir, + "no-global-site-packages.txt", ) return os.path.exists(no_global_site_packages_file) -def virtualenv_no_global(): - # type: () -> bool - """Returns a boolean, whether running in venv with no system site-packages. - """ +def virtualenv_no_global() -> bool: + """Returns a boolean, whether running in venv with no system site-packages.""" # PEP 405 compliance needs to be checked first since virtualenv >=20 would # return True for both checks, but is only able to use the PEP 405 config. if _running_under_venv(): diff --git a/venv/Lib/site-packages/pip/_internal/utils/wheel.py b/venv/Lib/site-packages/pip/_internal/utils/wheel.py index 3ebb7710bc6f10bfc4b62b1c20e7471f90b700b5..03f00e40939ade49ede2af392065454e042f7877 100644 --- a/venv/Lib/site-packages/pip/_internal/utils/wheel.py +++ b/venv/Lib/site-packages/pip/_internal/utils/wheel.py @@ -1,31 +1,17 @@ """Support functions for working with wheel files. """ -from __future__ import absolute_import - import logging +from email.message import Message from email.parser import Parser -from zipfile import ZipFile +from typing import Dict, Tuple +from zipfile import BadZipFile, ZipFile from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.pkg_resources import DistInfoDistribution -from pip._vendor.six import PY2, ensure_str +from pip._vendor.pkg_resources import DistInfoDistribution, Distribution from pip._internal.exceptions import UnsupportedWheel from pip._internal.utils.pkg_resources import DictMetadata -from pip._internal.utils.typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: - from email.message import Message - from typing import Dict, Tuple - - from pip._vendor.pkg_resources import Distribution - -if PY2: - from zipfile import BadZipfile as BadZipFile -else: - from zipfile import BadZipFile - VERSION_COMPATIBLE = (1, 0) @@ -37,63 +23,47 @@ class WheelMetadata(DictMetadata): """Metadata provider that maps metadata decoding exceptions to our internal exception type. """ - def __init__(self, metadata, wheel_name): - # type: (Dict[str, bytes], str) -> None - super(WheelMetadata, self).__init__(metadata) + + def __init__(self, metadata: Dict[str, bytes], wheel_name: str) -> None: + super().__init__(metadata) self._wheel_name = wheel_name - def get_metadata(self, name): - # type: (str) -> str + def get_metadata(self, name: str) -> str: try: - return super(WheelMetadata, self).get_metadata(name) + return super().get_metadata(name) except UnicodeDecodeError as e: # Augment the default error with the origin of the file. raise UnsupportedWheel( - "Error decoding metadata for {}: {}".format( - self._wheel_name, e - ) + f"Error decoding metadata for {self._wheel_name}: {e}" ) -def pkg_resources_distribution_for_wheel(wheel_zip, name, location): - # type: (ZipFile, str, str) -> Distribution +def pkg_resources_distribution_for_wheel( + wheel_zip: ZipFile, name: str, location: str +) -> Distribution: """Get a pkg_resources distribution given a wheel. :raises UnsupportedWheel: on any errors """ info_dir, _ = parse_wheel(wheel_zip, name) - metadata_files = [ - p for p in wheel_zip.namelist() if p.startswith("{}/".format(info_dir)) - ] + metadata_files = [p for p in wheel_zip.namelist() if p.startswith(f"{info_dir}/")] - metadata_text = {} # type: Dict[str, bytes] + metadata_text: Dict[str, bytes] = {} for path in metadata_files: - # If a flag is set, namelist entries may be unicode in Python 2. - # We coerce them to native str type to match the types used in the rest - # of the code. This cannot fail because unicode can always be encoded - # with UTF-8. - full_path = ensure_str(path) - _, metadata_name = full_path.split("/", 1) + _, metadata_name = path.split("/", 1) try: - metadata_text[metadata_name] = read_wheel_metadata_file( - wheel_zip, full_path - ) + metadata_text[metadata_name] = read_wheel_metadata_file(wheel_zip, path) except UnsupportedWheel as e: - raise UnsupportedWheel( - "{} has an invalid wheel, {}".format(name, str(e)) - ) + raise UnsupportedWheel("{} has an invalid wheel, {}".format(name, str(e))) metadata = WheelMetadata(metadata_text, location) - return DistInfoDistribution( - location=location, metadata=metadata, project_name=name - ) + return DistInfoDistribution(location=location, metadata=metadata, project_name=name) -def parse_wheel(wheel_zip, name): - # type: (ZipFile, str) -> Tuple[str, Message] +def parse_wheel(wheel_zip: ZipFile, name: str) -> Tuple[str, Message]: """Extract information from the provided wheel, ensuring it meets basic standards. @@ -104,35 +74,30 @@ def parse_wheel(wheel_zip, name): metadata = wheel_metadata(wheel_zip, info_dir) version = wheel_version(metadata) except UnsupportedWheel as e: - raise UnsupportedWheel( - "{} has an invalid wheel, {}".format(name, str(e)) - ) + raise UnsupportedWheel("{} has an invalid wheel, {}".format(name, str(e))) check_compatibility(version, name) return info_dir, metadata -def wheel_dist_info_dir(source, name): - # type: (ZipFile, str) -> str +def wheel_dist_info_dir(source: ZipFile, name: str) -> str: """Returns the name of the contained .dist-info directory. Raises AssertionError or UnsupportedWheel if not found, >1 found, or it doesn't match the provided name. """ # Zip file path separators must be / - subdirs = list(set(p.split("/")[0] for p in source.namelist())) + subdirs = {p.split("/", 1)[0] for p in source.namelist()} - info_dirs = [s for s in subdirs if s.endswith('.dist-info')] + info_dirs = [s for s in subdirs if s.endswith(".dist-info")] if not info_dirs: raise UnsupportedWheel(".dist-info directory not found") if len(info_dirs) > 1: raise UnsupportedWheel( - "multiple .dist-info directories found: {}".format( - ", ".join(info_dirs) - ) + "multiple .dist-info directories found: {}".format(", ".join(info_dirs)) ) info_dir = info_dirs[0] @@ -146,36 +111,30 @@ def wheel_dist_info_dir(source, name): ) ) - # Zip file paths can be unicode or str depending on the zip entry flags, - # so normalize it. - return ensure_str(info_dir) + return info_dir -def read_wheel_metadata_file(source, path): - # type: (ZipFile, str) -> bytes +def read_wheel_metadata_file(source: ZipFile, path: str) -> bytes: try: return source.read(path) # BadZipFile for general corruption, KeyError for missing entry, # and RuntimeError for password-protected files except (BadZipFile, KeyError, RuntimeError) as e: - raise UnsupportedWheel( - "could not read {!r} file: {!r}".format(path, e) - ) + raise UnsupportedWheel(f"could not read {path!r} file: {e!r}") -def wheel_metadata(source, dist_info_dir): - # type: (ZipFile, str) -> Message +def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message: """Return the WHEEL metadata of an extracted wheel, if possible. Otherwise, raise UnsupportedWheel. """ - path = "{}/WHEEL".format(dist_info_dir) + path = f"{dist_info_dir}/WHEEL" # Zip file path separators must be / wheel_contents = read_wheel_metadata_file(source, path) try: - wheel_text = ensure_str(wheel_contents) + wheel_text = wheel_contents.decode() except UnicodeDecodeError as e: - raise UnsupportedWheel("error decoding {!r}: {!r}".format(path, e)) + raise UnsupportedWheel(f"error decoding {path!r}: {e!r}") # FeedParser (used by Parser) does not raise any exceptions. The returned # message may have .defects populated, but for backwards-compatibility we @@ -183,8 +142,7 @@ def wheel_metadata(source, dist_info_dir): return Parser().parsestr(wheel_text) -def wheel_version(wheel_data): - # type: (Message) -> Tuple[int, ...] +def wheel_version(wheel_data: Message) -> Tuple[int, ...]: """Given WHEEL metadata, return the parsed Wheel-Version. Otherwise, raise UnsupportedWheel. """ @@ -195,13 +153,12 @@ def wheel_version(wheel_data): version = version_text.strip() try: - return tuple(map(int, version.split('.'))) + return tuple(map(int, version.split("."))) except ValueError: - raise UnsupportedWheel("invalid Wheel-Version: {!r}".format(version)) + raise UnsupportedWheel(f"invalid Wheel-Version: {version!r}") -def check_compatibility(version, name): - # type: (Tuple[int, ...], str) -> None +def check_compatibility(version: Tuple[int, ...], name: str) -> None: """Raises errors or warns if called with an incompatible Wheel-Version. pip should refuse to install a Wheel-Version that's a major series @@ -216,10 +173,10 @@ def check_compatibility(version, name): if version[0] > VERSION_COMPATIBLE[0]: raise UnsupportedWheel( "{}'s Wheel-Version ({}) is not compatible with this version " - "of pip".format(name, '.'.join(map(str, version))) + "of pip".format(name, ".".join(map(str, version))) ) elif version > VERSION_COMPATIBLE: logger.warning( - 'Installing from a newer Wheel-Version (%s)', - '.'.join(map(str, version)), + "Installing from a newer Wheel-Version (%s)", + ".".join(map(str, version)), ) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/__init__.py b/venv/Lib/site-packages/pip/_internal/vcs/__init__.py index 2a4eb1375763fa3287d171a2a1b0766d1d9d1224..b6beddbe6d24d2949dc89ed07abfebd59d8b63b9 100644 --- a/venv/Lib/site-packages/pip/_internal/vcs/__init__.py +++ b/venv/Lib/site-packages/pip/_internal/vcs/__init__.py @@ -1,7 +1,6 @@ # Expose a limited set of classes and functions so callers outside of # the vcs package don't need to import deeper than `pip._internal.vcs`. -# (The test directory and imports protected by MYPY_CHECK_RUNNING may -# still need to import from a vcs sub-package.) +# (The test directory may still need to import from a vcs sub-package.) # Import all vcs modules to register each VCS in the VcsSupport object. import pip._internal.vcs.bazaar import pip._internal.vcs.git @@ -9,6 +8,7 @@ import pip._internal.vcs.mercurial import pip._internal.vcs.subversion # noqa: F401 from pip._internal.vcs.versioncontrol import ( # noqa: F401 RemoteNotFoundError, + RemoteNotValidError, is_url, make_vcs_requirement_url, vcs, diff --git a/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py b/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py index 347c06f9dc7c882299bf1a829049849a06328fe5..82e75954a08e57652505882bfb3d9b121a00f534 100644 --- a/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py +++ b/venv/Lib/site-packages/pip/_internal/vcs/bazaar.py @@ -1,118 +1,91 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging -import os +from typing import List, Optional, Tuple -from pip._vendor.six.moves.urllib import parse as urllib_parse - -from pip._internal.utils.misc import display_path, rmtree +from pip._internal.utils.misc import HiddenText, display_path from pip._internal.utils.subprocess import make_command -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url -from pip._internal.vcs.versioncontrol import VersionControl, vcs - -if MYPY_CHECK_RUNNING: - from typing import Optional, Tuple - from pip._internal.utils.misc import HiddenText - from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions - +from pip._internal.vcs.versioncontrol import ( + AuthInfo, + RemoteNotFoundError, + RevOptions, + VersionControl, + vcs, +) logger = logging.getLogger(__name__) class Bazaar(VersionControl): - name = 'bzr' - dirname = '.bzr' - repo_name = 'branch' + name = "bzr" + dirname = ".bzr" + repo_name = "branch" schemes = ( - 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', - 'bzr+lp', + "bzr+http", + "bzr+https", + "bzr+ssh", + "bzr+sftp", + "bzr+ftp", + "bzr+lp", + "bzr+file", ) - def __init__(self, *args, **kwargs): - super(Bazaar, self).__init__(*args, **kwargs) - # This is only needed for python <2.7.5 - # Register lp but do not expose as a scheme to support bzr+lp. - if getattr(urllib_parse, 'uses_fragment', None): - urllib_parse.uses_fragment.extend(['lp']) - @staticmethod - def get_base_rev_args(rev): - return ['-r', rev] - - def export(self, location, url): - # type: (str, HiddenText) -> None - """ - Export the Bazaar repository at the url to the destination location - """ - # Remove the location to make sure Bazaar can export it correctly - if os.path.exists(location): - rmtree(location) + def get_base_rev_args(rev: str) -> List[str]: + return ["-r", rev] - url, rev_options = self.get_url_rev_options(url) - self.run_command( - make_command('export', location, url, rev_options.to_args()), - show_stdout=False, - ) - - def fetch_new(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: rev_display = rev_options.to_display() logger.info( - 'Checking out %s%s to %s', + "Checking out %s%s to %s", url, rev_display, display_path(dest), ) - cmd_args = ( - make_command('branch', '-q', rev_options.to_args(), url, dest) - ) + cmd_args = make_command("branch", "-q", rev_options.to_args(), url, dest) self.run_command(cmd_args) - def switch(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None - self.run_command(make_command('switch', url), cwd=dest) + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + self.run_command(make_command("switch", url), cwd=dest) - def update(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None - cmd_args = make_command('pull', '-q', rev_options.to_args()) + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + cmd_args = make_command("pull", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest) @classmethod - def get_url_rev_and_auth(cls, url): - # type: (str) -> Tuple[str, Optional[str], AuthInfo] + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it - url, rev, user_pass = super(Bazaar, cls).get_url_rev_and_auth(url) - if url.startswith('ssh://'): - url = 'bzr+' + url + url, rev, user_pass = super().get_url_rev_and_auth(url) + if url.startswith("ssh://"): + url = "bzr+" + url return url, rev, user_pass @classmethod - def get_remote_url(cls, location): - urls = cls.run_command(['info'], show_stdout=False, cwd=location) + def get_remote_url(cls, location: str) -> str: + urls = cls.run_command( + ["info"], show_stdout=False, stdout_only=True, cwd=location + ) for line in urls.splitlines(): line = line.strip() - for x in ('checkout of branch: ', - 'parent branch: '): + for x in ("checkout of branch: ", "parent branch: "): if line.startswith(x): repo = line.split(x)[1] if cls._is_local_repository(repo): return path_to_url(repo) return repo - return None + raise RemoteNotFoundError @classmethod - def get_revision(cls, location): + def get_revision(cls, location: str) -> str: revision = cls.run_command( - ['revno'], show_stdout=False, cwd=location, + ["revno"], + show_stdout=False, + stdout_only=True, + cwd=location, ) return revision.splitlines()[-1] @classmethod - def is_commit_id_equal(cls, dest, name): + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: """Always assume the versions don't match""" return False diff --git a/venv/Lib/site-packages/pip/_internal/vcs/git.py b/venv/Lib/site-packages/pip/_internal/vcs/git.py index e173ec894ca047e2a85b1ef0bbc91962a8fe8354..7a78ad12dd5edbd999f8cfd2f5a824f3e3a3ae5d 100644 --- a/venv/Lib/site-packages/pip/_internal/vcs/git.py +++ b/venv/Lib/site-packages/pip/_internal/vcs/git.py @@ -1,66 +1,82 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import os.path +import pathlib import re - -from pip._vendor.packaging.version import parse as parse_version -from pip._vendor.six.moves.urllib import parse as urllib_parse -from pip._vendor.six.moves.urllib import request as urllib_request +import urllib.parse +import urllib.request +from typing import List, Optional, Tuple from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.misc import display_path, hide_url +from pip._internal.utils.misc import HiddenText, display_path, hide_url from pip._internal.utils.subprocess import make_command -from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.vcs.versioncontrol import ( + AuthInfo, RemoteNotFoundError, + RemoteNotValidError, + RevOptions, VersionControl, - find_path_to_setup_from_repo_root, + find_path_to_project_root_from_repo_root, vcs, ) -if MYPY_CHECK_RUNNING: - from typing import Optional, Tuple - from pip._internal.utils.misc import HiddenText - from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions - - -urlsplit = urllib_parse.urlsplit -urlunsplit = urllib_parse.urlunsplit +urlsplit = urllib.parse.urlsplit +urlunsplit = urllib.parse.urlunsplit logger = logging.getLogger(__name__) -HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$') +GIT_VERSION_REGEX = re.compile( + r"^git version " # Prefix. + r"(\d+)" # Major. + r"\.(\d+)" # Dot, minor. + r"(?:\.(\d+))?" # Optional dot, patch. + r".*$" # Suffix, including any pre- and post-release segments we don't care about. +) + +HASH_REGEX = re.compile("^[a-fA-F0-9]{40}$") + +# SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git' +SCP_REGEX = re.compile( + r"""^ + # Optional user, e.g. 'git@' + (\w+@)? + # Server, e.g. 'github.com'. + ([^/:]+): + # The server-side path. e.g. 'user/project.git'. Must start with an + # alphanumeric character so as not to be confusable with a Windows paths + # like 'C:/foo/bar' or 'C:\foo\bar'. + (\w[^:]*) + $""", + re.VERBOSE, +) -def looks_like_hash(sha): +def looks_like_hash(sha: str) -> bool: return bool(HASH_REGEX.match(sha)) class Git(VersionControl): - name = 'git' - dirname = '.git' - repo_name = 'clone' + name = "git" + dirname = ".git" + repo_name = "clone" schemes = ( - 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + "git+http", + "git+https", + "git+ssh", + "git+git", + "git+file", ) # Prevent the user's environment variables from interfering with pip: # https://github.com/pypa/pip/issues/1130 - unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') - default_arg_rev = 'HEAD' + unset_environ = ("GIT_DIR", "GIT_WORK_TREE") + default_arg_rev = "HEAD" @staticmethod - def get_base_rev_args(rev): + def get_base_rev_args(rev: str) -> List[str]: return [rev] - def is_immutable_rev_checkout(self, url, dest): - # type: (str, str) -> bool + def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: _, rev_options = self.get_url_rev_options(hide_url(url)) if not rev_options.rev: return False @@ -71,26 +87,19 @@ class Git(VersionControl): # return False in the rare case rev is both a commit hash # and a tag or a branch; we don't want to cache in that case # because that branch/tag could point to something else in the future - is_tag_or_branch = bool( - self.get_revision_sha(dest, rev_options.rev)[0] - ) + is_tag_or_branch = bool(self.get_revision_sha(dest, rev_options.rev)[0]) return not is_tag_or_branch - def get_git_version(self): - VERSION_PFX = 'git version ' - version = self.run_command(['version'], show_stdout=False) - if version.startswith(VERSION_PFX): - version = version[len(VERSION_PFX):].split()[0] - else: - version = '' - # get first 3 positions of the git version because - # on windows it is x.y.z.windows.t, and this parses as - # LegacyVersion which always smaller than a Version. - version = '.'.join(version.split('.')[:3]) - return parse_version(version) + def get_git_version(self) -> Tuple[int, ...]: + version = self.run_command(["version"], show_stdout=False, stdout_only=True) + match = GIT_VERSION_REGEX.match(version) + if not match: + logger.warning("Can't parse git version: %s", version) + return () + return tuple(int(c) for c in match.groups()) @classmethod - def get_current_branch(cls, location): + def get_current_branch(cls, location: str) -> Optional[str]: """ Return the current branch, or None if HEAD isn't at a branch (e.g. detached HEAD). @@ -99,32 +108,23 @@ class Git(VersionControl): # HEAD rather than a symbolic ref. In addition, the -q causes the # command to exit with status code 1 instead of 128 in this case # and to suppress the message to stderr. - args = ['symbolic-ref', '-q', 'HEAD'] + args = ["symbolic-ref", "-q", "HEAD"] output = cls.run_command( - args, extra_ok_returncodes=(1, ), show_stdout=False, cwd=location, + args, + extra_ok_returncodes=(1,), + show_stdout=False, + stdout_only=True, + cwd=location, ) ref = output.strip() - if ref.startswith('refs/heads/'): - return ref[len('refs/heads/'):] + if ref.startswith("refs/heads/"): + return ref[len("refs/heads/") :] return None - def export(self, location, url): - # type: (str, HiddenText) -> None - """Export the Git repository at the url to the destination location""" - if not location.endswith('/'): - location = location + '/' - - with TempDirectory(kind="export") as temp_dir: - self.unpack(temp_dir.path, url=url) - self.run_command( - ['checkout-index', '-a', '-f', '--prefix', location], - show_stdout=False, cwd=temp_dir.path - ) - @classmethod - def get_revision_sha(cls, dest, rev): + def get_revision_sha(cls, dest: str, rev: str) -> Tuple[Optional[str], bool]: """ Return (sha_or_none, is_branch), where sha_or_none is a commit hash if the revision names a remote branch or tag, otherwise None. @@ -134,21 +134,32 @@ class Git(VersionControl): rev: the revision name. """ # Pass rev to pre-filter the list. - output = cls.run_command(['show-ref', rev], cwd=dest, - show_stdout=False, on_returncode='ignore') + output = cls.run_command( + ["show-ref", rev], + cwd=dest, + show_stdout=False, + stdout_only=True, + on_returncode="ignore", + ) refs = {} - for line in output.strip().splitlines(): + # NOTE: We do not use splitlines here since that would split on other + # unicode separators, which can be maliciously used to install a + # different revision. + for line in output.strip().split("\n"): + line = line.rstrip("\r") + if not line: + continue try: - sha, ref = line.split() + ref_sha, ref_name = line.split(" ", maxsplit=2) except ValueError: # Include the offending line to simplify troubleshooting if # this error ever occurs. - raise ValueError('unexpected show-ref line: {!r}'.format(line)) + raise ValueError(f"unexpected show-ref line: {line!r}") - refs[ref] = sha + refs[ref_name] = ref_sha - branch_ref = 'refs/remotes/origin/{}'.format(rev) - tag_ref = 'refs/tags/{}'.format(rev) + branch_ref = f"refs/remotes/origin/{rev}" + tag_ref = f"refs/tags/{rev}" sha = refs.get(branch_ref) if sha is not None: @@ -159,8 +170,32 @@ class Git(VersionControl): return (sha, False) @classmethod - def resolve_revision(cls, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> RevOptions + def _should_fetch(cls, dest: str, rev: str) -> bool: + """ + Return true if rev is a ref or is a commit that we don't have locally. + + Branches and tags are not considered in this method because they are + assumed to be always available locally (which is a normal outcome of + ``git clone`` and ``git fetch --tags``). + """ + if rev.startswith("refs/"): + # Always fetch remote refs. + return True + + if not looks_like_hash(rev): + # Git fetch would fail with abbreviated commits. + return False + + if cls.has_commit(dest, rev): + # Don't fetch if we have the commit locally. + return False + + return True + + @classmethod + def resolve_revision( + cls, dest: str, url: HiddenText, rev_options: RevOptions + ) -> RevOptions: """ Resolve a revision to a new RevOptions object with the SHA1 of the branch, tag, or ref if found. @@ -189,22 +224,22 @@ class Git(VersionControl): rev, ) - if not rev.startswith('refs/'): + if not cls._should_fetch(dest, rev): return rev_options - # If it looks like a ref, we have to fetch it explicitly. + # fetch the requested revision cls.run_command( - make_command('fetch', '-q', url, rev_options.to_args()), + make_command("fetch", "-q", url, rev_options.to_args()), cwd=dest, ) # Change the revision to the SHA of the ref we fetched - sha = cls.get_revision(dest, rev='FETCH_HEAD') + sha = cls.get_revision(dest, rev="FETCH_HEAD") rev_options = rev_options.make_new(sha) return rev_options @classmethod - def is_commit_id_equal(cls, dest, name): + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: """ Return whether the current commit hash equals the given name. @@ -218,64 +253,87 @@ class Git(VersionControl): return cls.get_revision(dest) == name - def fetch_new(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: rev_display = rev_options.to_display() - logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest)) - self.run_command(make_command('clone', '-q', url, dest)) + logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest)) + if self.get_git_version() >= (2, 17): + # Git added support for partial clone in 2.17 + # https://git-scm.com/docs/partial-clone + # Speeds up cloning by functioning without a complete copy of repository + self.run_command( + make_command( + "clone", + "--filter=blob:none", + "-q", + url, + dest, + ) + ) + else: + self.run_command(make_command("clone", "-q", url, dest)) if rev_options.rev: # Then a specific revision was requested. rev_options = self.resolve_revision(dest, url, rev_options) - branch_name = getattr(rev_options, 'branch_name', None) + branch_name = getattr(rev_options, "branch_name", None) + logger.debug("Rev options %s, branch_name %s", rev_options, branch_name) if branch_name is None: # Only do a checkout if the current commit id doesn't match # the requested revision. if not self.is_commit_id_equal(dest, rev_options.rev): cmd_args = make_command( - 'checkout', '-q', rev_options.to_args(), + "checkout", + "-q", + rev_options.to_args(), ) self.run_command(cmd_args, cwd=dest) elif self.get_current_branch(dest) != branch_name: # Then a specific branch was requested, and that branch # is not yet checked out. - track_branch = 'origin/{}'.format(branch_name) + track_branch = f"origin/{branch_name}" cmd_args = [ - 'checkout', '-b', branch_name, '--track', track_branch, + "checkout", + "-b", + branch_name, + "--track", + track_branch, ] self.run_command(cmd_args, cwd=dest) + else: + sha = self.get_revision(dest) + rev_options = rev_options.make_new(sha) + + logger.info("Resolved %s to commit %s", url, rev_options.rev) #: repo may contain submodules self.update_submodules(dest) - def switch(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: self.run_command( - make_command('config', 'remote.origin.url', url), + make_command("config", "remote.origin.url", url), cwd=dest, ) - cmd_args = make_command('checkout', '-q', rev_options.to_args()) + cmd_args = make_command("checkout", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest) self.update_submodules(dest) - def update(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: # First fetch changes from the default remote - if self.get_git_version() >= parse_version('1.9.0'): + if self.get_git_version() >= (1, 9): # fetch tags in addition to everything else - self.run_command(['fetch', '-q', '--tags'], cwd=dest) + self.run_command(["fetch", "-q", "--tags"], cwd=dest) else: - self.run_command(['fetch', '-q'], cwd=dest) + self.run_command(["fetch", "-q"], cwd=dest) # Then reset to wanted revision (maybe even origin/master) rev_options = self.resolve_revision(dest, url, rev_options) - cmd_args = make_command('reset', '--hard', '-q', rev_options.to_args()) + cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest) #: update submodules self.update_submodules(dest) @classmethod - def get_remote_url(cls, location): + def get_remote_url(cls, location: str) -> str: """ Return URL of the first remote encountered. @@ -285,8 +343,11 @@ class Git(VersionControl): # We need to pass 1 for extra_ok_returncodes since the command # exits with return code 1 if there are no matching lines. stdout = cls.run_command( - ['config', '--get-regexp', r'remote\..*\.url'], - extra_ok_returncodes=(1, ), show_stdout=False, cwd=location, + ["config", "--get-regexp", r"remote\..*\.url"], + extra_ok_returncodes=(1,), + show_stdout=False, + stdout_only=True, + cwd=location, ) remotes = stdout.splitlines() try: @@ -295,39 +356,91 @@ class Git(VersionControl): raise RemoteNotFoundError for remote in remotes: - if remote.startswith('remote.origin.url '): + if remote.startswith("remote.origin.url "): found_remote = remote break - url = found_remote.split(' ')[1] - return url.strip() + url = found_remote.split(" ")[1] + return cls._git_remote_to_pip_url(url.strip()) + + @staticmethod + def _git_remote_to_pip_url(url: str) -> str: + """ + Convert a remote url from what git uses to what pip accepts. + + There are 3 legal forms **url** may take: + + 1. A fully qualified url: ssh://git@example.com/foo/bar.git + 2. A local project.git folder: /path/to/bare/repository.git + 3. SCP shorthand for form 1: git@example.com:foo/bar.git + + Form 1 is output as-is. Form 2 must be converted to URI and form 3 must + be converted to form 1. + + See the corresponding test test_git_remote_url_to_pip() for examples of + sample inputs/outputs. + """ + if re.match(r"\w+://", url): + # This is already valid. Pass it though as-is. + return url + if os.path.exists(url): + # A local bare remote (git clone --mirror). + # Needs a file:// prefix. + return pathlib.PurePath(url).as_uri() + scp_match = SCP_REGEX.match(url) + if scp_match: + # Add an ssh:// prefix and replace the ':' with a '/'. + return scp_match.expand(r"ssh://\1\2/\3") + # Otherwise, bail out. + raise RemoteNotValidError(url) + + @classmethod + def has_commit(cls, location: str, rev: str) -> bool: + """ + Check if rev is a commit that is available in the local repository. + """ + try: + cls.run_command( + ["rev-parse", "-q", "--verify", "sha^" + rev], + cwd=location, + log_failed_cmd=False, + ) + except InstallationError: + return False + else: + return True @classmethod - def get_revision(cls, location, rev=None): + def get_revision(cls, location: str, rev: Optional[str] = None) -> str: if rev is None: - rev = 'HEAD' + rev = "HEAD" current_rev = cls.run_command( - ['rev-parse', rev], show_stdout=False, cwd=location, + ["rev-parse", rev], + show_stdout=False, + stdout_only=True, + cwd=location, ) return current_rev.strip() @classmethod - def get_subdirectory(cls, location): + def get_subdirectory(cls, location: str) -> Optional[str]: """ - Return the path to setup.py, relative to the repo root. - Return None if setup.py is in the repo root. + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. """ # find the repo root git_dir = cls.run_command( - ['rev-parse', '--git-dir'], - show_stdout=False, cwd=location).strip() + ["rev-parse", "--git-dir"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() if not os.path.isabs(git_dir): git_dir = os.path.join(location, git_dir) - repo_root = os.path.abspath(os.path.join(git_dir, '..')) - return find_path_to_setup_from_repo_root(location, repo_root) + repo_root = os.path.abspath(os.path.join(git_dir, "..")) + return find_path_to_project_root_from_repo_root(location, repo_root) @classmethod - def get_url_rev_and_auth(cls, url): - # type: (str) -> Tuple[str, Optional[str], AuthInfo] + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: """ Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. That's required because although they use SSH they sometimes don't @@ -337,58 +450,64 @@ class Git(VersionControl): # Works around an apparent Git bug # (see https://article.gmane.org/gmane.comp.version-control.git/146500) scheme, netloc, path, query, fragment = urlsplit(url) - if scheme.endswith('file'): - initial_slashes = path[:-len(path.lstrip('/'))] - newpath = ( - initial_slashes + - urllib_request.url2pathname(path) - .replace('\\', '/').lstrip('/') - ) - url = urlunsplit((scheme, netloc, newpath, query, fragment)) - after_plus = scheme.find('+') + 1 + if scheme.endswith("file"): + initial_slashes = path[: -len(path.lstrip("/"))] + newpath = initial_slashes + urllib.request.url2pathname(path).replace( + "\\", "/" + ).lstrip("/") + after_plus = scheme.find("+") + 1 url = scheme[:after_plus] + urlunsplit( (scheme[after_plus:], netloc, newpath, query, fragment), ) - if '://' not in url: - assert 'file:' not in url - url = url.replace('git+', 'git+ssh://') - url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) - url = url.replace('ssh://', '') + if "://" not in url: + assert "file:" not in url + url = url.replace("git+", "git+ssh://") + url, rev, user_pass = super().get_url_rev_and_auth(url) + url = url.replace("ssh://", "") else: - url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + url, rev, user_pass = super().get_url_rev_and_auth(url) return url, rev, user_pass @classmethod - def update_submodules(cls, location): - if not os.path.exists(os.path.join(location, '.gitmodules')): + def update_submodules(cls, location: str) -> None: + if not os.path.exists(os.path.join(location, ".gitmodules")): return cls.run_command( - ['submodule', 'update', '--init', '--recursive', '-q'], + ["submodule", "update", "--init", "--recursive", "-q"], cwd=location, ) @classmethod - def get_repository_root(cls, location): - loc = super(Git, cls).get_repository_root(location) + def get_repository_root(cls, location: str) -> Optional[str]: + loc = super().get_repository_root(location) if loc: return loc try: r = cls.run_command( - ['rev-parse', '--show-toplevel'], + ["rev-parse", "--show-toplevel"], cwd=location, show_stdout=False, - on_returncode='raise', + stdout_only=True, + on_returncode="raise", log_failed_cmd=False, ) except BadCommand: - logger.debug("could not determine if %s is under git control " - "because git is not available", location) + logger.debug( + "could not determine if %s is under git control " + "because git is not available", + location, + ) return None except InstallationError: return None - return os.path.normpath(r.rstrip('\r\n')) + return os.path.normpath(r.rstrip("\r\n")) + + @staticmethod + def should_add_vcs_url_prefix(repo_url: str) -> bool: + """In either https or ssh form, requirements must be prefixed with git+.""" + return True vcs.register(Git) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py b/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py index 75e903cc8a6d104d522395fc2f67ad0ff4abe778..410c79d903abf695a5b21fe6ba9ecc9c832c0341 100644 --- a/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py +++ b/venv/Lib/site-packages/pip/_internal/vcs/mercurial.py @@ -1,161 +1,153 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - +import configparser import logging import os - -from pip._vendor.six.moves import configparser +from typing import List, Optional from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import HiddenText, display_path from pip._internal.utils.subprocess import make_command -from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url from pip._internal.vcs.versioncontrol import ( + RevOptions, VersionControl, - find_path_to_setup_from_repo_root, + find_path_to_project_root_from_repo_root, vcs, ) -if MYPY_CHECK_RUNNING: - from pip._internal.utils.misc import HiddenText - from pip._internal.vcs.versioncontrol import RevOptions - - logger = logging.getLogger(__name__) class Mercurial(VersionControl): - name = 'hg' - dirname = '.hg' - repo_name = 'clone' + name = "hg" + dirname = ".hg" + repo_name = "clone" schemes = ( - 'hg', 'hg+file', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http', + "hg+file", + "hg+http", + "hg+https", + "hg+ssh", + "hg+static-http", ) @staticmethod - def get_base_rev_args(rev): + def get_base_rev_args(rev: str) -> List[str]: return [rev] - def export(self, location, url): - # type: (str, HiddenText) -> None - """Export the Hg repository at the url to the destination location""" - with TempDirectory(kind="export") as temp_dir: - self.unpack(temp_dir.path, url=url) - - self.run_command( - ['archive', location], show_stdout=False, cwd=temp_dir.path - ) - - def fetch_new(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: rev_display = rev_options.to_display() logger.info( - 'Cloning hg %s%s to %s', + "Cloning hg %s%s to %s", url, rev_display, display_path(dest), ) - self.run_command(make_command('clone', '--noupdate', '-q', url, dest)) + self.run_command(make_command("clone", "--noupdate", "-q", url, dest)) self.run_command( - make_command('update', '-q', rev_options.to_args()), + make_command("update", "-q", rev_options.to_args()), cwd=dest, ) - def switch(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None - repo_config = os.path.join(dest, self.dirname, 'hgrc') + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + repo_config = os.path.join(dest, self.dirname, "hgrc") config = configparser.RawConfigParser() try: config.read(repo_config) - config.set('paths', 'default', url.secret) - with open(repo_config, 'w') as config_file: + config.set("paths", "default", url.secret) + with open(repo_config, "w") as config_file: config.write(config_file) except (OSError, configparser.NoSectionError) as exc: - logger.warning( - 'Could not switch Mercurial repository to %s: %s', url, exc, - ) + logger.warning("Could not switch Mercurial repository to %s: %s", url, exc) else: - cmd_args = make_command('update', '-q', rev_options.to_args()) + cmd_args = make_command("update", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest) - def update(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None - self.run_command(['pull', '-q'], cwd=dest) - cmd_args = make_command('update', '-q', rev_options.to_args()) + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + self.run_command(["pull", "-q"], cwd=dest) + cmd_args = make_command("update", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest) @classmethod - def get_remote_url(cls, location): + def get_remote_url(cls, location: str) -> str: url = cls.run_command( - ['showconfig', 'paths.default'], - show_stdout=False, cwd=location).strip() + ["showconfig", "paths.default"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() if cls._is_local_repository(url): url = path_to_url(url) return url.strip() @classmethod - def get_revision(cls, location): + def get_revision(cls, location: str) -> str: """ Return the repository-local changeset revision number, as an integer. """ current_revision = cls.run_command( - ['parents', '--template={rev}'], - show_stdout=False, cwd=location).strip() + ["parents", "--template={rev}"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() return current_revision @classmethod - def get_requirement_revision(cls, location): + def get_requirement_revision(cls, location: str) -> str: """ Return the changeset identification hash, as a 40-character hexadecimal string """ current_rev_hash = cls.run_command( - ['parents', '--template={node}'], - show_stdout=False, cwd=location).strip() + ["parents", "--template={node}"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() return current_rev_hash @classmethod - def is_commit_id_equal(cls, dest, name): + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: """Always assume the versions don't match""" return False @classmethod - def get_subdirectory(cls, location): + def get_subdirectory(cls, location: str) -> Optional[str]: """ - Return the path to setup.py, relative to the repo root. - Return None if setup.py is in the repo root. + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. """ # find the repo root repo_root = cls.run_command( - ['root'], show_stdout=False, cwd=location).strip() + ["root"], show_stdout=False, stdout_only=True, cwd=location + ).strip() if not os.path.isabs(repo_root): repo_root = os.path.abspath(os.path.join(location, repo_root)) - return find_path_to_setup_from_repo_root(location, repo_root) + return find_path_to_project_root_from_repo_root(location, repo_root) @classmethod - def get_repository_root(cls, location): - loc = super(Mercurial, cls).get_repository_root(location) + def get_repository_root(cls, location: str) -> Optional[str]: + loc = super().get_repository_root(location) if loc: return loc try: r = cls.run_command( - ['root'], + ["root"], cwd=location, show_stdout=False, - on_returncode='raise', + stdout_only=True, + on_returncode="raise", log_failed_cmd=False, ) except BadCommand: - logger.debug("could not determine if %s is under hg control " - "because hg is not available", location) + logger.debug( + "could not determine if %s is under hg control " + "because hg is not available", + location, + ) return None except InstallationError: return None - return os.path.normpath(r.rstrip('\r\n')) + return os.path.normpath(r.rstrip("\r\n")) vcs.register(Mercurial) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/subversion.py b/venv/Lib/site-packages/pip/_internal/vcs/subversion.py index 0ec65974492d99a00fcef6820b68526bf1b08a42..b5b6fd5c09f4cd09ea5b6b5a435e22b22d2f0a6d 100644 --- a/venv/Lib/site-packages/pip/_internal/vcs/subversion.py +++ b/venv/Lib/site-packages/pip/_internal/vcs/subversion.py @@ -1,67 +1,60 @@ -# The following comment should be removed at some point in the future. -# mypy: disallow-untyped-defs=False - -from __future__ import absolute_import - import logging import os import re +from typing import List, Optional, Tuple -from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ( + HiddenText, display_path, is_console_interactive, - rmtree, + is_installable_dir, split_auth_from_netloc, ) -from pip._internal.utils.subprocess import make_command -from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.vcs.versioncontrol import VersionControl, vcs +from pip._internal.utils.subprocess import CommandArgs, make_command +from pip._internal.vcs.versioncontrol import ( + AuthInfo, + RemoteNotFoundError, + RevOptions, + VersionControl, + vcs, +) + +logger = logging.getLogger(__name__) _svn_xml_url_re = re.compile('url="([^"]+)"') _svn_rev_re = re.compile(r'committed-rev="(\d+)"') _svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') -_svn_info_xml_url_re = re.compile(r'(.*)') - - -if MYPY_CHECK_RUNNING: - from typing import Optional, Tuple - from pip._internal.utils.subprocess import CommandArgs - from pip._internal.utils.misc import HiddenText - from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions - - -logger = logging.getLogger(__name__) +_svn_info_xml_url_re = re.compile(r"(.*)") class Subversion(VersionControl): - name = 'svn' - dirname = '.svn' - repo_name = 'checkout' - schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + name = "svn" + dirname = ".svn" + repo_name = "checkout" + schemes = ("svn+ssh", "svn+http", "svn+https", "svn+svn", "svn+file") @classmethod - def should_add_vcs_url_prefix(cls, remote_url): + def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: return True @staticmethod - def get_base_rev_args(rev): - return ['-r', rev] + def get_base_rev_args(rev: str) -> List[str]: + return ["-r", rev] @classmethod - def get_revision(cls, location): + def get_revision(cls, location: str) -> str: """ Return the maximum revision for all files under a given location """ # Note: taken from setuptools.command.egg_info revision = 0 - for base, dirs, files in os.walk(location): + for base, dirs, _ in os.walk(location): if cls.dirname not in dirs: dirs[:] = [] - continue # no sense walking uncontrolled subdirs + continue # no sense walking uncontrolled subdirs dirs.remove(cls.dirname) - entries_fn = os.path.join(base, cls.dirname, 'entries') + entries_fn = os.path.join(base, cls.dirname, "entries") if not os.path.exists(entries_fn): # FIXME: should we warn? continue @@ -69,91 +62,95 @@ class Subversion(VersionControl): dirurl, localrev = cls._get_svn_url_rev(base) if base == location: - base = dirurl + '/' # save the root url + assert dirurl is not None + base = dirurl + "/" # save the root url elif not dirurl or not dirurl.startswith(base): dirs[:] = [] - continue # not part of the same svn tree, skip it + continue # not part of the same svn tree, skip it revision = max(revision, localrev) - return revision + return str(revision) @classmethod - def get_netloc_and_auth(cls, netloc, scheme): + def get_netloc_and_auth( + cls, netloc: str, scheme: str + ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: """ This override allows the auth information to be passed to svn via the --username and --password options instead of via the URL. """ - if scheme == 'ssh': + if scheme == "ssh": # The --username and --password options can't be used for # svn+ssh URLs, so keep the auth information in the URL. - return super(Subversion, cls).get_netloc_and_auth(netloc, scheme) + return super().get_netloc_and_auth(netloc, scheme) return split_auth_from_netloc(netloc) @classmethod - def get_url_rev_and_auth(cls, url): - # type: (str) -> Tuple[str, Optional[str], AuthInfo] + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it - url, rev, user_pass = super(Subversion, cls).get_url_rev_and_auth(url) - if url.startswith('ssh://'): - url = 'svn+' + url + url, rev, user_pass = super().get_url_rev_and_auth(url) + if url.startswith("ssh://"): + url = "svn+" + url return url, rev, user_pass @staticmethod - def make_rev_args(username, password): - # type: (Optional[str], Optional[HiddenText]) -> CommandArgs - extra_args = [] # type: CommandArgs + def make_rev_args( + username: Optional[str], password: Optional[HiddenText] + ) -> CommandArgs: + extra_args: CommandArgs = [] if username: - extra_args += ['--username', username] + extra_args += ["--username", username] if password: - extra_args += ['--password', password] + extra_args += ["--password", password] return extra_args @classmethod - def get_remote_url(cls, location): - # In cases where the source is in a subdirectory, not alongside - # setup.py we have to look up in the location until we find a real - # setup.py + def get_remote_url(cls, location: str) -> str: + # In cases where the source is in a subdirectory, we have to look up in + # the location until we find a valid project root. orig_location = location - while not os.path.exists(os.path.join(location, 'setup.py')): + while not is_installable_dir(location): last_location = location location = os.path.dirname(location) if location == last_location: # We've traversed up to the root of the filesystem without - # finding setup.py + # finding a Python project. logger.warning( - "Could not find setup.py for directory %s (tried all " + "Could not find Python project for directory %s (tried all " "parent directories)", orig_location, ) - return None + raise RemoteNotFoundError - return cls._get_svn_url_rev(location)[0] + url, _rev = cls._get_svn_url_rev(location) + if url is None: + raise RemoteNotFoundError + + return url @classmethod - def _get_svn_url_rev(cls, location): + def _get_svn_url_rev(cls, location: str) -> Tuple[Optional[str], int]: from pip._internal.exceptions import InstallationError - entries_path = os.path.join(location, cls.dirname, 'entries') + entries_path = os.path.join(location, cls.dirname, "entries") if os.path.exists(entries_path): with open(entries_path) as f: data = f.read() else: # subversion >= 1.7 does not have the 'entries' file - data = '' - - if (data.startswith('8') or - data.startswith('9') or - data.startswith('10')): - data = list(map(str.splitlines, data.split('\n\x0c\n'))) - del data[0][0] # get rid of the '8' - url = data[0][3] - revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] - elif data.startswith(' 9 and d[9]] + [0] + elif data.startswith(" bool: """Always assume the versions don't match""" return False - def __init__(self, use_interactive=None): - # type: (bool) -> None + def __init__(self, use_interactive: bool = None) -> None: if use_interactive is None: use_interactive = is_console_interactive() self.use_interactive = use_interactive @@ -197,12 +194,11 @@ class Subversion(VersionControl): # Special value definitions: # None: Not evaluated yet. # Empty tuple: Could not parse version. - self._vcs_version = None # type: Optional[Tuple[int, ...]] + self._vcs_version: Optional[Tuple[int, ...]] = None - super(Subversion, self).__init__() + super().__init__() - def call_vcs_version(self): - # type: () -> Tuple[int, ...] + def call_vcs_version(self) -> Tuple[int, ...]: """Query the version of the currently installed Subversion client. :return: A tuple containing the parts of the version information or @@ -214,13 +210,15 @@ class Subversion(VersionControl): # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0 # svn, version 1.7.14 (r1542130) # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu - version_prefix = 'svn, version ' - version = self.run_command(['--version'], show_stdout=False) + # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0) + # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2 + version_prefix = "svn, version " + version = self.run_command(["--version"], show_stdout=False, stdout_only=True) if not version.startswith(version_prefix): return () - version = version[len(version_prefix):].split()[0] - version_list = version.split('.') + version = version[len(version_prefix) :].split()[0] + version_list = version.partition("-")[0].split(".") try: parsed_version = tuple(map(int, version_list)) except ValueError: @@ -228,8 +226,7 @@ class Subversion(VersionControl): return parsed_version - def get_vcs_version(self): - # type: () -> Tuple[int, ...] + def get_vcs_version(self) -> Tuple[int, ...]: """Return the version of the currently installed Subversion client. If the version of the Subversion client has already been queried, @@ -249,15 +246,13 @@ class Subversion(VersionControl): self._vcs_version = vcs_version return vcs_version - def get_remote_call_options(self): - # type: () -> CommandArgs + def get_remote_call_options(self) -> CommandArgs: """Return options to be used on calls to Subversion that contact the server. These options are applicable for the following ``svn`` subcommands used in this class. - checkout - - export - switch - update @@ -266,7 +261,7 @@ class Subversion(VersionControl): if not self.use_interactive: # --non-interactive switch is available since Subversion 0.14.4. # Subversion < 1.8 runs in interactive mode by default. - return ['--non-interactive'] + return ["--non-interactive"] svn_version = self.get_vcs_version() # By default, Subversion >= 1.8 runs in non-interactive mode if @@ -278,54 +273,43 @@ class Subversion(VersionControl): # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip # can't safely add the option if the SVN version is < 1.8 (or unknown). if svn_version >= (1, 8): - return ['--force-interactive'] + return ["--force-interactive"] return [] - def export(self, location, url): - # type: (str, HiddenText) -> None - """Export the svn repository at the url to the destination location""" - url, rev_options = self.get_url_rev_options(url) - - logger.info('Exporting svn repository %s to %s', url, location) - with indent_log(): - if os.path.exists(location): - # Subversion doesn't like to check out over an existing - # directory --force fixes this, but was only added in svn 1.5 - rmtree(location) - cmd_args = make_command( - 'export', self.get_remote_call_options(), - rev_options.to_args(), url, location, - ) - self.run_command(cmd_args, show_stdout=False) - - def fetch_new(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: rev_display = rev_options.to_display() logger.info( - 'Checking out %s%s to %s', + "Checking out %s%s to %s", url, rev_display, display_path(dest), ) cmd_args = make_command( - 'checkout', '-q', self.get_remote_call_options(), - rev_options.to_args(), url, dest, + "checkout", + "-q", + self.get_remote_call_options(), + rev_options.to_args(), + url, + dest, ) self.run_command(cmd_args) - def switch(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: cmd_args = make_command( - 'switch', self.get_remote_call_options(), rev_options.to_args(), - url, dest, + "switch", + self.get_remote_call_options(), + rev_options.to_args(), + url, + dest, ) self.run_command(cmd_args) - def update(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: cmd_args = make_command( - 'update', self.get_remote_call_options(), rev_options.to_args(), + "update", + self.get_remote_call_options(), + rev_options.to_args(), dest, ) self.run_command(cmd_args) diff --git a/venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py b/venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py index 71b4650a252039ec4e1b12e08a339f8207e2cdcc..1139051f3e13c0091b5afa476bf305ddf2737a55 100644 --- a/venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py +++ b/venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py @@ -1,61 +1,67 @@ """Handles all VCS (version control) support""" -from __future__ import absolute_import - -import errno import logging import os import shutil import sys +import urllib.parse +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Tuple, + Type, + Union, +) -from pip._vendor import pkg_resources -from pip._vendor.six.moves.urllib import parse as urllib_parse - +from pip._internal.cli.spinners import SpinnerInterface from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.compat import samefile from pip._internal.utils.misc import ( + HiddenText, ask_path_exists, backup_dir, display_path, hide_url, hide_value, + is_installable_dir, rmtree, ) -from pip._internal.utils.subprocess import call_subprocess, make_command -from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.subprocess import CommandArgs, call_subprocess, make_command from pip._internal.utils.urls import get_url_scheme -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Dict, Iterable, Iterator, List, Mapping, Optional, Text, Tuple, - Type, Union - ) - from pip._internal.cli.spinners import SpinnerInterface - from pip._internal.utils.misc import HiddenText - from pip._internal.utils.subprocess import CommandArgs +if TYPE_CHECKING: + # Literal was introduced in Python 3.8. + # + # TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7. + from typing import Literal - AuthInfo = Tuple[Optional[str], Optional[str]] - -__all__ = ['vcs'] +__all__ = ["vcs"] logger = logging.getLogger(__name__) +AuthInfo = Tuple[Optional[str], Optional[str]] + -def is_url(name): - # type: (Union[str, Text]) -> bool +def is_url(name: str) -> bool: """ Return true if the name looks like a URL. """ scheme = get_url_scheme(name) if scheme is None: return False - return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + return scheme in ["http", "https", "file", "ftp"] + vcs.all_schemes -def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): - # type: (str, str, str, Optional[str]) -> str +def make_vcs_requirement_url( + repo_url: str, rev: str, project_name: str, subdir: Optional[str] = None +) -> str: """ Return the URL for a VCS requirement. @@ -63,37 +69,38 @@ def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). project_name: the (unescaped) project name. """ - egg_project_name = pkg_resources.to_filename(project_name) - req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + egg_project_name = project_name.replace("-", "_") + req = f"{repo_url}@{rev}#egg={egg_project_name}" if subdir: - req += '&subdirectory={}'.format(subdir) + req += f"&subdirectory={subdir}" return req -def find_path_to_setup_from_repo_root(location, repo_root): - # type: (str, str) -> Optional[str] +def find_path_to_project_root_from_repo_root( + location: str, repo_root: str +) -> Optional[str]: """ - Find the path to `setup.py` by searching up the filesystem from `location`. - Return the path to `setup.py` relative to `repo_root`. - Return None if `setup.py` is in `repo_root` or cannot be found. + Find the the Python project's root by searching up the filesystem from + `location`. Return the path to project root relative to `repo_root`. + Return None if the project root is `repo_root`, or cannot be found. """ - # find setup.py + # find project root. orig_location = location - while not os.path.exists(os.path.join(location, 'setup.py')): + while not is_installable_dir(location): last_location = location location = os.path.dirname(location) if location == last_location: # We've traversed up to the root of the filesystem without - # finding setup.py + # finding a Python project. logger.warning( - "Could not find setup.py for directory %s (tried all " + "Could not find a Python project for directory %s (tried all " "parent directories)", orig_location, ) return None - if samefile(repo_root, location): + if os.path.samefile(repo_root, location): return None return os.path.relpath(location, repo_root) @@ -103,7 +110,13 @@ class RemoteNotFoundError(Exception): pass -class RevOptions(object): +class RemoteNotValidError(Exception): + def __init__(self, url: str): + super().__init__(url) + self.url = url + + +class RevOptions: """ Encapsulates a VCS-specific revision to install, along with any VCS @@ -114,11 +127,10 @@ class RevOptions(object): def __init__( self, - vc_class, # type: Type[VersionControl] - rev=None, # type: Optional[str] - extra_args=None, # type: Optional[CommandArgs] - ): - # type: (...) -> None + vc_class: Type["VersionControl"], + rev: Optional[str] = None, + extra_args: Optional[CommandArgs] = None, + ) -> None: """ Args: vc_class: a VersionControl subclass. @@ -131,26 +143,23 @@ class RevOptions(object): self.extra_args = extra_args self.rev = rev self.vc_class = vc_class - self.branch_name = None # type: Optional[str] + self.branch_name: Optional[str] = None - def __repr__(self): - # type: () -> str - return ''.format(self.vc_class.name, self.rev) + def __repr__(self) -> str: + return f"" @property - def arg_rev(self): - # type: () -> Optional[str] + def arg_rev(self) -> Optional[str]: if self.rev is None: return self.vc_class.default_arg_rev return self.rev - def to_args(self): - # type: () -> CommandArgs + def to_args(self) -> CommandArgs: """ Return the VCS-specific command arguments. """ - args = [] # type: CommandArgs + args: CommandArgs = [] rev = self.arg_rev if rev is not None: args += self.vc_class.get_base_rev_args(rev) @@ -158,15 +167,13 @@ class RevOptions(object): return args - def to_display(self): - # type: () -> str + def to_display(self) -> str: if not self.rev: - return '' + return "" - return ' (to revision {})'.format(self.rev) + return f" (to revision {self.rev})" - def make_new(self, rev): - # type: (str) -> RevOptions + def make_new(self, rev: str) -> "RevOptions": """ Make a copy of the current instance, but with a new rev. @@ -176,58 +183,47 @@ class RevOptions(object): return self.vc_class.make_rev_options(rev, extra_args=self.extra_args) -class VcsSupport(object): - _registry = {} # type: Dict[str, VersionControl] - schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] +class VcsSupport: + _registry: Dict[str, "VersionControl"] = {} + schemes = ["ssh", "git", "hg", "bzr", "sftp", "svn"] - def __init__(self): - # type: () -> None + def __init__(self) -> None: # Register more schemes with urlparse for various version control # systems - urllib_parse.uses_netloc.extend(self.schemes) - # Python >= 2.7.4, 3.3 doesn't have uses_fragment - if getattr(urllib_parse, 'uses_fragment', None): - urllib_parse.uses_fragment.extend(self.schemes) - super(VcsSupport, self).__init__() - - def __iter__(self): - # type: () -> Iterator[str] + urllib.parse.uses_netloc.extend(self.schemes) + super().__init__() + + def __iter__(self) -> Iterator[str]: return self._registry.__iter__() @property - def backends(self): - # type: () -> List[VersionControl] + def backends(self) -> List["VersionControl"]: return list(self._registry.values()) @property - def dirnames(self): - # type: () -> List[str] + def dirnames(self) -> List[str]: return [backend.dirname for backend in self.backends] @property - def all_schemes(self): - # type: () -> List[str] - schemes = [] # type: List[str] + def all_schemes(self) -> List[str]: + schemes: List[str] = [] for backend in self.backends: schemes.extend(backend.schemes) return schemes - def register(self, cls): - # type: (Type[VersionControl]) -> None - if not hasattr(cls, 'name'): - logger.warning('Cannot register VCS %s', cls.__name__) + def register(self, cls: Type["VersionControl"]) -> None: + if not hasattr(cls, "name"): + logger.warning("Cannot register VCS %s", cls.__name__) return if cls.name not in self._registry: self._registry[cls.name] = cls() - logger.debug('Registered VCS backend: %s', cls.name) + logger.debug("Registered VCS backend: %s", cls.name) - def unregister(self, name): - # type: (str) -> None + def unregister(self, name: str) -> None: if name in self._registry: del self._registry[name] - def get_backend_for_dir(self, location): - # type: (str) -> Optional[VersionControl] + def get_backend_for_dir(self, location: str) -> Optional["VersionControl"]: """ Return a VersionControl object if a repository of that type is found at the given directory. @@ -237,8 +233,7 @@ class VcsSupport(object): repo_path = vcs_backend.get_repository_root(location) if not repo_path: continue - logger.debug('Determine that %s uses VCS: %s', - location, vcs_backend.name) + logger.debug("Determine that %s uses VCS: %s", location, vcs_backend.name) vcs_backends[repo_path] = vcs_backend if not vcs_backends: @@ -251,8 +246,7 @@ class VcsSupport(object): inner_most_repo_path = max(vcs_backends, key=len) return vcs_backends[inner_most_repo_path] - def get_backend_for_scheme(self, scheme): - # type: (str) -> Optional[VersionControl] + def get_backend_for_scheme(self, scheme: str) -> Optional["VersionControl"]: """ Return a VersionControl object or None. """ @@ -261,8 +255,7 @@ class VcsSupport(object): return vcs_backend return None - def get_backend(self, name): - # type: (str) -> Optional[VersionControl] + def get_backend(self, name: str) -> Optional["VersionControl"]: """ Return a VersionControl object or None. """ @@ -273,45 +266,41 @@ class VcsSupport(object): vcs = VcsSupport() -class VersionControl(object): - name = '' - dirname = '' - repo_name = '' +class VersionControl: + name = "" + dirname = "" + repo_name = "" # List of supported schemes for this Version Control - schemes = () # type: Tuple[str, ...] + schemes: Tuple[str, ...] = () # Iterable of environment variable names to pass to call_subprocess(). - unset_environ = () # type: Tuple[str, ...] - default_arg_rev = None # type: Optional[str] + unset_environ: Tuple[str, ...] = () + default_arg_rev: Optional[str] = None @classmethod - def should_add_vcs_url_prefix(cls, remote_url): - # type: (str) -> bool + def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: """ Return whether the vcs prefix (e.g. "git+") should be added to a repository's remote url when used in a requirement. """ - return not remote_url.lower().startswith('{}:'.format(cls.name)) + return not remote_url.lower().startswith(f"{cls.name}:") @classmethod - def get_subdirectory(cls, location): - # type: (str) -> Optional[str] + def get_subdirectory(cls, location: str) -> Optional[str]: """ - Return the path to setup.py, relative to the repo root. - Return None if setup.py is in the repo root. + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. """ return None @classmethod - def get_requirement_revision(cls, repo_dir): - # type: (str) -> str + def get_requirement_revision(cls, repo_dir: str) -> str: """ Return the revision string that should be used in a requirement. """ return cls.get_revision(repo_dir) @classmethod - def get_src_requirement(cls, repo_dir, project_name): - # type: (str, str) -> Optional[str] + def get_src_requirement(cls, repo_dir: str, project_name: str) -> str: """ Return the requirement string to use to redownload the files currently at the given repository directory. @@ -324,22 +313,18 @@ class VersionControl(object): {repository_url}@{revision}#egg={project_name} """ repo_url = cls.get_remote_url(repo_dir) - if repo_url is None: - return None if cls.should_add_vcs_url_prefix(repo_url): - repo_url = '{}+{}'.format(cls.name, repo_url) + repo_url = f"{cls.name}+{repo_url}" revision = cls.get_requirement_revision(repo_dir) subdir = cls.get_subdirectory(repo_dir) - req = make_vcs_requirement_url(repo_url, revision, project_name, - subdir=subdir) + req = make_vcs_requirement_url(repo_url, revision, project_name, subdir=subdir) return req @staticmethod - def get_base_rev_args(rev): - # type: (str) -> List[str] + def get_base_rev_args(rev: str) -> List[str]: """ Return the base revision arguments for a vcs command. @@ -348,8 +333,7 @@ class VersionControl(object): """ raise NotImplementedError - def is_immutable_rev_checkout(self, url, dest): - # type: (str, str) -> bool + def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: """ Return true if the commit hash checked out at dest matches the revision in url. @@ -363,8 +347,9 @@ class VersionControl(object): return False @classmethod - def make_rev_options(cls, rev=None, extra_args=None): - # type: (Optional[str], Optional[CommandArgs]) -> RevOptions + def make_rev_options( + cls, rev: Optional[str] = None, extra_args: Optional[CommandArgs] = None + ) -> RevOptions: """ Return a RevOptions object. @@ -375,28 +360,18 @@ class VersionControl(object): return RevOptions(cls, rev, extra_args=extra_args) @classmethod - def _is_local_repository(cls, repo): - # type: (str) -> bool + def _is_local_repository(cls, repo: str) -> bool: """ - posix absolute paths start with os.path.sep, - win32 ones start with drive (like c:\\folder) + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) """ drive, tail = os.path.splitdrive(repo) return repo.startswith(os.path.sep) or bool(drive) - def export(self, location, url): - # type: (str, HiddenText) -> None - """ - Export the repository at the url to the destination location - i.e. only download the files, without vcs informations - - :param url: the repository URL starting with a vcs prefix. - """ - raise NotImplementedError - @classmethod - def get_netloc_and_auth(cls, netloc, scheme): - # type: (str, str) -> Tuple[str, Tuple[Optional[str], Optional[str]]] + def get_netloc_and_auth( + cls, netloc: str, scheme: str + ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: """ Parse the repository URL's netloc, and return the new netloc to use along with auth information. @@ -415,53 +390,52 @@ class VersionControl(object): return netloc, (None, None) @classmethod - def get_url_rev_and_auth(cls, url): - # type: (str) -> Tuple[str, Optional[str], AuthInfo] + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: """ Parse the repository URL to use, and return the URL, revision, and auth info to use. Returns: (url, rev, (username, password)). """ - scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) - if '+' not in scheme: + scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) + if "+" not in scheme: raise ValueError( "Sorry, {!r} is a malformed VCS url. " "The format is +://, " "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) ) # Remove the vcs prefix. - scheme = scheme.split('+', 1)[1] + scheme = scheme.split("+", 1)[1] netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) rev = None - if '@' in path: - path, rev = path.rsplit('@', 1) + if "@" in path: + path, rev = path.rsplit("@", 1) if not rev: raise InstallationError( "The URL {!r} has an empty revision (after @) " "which is not supported. Include a revision after @ " "or remove @ from the URL.".format(url) ) - url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + url = urllib.parse.urlunsplit((scheme, netloc, path, query, "")) return url, rev, user_pass @staticmethod - def make_rev_args(username, password): - # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + def make_rev_args( + username: Optional[str], password: Optional[HiddenText] + ) -> CommandArgs: """ Return the RevOptions "extra arguments" to use in obtain(). """ return [] - def get_url_rev_options(self, url): - # type: (HiddenText) -> Tuple[HiddenText, RevOptions] + def get_url_rev_options(self, url: HiddenText) -> Tuple[HiddenText, RevOptions]: """ - Return the URL and RevOptions object to use in obtain() and in - some cases export(), as a tuple (url, rev_options). + Return the URL and RevOptions object to use in obtain(), + as a tuple (url, rev_options). """ secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) username, secret_password = user_pass - password = None # type: Optional[HiddenText] + password: Optional[HiddenText] = None if secret_password is not None: password = hide_value(secret_password) extra_args = self.make_rev_args(username, password) @@ -470,24 +444,21 @@ class VersionControl(object): return hide_url(secret_url), rev_options @staticmethod - def normalize_url(url): - # type: (str) -> str + def normalize_url(url: str) -> str: """ Normalize a URL for comparison by unquoting it and removing any trailing slash. """ - return urllib_parse.unquote(url).rstrip('/') + return urllib.parse.unquote(url).rstrip("/") @classmethod - def compare_urls(cls, url1, url2): - # type: (str, str) -> bool + def compare_urls(cls, url1: str, url2: str) -> bool: """ Compare two repo URLs for identity, ignoring incidental differences. """ - return (cls.normalize_url(url1) == cls.normalize_url(url2)) + return cls.normalize_url(url1) == cls.normalize_url(url2) - def fetch_new(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: """ Fetch a revision from a repository, in the case that this is the first fetch from the repository. @@ -498,8 +469,7 @@ class VersionControl(object): """ raise NotImplementedError - def switch(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: """ Switch the repo at ``dest`` to point to ``URL``. @@ -508,8 +478,7 @@ class VersionControl(object): """ raise NotImplementedError - def update(self, dest, url, rev_options): - # type: (str, HiddenText, RevOptions) -> None + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: """ Update an already-existing repo to the given ``rev_options``. @@ -519,8 +488,7 @@ class VersionControl(object): raise NotImplementedError @classmethod - def is_commit_id_equal(cls, dest, name): - # type: (str, Optional[str]) -> bool + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: """ Return whether the id of the current commit equals the given name. @@ -530,8 +498,7 @@ class VersionControl(object): """ raise NotImplementedError - def obtain(self, dest, url): - # type: (str, HiddenText) -> None + def obtain(self, dest: str, url: HiddenText) -> None: """ Install or update in editable mode the package represented by this VersionControl object. @@ -550,73 +517,68 @@ class VersionControl(object): existing_url = self.get_remote_url(dest) if self.compare_urls(existing_url, url.secret): logger.debug( - '%s in %s exists, and has correct URL (%s)', + "%s in %s exists, and has correct URL (%s)", self.repo_name.title(), display_path(dest), url, ) if not self.is_commit_id_equal(dest, rev_options.rev): logger.info( - 'Updating %s %s%s', + "Updating %s %s%s", display_path(dest), self.repo_name, rev_display, ) self.update(dest, url, rev_options) else: - logger.info('Skipping because already up-to-date.') + logger.info("Skipping because already up-to-date.") return logger.warning( - '%s %s in %s exists with URL %s', + "%s %s in %s exists with URL %s", self.name, self.repo_name, display_path(dest), existing_url, ) - prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', - ('s', 'i', 'w', 'b')) + prompt = ("(s)witch, (i)gnore, (w)ipe, (b)ackup ", ("s", "i", "w", "b")) else: logger.warning( - 'Directory %s already exists, and is not a %s %s.', + "Directory %s already exists, and is not a %s %s.", dest, self.name, self.repo_name, ) # https://github.com/python/mypy/issues/1174 - prompt = ('(i)gnore, (w)ipe, (b)ackup ', # type: ignore - ('i', 'w', 'b')) + prompt = ("(i)gnore, (w)ipe, (b)ackup ", ("i", "w", "b")) # type: ignore logger.warning( - 'The plan is to install the %s repository %s', + "The plan is to install the %s repository %s", self.name, url, ) - response = ask_path_exists('What to do? {}'.format( - prompt[0]), prompt[1]) + response = ask_path_exists("What to do? {}".format(prompt[0]), prompt[1]) - if response == 'a': + if response == "a": sys.exit(-1) - if response == 'w': - logger.warning('Deleting %s', display_path(dest)) + if response == "w": + logger.warning("Deleting %s", display_path(dest)) rmtree(dest) self.fetch_new(dest, url, rev_options) return - if response == 'b': + if response == "b": dest_dir = backup_dir(dest) - logger.warning( - 'Backing up %s to %s', display_path(dest), dest_dir, - ) + logger.warning("Backing up %s to %s", display_path(dest), dest_dir) shutil.move(dest, dest_dir) self.fetch_new(dest, url, rev_options) return # Do nothing if the response is "i". - if response == 's': + if response == "s": logger.info( - 'Switching %s %s to %s%s', + "Switching %s %s to %s%s", self.repo_name, display_path(dest), url, @@ -624,8 +586,7 @@ class VersionControl(object): ) self.switch(dest, url, rev_options) - def unpack(self, location, url): - # type: (str, HiddenText) -> None + def unpack(self, location: str, url: HiddenText) -> None: """ Clean up current location and download the url repository (and vcs infos) into location @@ -637,8 +598,7 @@ class VersionControl(object): self.obtain(location, url=url) @classmethod - def get_remote_url(cls, location): - # type: (str) -> str + def get_remote_url(cls, location: str) -> str: """ Return the url used at location @@ -648,8 +608,7 @@ class VersionControl(object): raise NotImplementedError @classmethod - def get_revision(cls, location): - # type: (str) -> str + def get_revision(cls, location: str) -> str: """ Return the current commit id of the files at the given location. """ @@ -658,17 +617,17 @@ class VersionControl(object): @classmethod def run_command( cls, - cmd, # type: Union[List[str], CommandArgs] - show_stdout=True, # type: bool - cwd=None, # type: Optional[str] - on_returncode='raise', # type: str - extra_ok_returncodes=None, # type: Optional[Iterable[int]] - command_desc=None, # type: Optional[str] - extra_environ=None, # type: Optional[Mapping[str, Any]] - spinner=None, # type: Optional[SpinnerInterface] - log_failed_cmd=True # type: bool - ): - # type: (...) -> Text + cmd: Union[List[str], CommandArgs], + show_stdout: bool = True, + cwd: Optional[str] = None, + on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", + extra_ok_returncodes: Optional[Iterable[int]] = None, + command_desc: Optional[str] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + spinner: Optional[SpinnerInterface] = None, + log_failed_cmd: bool = True, + stdout_only: bool = False, + ) -> str: """ Run a VCS subcommand This is simply a wrapper around call_subprocess that adds the VCS @@ -676,38 +635,49 @@ class VersionControl(object): """ cmd = make_command(cls.name, *cmd) try: - return call_subprocess(cmd, show_stdout, cwd, - on_returncode=on_returncode, - extra_ok_returncodes=extra_ok_returncodes, - command_desc=command_desc, - extra_environ=extra_environ, - unset_environ=cls.unset_environ, - spinner=spinner, - log_failed_cmd=log_failed_cmd) - except OSError as e: + return call_subprocess( + cmd, + show_stdout, + cwd, + on_returncode=on_returncode, + extra_ok_returncodes=extra_ok_returncodes, + command_desc=command_desc, + extra_environ=extra_environ, + unset_environ=cls.unset_environ, + spinner=spinner, + log_failed_cmd=log_failed_cmd, + stdout_only=stdout_only, + ) + except FileNotFoundError: # errno.ENOENT = no such file or directory # In other words, the VCS executable isn't available - if e.errno == errno.ENOENT: - raise BadCommand( - 'Cannot find command {cls.name!r} - do you have ' - '{cls.name!r} installed and in your ' - 'PATH?'.format(**locals())) - else: - raise # re-raise exception if a different error occurred + raise BadCommand( + f"Cannot find command {cls.name!r} - do you have " + f"{cls.name!r} installed and in your PATH?" + ) + except PermissionError: + # errno.EACCES = Permission denied + # This error occurs, for instance, when the command is installed + # only for another user. So, the current user don't have + # permission to call the other user command. + raise BadCommand( + f"No permission to execute {cls.name!r} - install it " + f"locally, globally (ask admin), or check your PATH. " + f"See possible solutions at " + f"https://pip.pypa.io/en/latest/reference/pip_freeze/" + f"#fixing-permission-denied." + ) @classmethod - def is_repository_directory(cls, path): - # type: (str) -> bool + def is_repository_directory(cls, path: str) -> bool: """ Return whether a directory path is a repository directory. """ - logger.debug('Checking in %s for %s (%s)...', - path, cls.dirname, cls.name) + logger.debug("Checking in %s for %s (%s)...", path, cls.dirname, cls.name) return os.path.exists(os.path.join(path, cls.dirname)) @classmethod - def get_repository_root(cls, location): - # type: (str) -> Optional[str] + def get_repository_root(cls, location: str) -> Optional[str]: """ Return the "root" (top-level) directory controlled by the vcs, or `None` if the directory is not in any. diff --git a/venv/Lib/site-packages/pip/_internal/wheel_builder.py b/venv/Lib/site-packages/pip/_internal/wheel_builder.py index fcaeeb6c3f404d334c7b871bb93c76797419ba4d..a9123a0f1f6aa19065cb4184820ed8c219e415b2 100644 --- a/venv/Lib/site-packages/pip/_internal/wheel_builder.py +++ b/venv/Lib/site-packages/pip/_internal/wheel_builder.py @@ -1,43 +1,41 @@ """Orchestrator for building wheels from InstallRequirements. """ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import logging import os.path import re import shutil +from typing import Any, Callable, Iterable, List, Optional, Tuple + +from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version +from pip._vendor.packaging.version import InvalidVersion, Version +from pip._internal.cache import WheelCache +from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel +from pip._internal.metadata import FilesystemWheel, get_wheel_distribution from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel from pip._internal.operations.build.wheel import build_wheel_pep517 +from pip._internal.operations.build.wheel_editable import build_wheel_editable from pip._internal.operations.build.wheel_legacy import build_wheel_legacy +from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed from pip._internal.utils.setuptools_build import make_setuptools_clean_args from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url from pip._internal.vcs import vcs -if MYPY_CHECK_RUNNING: - from typing import ( - Any, Callable, Iterable, List, Optional, Pattern, Tuple, - ) - - from pip._internal.cache import WheelCache - from pip._internal.req.req_install import InstallRequirement +logger = logging.getLogger(__name__) - BinaryAllowedPredicate = Callable[[InstallRequirement], bool] - BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] +_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE) -logger = logging.getLogger(__name__) +BinaryAllowedPredicate = Callable[[InstallRequirement], bool] +BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] -def _contains_egg_info( - s, _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): - # type: (str, Pattern[str]) -> bool +def _contains_egg_info(s: str) -> bool: """Determine whether the string looks like an egg_info. :param s: The string to parse. E.g. foo-2.1 @@ -46,11 +44,10 @@ def _contains_egg_info( def _should_build( - req, # type: InstallRequirement - need_wheel, # type: bool - check_binary_allowed, # type: BinaryAllowedPredicate -): - # type: (...) -> bool + req: InstallRequirement, + need_wheel: bool, + check_binary_allowed: BinaryAllowedPredicate, +) -> bool: """Return whether an InstallRequirement should be built into a wheel.""" if req.constraint: # never build requirements that are merely constraints @@ -58,7 +55,8 @@ def _should_build( if req.is_wheel: if need_wheel: logger.info( - 'Skipping %s, due to already being wheel.', req.name, + "Skipping %s, due to already being wheel.", + req.name, ) return False @@ -69,21 +67,29 @@ def _should_build( # From this point, this concerns the pip install command only # (need_wheel=False). - if req.editable or not req.source_dir: + if not req.source_dir: return False + if req.editable: + # we only build PEP 660 editable requirements + return req.supports_pyproject_editable() + + if req.use_pep517: + return True + if not check_binary_allowed(req): logger.info( - "Skipping wheel build for %s, due to binaries " - "being disabled for it.", req.name, + "Skipping wheel build for %s, due to binaries being disabled for it.", + req.name, ) return False - if not req.use_pep517 and not is_wheel_installed(): + if not is_wheel_installed(): # we don't build legacy requirements if wheel is not installed logger.info( - "Using legacy setup.py install for %s, " - "since package 'wheel' is not installed.", req.name, + "Using legacy 'setup.py install' for %s, " + "since package 'wheel' is not installed.", + req.name, ) return False @@ -91,38 +97,30 @@ def _should_build( def should_build_for_wheel_command( - req, # type: InstallRequirement -): - # type: (...) -> bool - return _should_build( - req, need_wheel=True, check_binary_allowed=_always_true - ) + req: InstallRequirement, +) -> bool: + return _should_build(req, need_wheel=True, check_binary_allowed=_always_true) def should_build_for_install_command( - req, # type: InstallRequirement - check_binary_allowed, # type: BinaryAllowedPredicate -): - # type: (...) -> bool + req: InstallRequirement, + check_binary_allowed: BinaryAllowedPredicate, +) -> bool: return _should_build( req, need_wheel=False, check_binary_allowed=check_binary_allowed ) def _should_cache( - req, # type: InstallRequirement -): - # type: (...) -> Optional[bool] + req: InstallRequirement, +) -> Optional[bool]: """ Return whether a built InstallRequirement can be stored in the persistent wheel cache, assuming the wheel cache is available, and _should_build() has determined a wheel needs to be built. """ - if not should_build_for_install_command( - req, check_binary_allowed=_always_true - ): - # never cache if pip install would not have built - # (editable mode, etc) + if req.editable or not req.source_dir: + # never cache editable requirements return False if req.link and req.link.is_vcs: @@ -136,6 +134,7 @@ def _should_cache( return True return False + assert req.link base, ext = req.link.splitext() if _contains_egg_info(base): return True @@ -145,14 +144,14 @@ def _should_cache( def _get_cache_dir( - req, # type: InstallRequirement - wheel_cache, # type: WheelCache -): - # type: (...) -> str + req: InstallRequirement, + wheel_cache: WheelCache, +) -> str: """Return the persistent or temporary cache directory where the built wheel need to be stored. """ cache_available = bool(wheel_cache.cache_dir) + assert req.link if cache_available and _should_cache(req): cache_dir = wheel_cache.get_path_for_link(req.link) else: @@ -160,54 +159,112 @@ def _get_cache_dir( return cache_dir -def _always_true(_): - # type: (Any) -> bool +def _always_true(_: Any) -> bool: return True +def _verify_one(req: InstallRequirement, wheel_path: str) -> None: + canonical_name = canonicalize_name(req.name or "") + w = Wheel(os.path.basename(wheel_path)) + if canonicalize_name(w.name) != canonical_name: + raise InvalidWheelFilename( + "Wheel has unexpected file name: expected {!r}, " + "got {!r}".format(canonical_name, w.name), + ) + dist = get_wheel_distribution(FilesystemWheel(wheel_path), canonical_name) + dist_verstr = str(dist.version) + if canonicalize_version(dist_verstr) != canonicalize_version(w.version): + raise InvalidWheelFilename( + "Wheel has unexpected file name: expected {!r}, " + "got {!r}".format(dist_verstr, w.version), + ) + metadata_version_value = dist.metadata_version + if metadata_version_value is None: + raise UnsupportedWheel("Missing Metadata-Version") + try: + metadata_version = Version(metadata_version_value) + except InvalidVersion: + msg = f"Invalid Metadata-Version: {metadata_version_value}" + raise UnsupportedWheel(msg) + if metadata_version >= Version("1.2") and not isinstance(dist.version, Version): + raise UnsupportedWheel( + "Metadata 1.2 mandates PEP 440 version, " + "but {!r} is not".format(dist_verstr) + ) + + def _build_one( - req, # type: InstallRequirement - output_dir, # type: str - build_options, # type: List[str] - global_options, # type: List[str] -): - # type: (...) -> Optional[str] + req: InstallRequirement, + output_dir: str, + verify: bool, + build_options: List[str], + global_options: List[str], + editable: bool, +) -> Optional[str]: """Build one wheel. :return: The filename of the built wheel, or None if the build failed. """ + artifact = "editable" if editable else "wheel" try: ensure_dir(output_dir) except OSError as e: logger.warning( - "Building wheel for %s failed: %s", - req.name, e, + "Building %s for %s failed: %s", + artifact, + req.name, + e, ) return None # Install build deps into temporary directory (PEP 518) with req.build_env: - return _build_one_inside_env( - req, output_dir, build_options, global_options + wheel_path = _build_one_inside_env( + req, output_dir, build_options, global_options, editable ) + if wheel_path and verify: + try: + _verify_one(req, wheel_path) + except (InvalidWheelFilename, UnsupportedWheel) as e: + logger.warning("Built %s for %s is invalid: %s", artifact, req.name, e) + return None + return wheel_path def _build_one_inside_env( - req, # type: InstallRequirement - output_dir, # type: str - build_options, # type: List[str] - global_options, # type: List[str] -): - # type: (...) -> Optional[str] + req: InstallRequirement, + output_dir: str, + build_options: List[str], + global_options: List[str], + editable: bool, +) -> Optional[str]: with TempDirectory(kind="wheel") as temp_dir: + assert req.name if req.use_pep517: - wheel_path = build_wheel_pep517( - name=req.name, - backend=req.pep517_backend, - metadata_directory=req.metadata_directory, - build_options=build_options, - tempd=temp_dir.path, - ) + assert req.metadata_directory + assert req.pep517_backend + if global_options: + logger.warning( + "Ignoring --global-option when building %s using PEP 517", req.name + ) + if build_options: + logger.warning( + "Ignoring --build-option when building %s using PEP 517", req.name + ) + if editable: + wheel_path = build_wheel_editable( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + tempd=temp_dir.path, + ) + else: + wheel_path = build_wheel_pep517( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + tempd=temp_dir.path, + ) else: wheel_path = build_wheel_legacy( name=req.name, @@ -224,16 +281,20 @@ def _build_one_inside_env( try: wheel_hash, length = hash_file(wheel_path) shutil.move(wheel_path, dest_path) - logger.info('Created wheel for %s: ' - 'filename=%s size=%d sha256=%s', - req.name, wheel_name, length, - wheel_hash.hexdigest()) - logger.info('Stored in directory: %s', output_dir) + logger.info( + "Created wheel for %s: filename=%s size=%d sha256=%s", + req.name, + wheel_name, + length, + wheel_hash.hexdigest(), + ) + logger.info("Stored in directory: %s", output_dir) return dest_path except Exception as e: logger.warning( "Building wheel for %s failed: %s", - req.name, e, + req.name, + e, ) # Ignore return, we can't do anything else useful. if not req.use_pep517: @@ -241,29 +302,28 @@ def _build_one_inside_env( return None -def _clean_one_legacy(req, global_options): - # type: (InstallRequirement, List[str]) -> bool +def _clean_one_legacy(req: InstallRequirement, global_options: List[str]) -> bool: clean_args = make_setuptools_clean_args( req.setup_py_path, global_options=global_options, ) - logger.info('Running setup.py clean for %s', req.name) + logger.info("Running setup.py clean for %s", req.name) try: call_subprocess(clean_args, cwd=req.source_dir) return True except Exception: - logger.error('Failed cleaning build dir for %s', req.name) + logger.error("Failed cleaning build dir for %s", req.name) return False def build( - requirements, # type: Iterable[InstallRequirement] - wheel_cache, # type: WheelCache - build_options, # type: List[str] - global_options, # type: List[str] -): - # type: (...) -> BuildResult + requirements: Iterable[InstallRequirement], + wheel_cache: WheelCache, + verify: bool, + build_options: List[str], + global_options: List[str], +) -> BuildResult: """Build wheels. :return: The list of InstallRequirement that succeeded to build and @@ -274,16 +334,22 @@ def build( # Build the wheels. logger.info( - 'Building wheels for collected packages: %s', - ', '.join(req.name for req in requirements), + "Building wheels for collected packages: %s", + ", ".join(req.name for req in requirements), # type: ignore ) with indent_log(): build_successes, build_failures = [], [] for req in requirements: + assert req.name cache_dir = _get_cache_dir(req, wheel_cache) wheel_file = _build_one( - req, cache_dir, build_options, global_options + req, + cache_dir, + verify, + build_options, + global_options, + req.editable and req.permit_editable_wheels, ) if wheel_file: # Update the link for this. @@ -297,13 +363,13 @@ def build( # notify success/failure if build_successes: logger.info( - 'Successfully built %s', - ' '.join([req.name for req in build_successes]), + "Successfully built %s", + " ".join([req.name for req in build_successes]), # type: ignore ) if build_failures: logger.info( - 'Failed to build %s', - ' '.join([req.name for req in build_failures]), + "Failed to build %s", + " ".join([req.name for req in build_failures]), # type: ignore ) # Return a list of requirements that failed to build return build_successes, build_failures diff --git a/venv/Lib/site-packages/pip/_vendor/__init__.py b/venv/Lib/site-packages/pip/_vendor/__init__.py index c3db83ff6aa49908e9d691c853f762bfb0fe09c4..3843cb099554e725db59d073cae4f85c6eed69c1 100644 --- a/venv/Lib/site-packages/pip/_vendor/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/__init__.py @@ -58,11 +58,9 @@ if DEBUNDLED: sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path # Actually alias all of our vendored dependencies. - vendored("appdirs") vendored("cachecontrol") vendored("certifi") vendored("colorama") - vendored("contextlib2") vendored("distlib") vendored("distro") vendored("html5lib") @@ -75,8 +73,8 @@ if DEBUNDLED: vendored("packaging.specifiers") vendored("pep517") vendored("pkg_resources") + vendored("platformdirs") vendored("progress") - vendored("retrying") vendored("requests") vendored("requests.exceptions") vendored("requests.packages") @@ -108,7 +106,6 @@ if DEBUNDLED: vendored("requests.packages.urllib3.util.timeout") vendored("requests.packages.urllib3.util.url") vendored("resolvelib") - vendored("toml") - vendored("toml.encoder") - vendored("toml.decoder") + vendored("tenacity") + vendored("tomli") vendored("urllib3") diff --git a/venv/Lib/site-packages/pip/_vendor/appdirs.py b/venv/Lib/site-packages/pip/_vendor/appdirs.py deleted file mode 100644 index 8bd9c9ca0b83e75b0064c2c3e17d4e738e9ebb48..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/appdirs.py +++ /dev/null @@ -1,633 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (c) 2005-2010 ActiveState Software Inc. -# Copyright (c) 2013 Eddy Petrișor - -"""Utilities for determining application-specific dirs. - -See for details and usage. -""" -# Dev Notes: -# - MSDN on where to store app data files: -# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 -# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html -# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - -__version_info__ = (1, 4, 3) -__version__ = '.'.join(map(str, __version_info__)) - - -import sys -import os - -PY3 = sys.version_info[0] == 3 - -if PY3: - unicode = str - -if sys.platform.startswith('java'): - import platform - os_name = platform.java_ver()[3][0] - if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. - system = 'win32' - elif os_name.startswith('Mac'): # "Mac OS X", etc. - system = 'darwin' - else: # "Linux", "SunOS", "FreeBSD", etc. - # Setting this to "linux2" is not ideal, but only Windows or Mac - # are actually checked for and the rest of the module expects - # *sys.platform* style strings. - system = 'linux2' -elif sys.platform == 'cli' and os.name == 'nt': - # Detect Windows in IronPython to match pip._internal.utils.compat.WINDOWS - # Discussion: - system = 'win32' -else: - system = sys.platform - - - -def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user data directories are: - Mac OS X: ~/Library/Application Support/ # or ~/.config/, if the other does not exist - Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined - Win XP (not roaming): C:\Documents and Settings\\Application Data\\ - Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ - Win 7 (not roaming): C:\Users\\AppData\Local\\ - Win 7 (roaming): C:\Users\\AppData\Roaming\\ - - For Unix, we follow the XDG spec and support $XDG_DATA_HOME. - That means, by default "~/.local/share/". - """ - if system == "win32": - if appauthor is None: - appauthor = appname - const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" - path = os.path.normpath(_get_win_folder(const)) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('~/Library/Application Support/') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of data dirs should be - returned. By default, the first item from XDG_DATA_DIRS is - returned, or '/usr/local/share/', - if XDG_DATA_DIRS is not set - - Typical site data directories are: - Mac OS X: /Library/Application Support/ - Unix: /usr/local/share/ or /usr/share/ - Win XP: C:\Documents and Settings\All Users\Application Data\\ - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. - - For Unix, this is using the $XDG_DATA_DIRS[0] default. - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('/Library/Application Support') - if appname: - path = os.path.join(path, appname) - else: - # XDG default for $XDG_DATA_DIRS - # only first, if multipath is False - path = os.getenv('XDG_DATA_DIRS', - os.pathsep.join(['/usr/local/share', '/usr/share'])) - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.path.join(x, appname) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - if appname and version: - path = os.path.join(path, version) - return path - - -def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific config dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user config directories are: - Mac OS X: same as user_data_dir - Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. - That means, by default "~/.config/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -# for the discussion regarding site_config_dir locations -# see -def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of config dirs should be - returned. By default, the first item from XDG_CONFIG_DIRS is - returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set - - Typical site config directories are: - Mac OS X: same as site_data_dir - Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in - $XDG_CONFIG_DIRS - Win *: same as site_data_dir - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - - For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system in ["win32", "darwin"]: - path = site_data_dir(appname, appauthor) - if appname and version: - path = os.path.join(path, version) - else: - # XDG default for $XDG_CONFIG_DIRS (missing or empty) - # see - # only first, if multipath is False - path = os.getenv('XDG_CONFIG_DIRS') or '/etc/xdg' - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) if x] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.path.join(x, appname) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - -def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific cache dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Cache" to the base app data dir for Windows. See - discussion below. - - Typical user cache directories are: - Mac OS X: ~/Library/Caches/ - Unix: ~/.cache/ (XDG default) - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache - Vista: C:\Users\\AppData\Local\\\Cache - - On Windows the only suggestion in the MSDN docs is that local settings go in - the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming - app data dir (the default returned by `user_data_dir` above). Apps typically - put cache data somewhere *under* the given dir here. Some examples: - ...\Mozilla\Firefox\Profiles\\Cache - ...\Acme\SuperApp\Cache\1.0 - OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. - This can be disabled with the `opinion=False` option. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) - # When using Python 2, return paths as bytes on Windows like we do on - # other operating systems. See helper function docs for more details. - if not PY3 and isinstance(path, unicode): - path = _win_path_to_bytes(path) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - if opinion: - path = os.path.join(path, "Cache") - elif system == 'darwin': - path = os.path.expanduser('~/Library/Caches') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific state dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user state directories are: - Mac OS X: same as user_data_dir - Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow this Debian proposal - to extend the XDG spec and support $XDG_STATE_HOME. - - That means, by default "~/.local/state/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific log dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Logs" to the base app data dir for Windows, and "log" to the - base cache dir for Unix. See discussion below. - - Typical user log directories are: - Mac OS X: ~/Library/Logs/ - Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs - Vista: C:\Users\\AppData\Local\\\Logs - - On Windows the only suggestion in the MSDN docs is that local settings - go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in - examples of what some windows apps use for a logs dir.) - - OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` - value for Windows and appends "log" to the user cache dir for Unix. - This can be disabled with the `opinion=False` option. - """ - if system == "darwin": - path = os.path.join( - os.path.expanduser('~/Library/Logs'), - appname) - elif system == "win32": - path = user_data_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "Logs") - else: - path = user_cache_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "log") - if appname and version: - path = os.path.join(path, version) - return path - - -class AppDirs(object): - """Convenience wrapper for getting application dirs.""" - def __init__(self, appname=None, appauthor=None, version=None, - roaming=False, multipath=False): - self.appname = appname - self.appauthor = appauthor - self.version = version - self.roaming = roaming - self.multipath = multipath - - @property - def user_data_dir(self): - return user_data_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_data_dir(self): - return site_data_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_config_dir(self): - return user_config_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_config_dir(self): - return site_config_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_cache_dir(self): - return user_cache_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_state_dir(self): - return user_state_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_log_dir(self): - return user_log_dir(self.appname, self.appauthor, - version=self.version) - - -#---- internal support stuff - -def _get_win_folder_from_registry(csidl_name): - """This is a fallback technique at best. I'm not sure if using the - registry for this guarantees us the correct answer for all CSIDL_* - names. - """ - if PY3: - import winreg as _winreg - else: - import _winreg - - shell_folder_name = { - "CSIDL_APPDATA": "AppData", - "CSIDL_COMMON_APPDATA": "Common AppData", - "CSIDL_LOCAL_APPDATA": "Local AppData", - }[csidl_name] - - key = _winreg.OpenKey( - _winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" - ) - dir, type = _winreg.QueryValueEx(key, shell_folder_name) - return dir - - -def _get_win_folder_with_pywin32(csidl_name): - from win32com.shell import shellcon, shell - dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) - # Try to make this a unicode path because SHGetFolderPath does - # not return unicode strings when there is unicode data in the - # path. - try: - dir = unicode(dir) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - try: - import win32api - dir = win32api.GetShortPathName(dir) - except ImportError: - pass - except UnicodeError: - pass - return dir - - -def _get_win_folder_with_ctypes(csidl_name): - import ctypes - - csidl_const = { - "CSIDL_APPDATA": 26, - "CSIDL_COMMON_APPDATA": 35, - "CSIDL_LOCAL_APPDATA": 28, - }[csidl_name] - - buf = ctypes.create_unicode_buffer(1024) - ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in buf: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf2 = ctypes.create_unicode_buffer(1024) - if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): - buf = buf2 - - return buf.value - -def _get_win_folder_with_jna(csidl_name): - import array - from com.sun import jna - from com.sun.jna.platform import win32 - - buf_size = win32.WinDef.MAX_PATH * 2 - buf = array.zeros('c', buf_size) - shell = win32.Shell32.INSTANCE - shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf = array.zeros('c', buf_size) - kernel = win32.Kernel32.INSTANCE - if kernel.GetShortPathName(dir, buf, buf_size): - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - return dir - -if system == "win32": - try: - from ctypes import windll - _get_win_folder = _get_win_folder_with_ctypes - except ImportError: - try: - import com.sun.jna - _get_win_folder = _get_win_folder_with_jna - except ImportError: - _get_win_folder = _get_win_folder_from_registry - - -def _win_path_to_bytes(path): - """Encode Windows paths to bytes. Only used on Python 2. - - Motivation is to be consistent with other operating systems where paths - are also returned as bytes. This avoids problems mixing bytes and Unicode - elsewhere in the codebase. For more details and discussion see - . - - If encoding using ASCII and MBCS fails, return the original Unicode path. - """ - for encoding in ('ASCII', 'MBCS'): - try: - return path.encode(encoding) - except (UnicodeEncodeError, LookupError): - pass - return path - - -#---- self test code - -if __name__ == "__main__": - appname = "MyApp" - appauthor = "MyCompany" - - props = ("user_data_dir", - "user_config_dir", - "user_cache_dir", - "user_state_dir", - "user_log_dir", - "site_data_dir", - "site_config_dir") - - print("-- app dirs %s --" % __version__) - - print("-- app dirs (with optional 'version')") - dirs = AppDirs(appname, appauthor, version="1.0") - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'version')") - dirs = AppDirs(appname, appauthor) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'appauthor')") - dirs = AppDirs(appname) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (with disabled 'appauthor')") - dirs = AppDirs(appname, appauthor=False) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py b/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py index 1e2dfac7dbeaca31338d604a8789c33de6f68c75..eebdf88867dd6722eb908134cc412072b9f8b183 100644 --- a/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/certifi/__init__.py @@ -1,3 +1,3 @@ from .core import contents, where -__version__ = "2020.04.05.1" +__version__ = "2021.05.30" diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem b/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem index ece147c9dc8d2ce7b8f65eead95aec602bbbcaf1..96e2fc65a816432247eed7c83c86fc7458af1ed4 100644 --- a/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem +++ b/venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem @@ -58,38 +58,6 @@ AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== -----END CERTIFICATE----- -# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only -# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only -# Label: "Verisign Class 3 Public Primary Certification Authority - G3" -# Serial: 206684696279472310254277870180966723415 -# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 -# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 -# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b -N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t -KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu -kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm -CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ -Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu -imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te -2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe -DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC -/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p -F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt -TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== ------END CERTIFICATE----- - # Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited # Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited # Label: "Entrust.net Premium 2048 Secure Server CA" @@ -152,39 +120,6 @@ ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- -# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network -# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network -# Label: "AddTrust External Root" -# Serial: 1 -# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f -# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 -# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- - # Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. # Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. # Label: "Entrust Root Certification Authority" @@ -220,112 +155,6 @@ eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m 0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- -# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. -# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. -# Label: "GeoTrust Global CA" -# Serial: 144470 -# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 -# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 -# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- - -# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. -# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. -# Label: "GeoTrust Universal CA" -# Serial: 1 -# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 -# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 -# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 ------BEGIN CERTIFICATE----- -MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW -MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy -c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE -BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 -IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV -VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 -cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT -QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh -F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v -c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w -mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd -VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX -teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ -f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe -Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ -nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB -/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY -MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG -9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc -aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX -IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn -ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z -uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN -Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja -QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW -koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 -ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt -DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm -bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= ------END CERTIFICATE----- - -# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. -# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. -# Label: "GeoTrust Universal CA 2" -# Serial: 1 -# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 -# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 -# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b ------BEGIN CERTIFICATE----- -MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW -MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy -c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD -VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 -c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 -WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG -FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq -XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL -se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb -KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd -IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 -y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt -hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc -QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 -Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV -HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ -KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z -dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ -L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr -Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo -ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY -T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz -GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m -1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV -OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH -6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX -QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS ------END CERTIFICATE----- - # Issuer: CN=AAA Certificate Services O=Comodo CA Limited # Subject: CN=AAA Certificate Services O=Comodo CA Limited # Label: "Comodo AAA Services root" @@ -359,48 +188,6 @@ l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- -# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority -# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority -# Label: "QuoVadis Root CA" -# Serial: 985026699 -# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 -# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 -# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 ------BEGIN CERTIFICATE----- -MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC -TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz -MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw -IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR -dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp -li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D -rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ -WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug -F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU -xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC -Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv -dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw -ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl -IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh -c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy -ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh -Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI -KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T -KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq -y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p -dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD -VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL -MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk -fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 -7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R -cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y -mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW -xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK -SnQ2+Q== ------END CERTIFICATE----- - # Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited # Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited # Label: "QuoVadis Root CA 2" @@ -516,33 +303,6 @@ JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== -----END CERTIFICATE----- -# Issuer: CN=Sonera Class2 CA O=Sonera -# Subject: CN=Sonera Class2 CA O=Sonera -# Label: "Sonera Class 2 Root CA" -# Serial: 29 -# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb -# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 -# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP -MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx -MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV -BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o -Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt -5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s -3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej -vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu -8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw -DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG -MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil -zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ -3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD -FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 -Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 -ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M ------END CERTIFICATE----- - # Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com # Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com # Label: "XRamp Global CA Root" @@ -640,46 +400,6 @@ VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= -----END CERTIFICATE----- -# Issuer: O=Government Root Certification Authority -# Subject: O=Government Root Certification Authority -# Label: "Taiwan GRCA" -# Serial: 42023070807708724159991140556527066870 -# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e -# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 -# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 ------BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ -MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow -PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR -IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q -gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy -yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts -F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 -jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx -ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC -VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK -YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH -EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN -Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud -DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE -MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK -UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ -TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf -qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK -ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE -JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 -hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 -EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm -nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX -udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz -ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe -LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl -pYYsfPQS ------END CERTIFICATE----- - # Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com # Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com # Label: "DigiCert Assured ID Root CA" @@ -881,104 +601,6 @@ hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u -----END CERTIFICATE----- -# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. -# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. -# Label: "GeoTrust Primary Certification Authority" -# Serial: 32798226551256963324313806436981982369 -# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf -# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 -# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c ------BEGIN CERTIFICATE----- -MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY -MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo -R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx -MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK -Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 -AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA -ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 -7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W -kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI -mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ -KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 -6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl -4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K -oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj -UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU -AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= ------END CERTIFICATE----- - -# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only -# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only -# Label: "thawte Primary Root CA" -# Serial: 69529181992039203566298953787712940909 -# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 -# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 -# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB -qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf -Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw -MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV -BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw -NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j -LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG -A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs -W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta -3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk -6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 -Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J -NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP -r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU -DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz -YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX -xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 -/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ -LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 -jVaMaA== ------END CERTIFICATE----- - -# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only -# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only -# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" -# Serial: 33037644167568058970164719475676101450 -# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c -# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 -# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df ------BEGIN CERTIFICATE----- -MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB -yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL -ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp -U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW -ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW -ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp -U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y -aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 -nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex -t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz -SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG -BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ -rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ -NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E -BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH -BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy -aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv -MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE -p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y -5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK -WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ -4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N -hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq ------END CERTIFICATE----- - # Issuer: CN=SecureTrust CA O=SecureTrust Corporation # Subject: CN=SecureTrust CA O=SecureTrust Corporation # Label: "SecureTrust CA" @@ -1127,38 +749,6 @@ fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- -# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed -# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed -# Label: "OISTE WISeKey Global Root GA CA" -# Serial: 86718877871133159090080555911823548314 -# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 -# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 -# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 ------BEGIN CERTIFICATE----- -MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB -ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly -aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl -ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w -NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G -A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD -VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX -SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR -VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 -w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF -mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg -4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 -4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw -DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw -EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx -SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 -ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 -vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa -hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi -Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ -/L7fCg0= ------END CERTIFICATE----- - # Issuer: CN=Certigna O=Dhimyotis # Subject: CN=Certigna O=Dhimyotis # Label: "Certigna" @@ -1288,185 +878,6 @@ i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN 9u6wWk5JRFRYX0KD -----END CERTIFICATE----- -# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only -# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only -# Label: "GeoTrust Primary Certification Authority - G3" -# Serial: 28809105769928564313984085209975885599 -# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 -# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd -# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB -mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT -MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s -eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv -cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ -BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg -MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 -BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz -+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm -hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn -5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W -JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL -DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC -huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw -HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB -AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB -zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN -kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD -AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH -SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G -spki4cErx5z481+oghLrGREt ------END CERTIFICATE----- - -# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only -# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only -# Label: "thawte Primary Root CA - G2" -# Serial: 71758320672825410020661621085256472406 -# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f -# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 -# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 ------BEGIN CERTIFICATE----- -MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp -IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi -BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw -MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh -d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig -YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v -dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ -BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 -papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K -DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 -KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox -XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== ------END CERTIFICATE----- - -# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only -# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only -# Label: "thawte Primary Root CA - G3" -# Serial: 127614157056681299805556476275995414779 -# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 -# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 -# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB -rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf -Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw -MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV -BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa -Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl -LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u -MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl -ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm -gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 -YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf -b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 -9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S -zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk -OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV -HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA -2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW -oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu -t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c -KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM -m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu -MdRAGmI0Nj81Aa6sY6A= ------END CERTIFICATE----- - -# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only -# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only -# Label: "GeoTrust Primary Certification Authority - G2" -# Serial: 80682863203381065782177908751794619243 -# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a -# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 -# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 ------BEGIN CERTIFICATE----- -MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL -MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj -KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 -MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV -BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw -NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV -BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH -MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL -So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal -tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG -CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT -qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz -rD6ogRLQy7rQkgu2npaqBA+K ------END CERTIFICATE----- - -# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only -# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only -# Label: "VeriSign Universal Root Certification Authority" -# Serial: 85209574734084581917763752644031726877 -# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 -# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 -# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c ------BEGIN CERTIFICATE----- -MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB -vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL -ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp -U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W -ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe -Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX -MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 -IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y -IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh -bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF -9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH -H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H -LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN -/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT -rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw -WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs -exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud -DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 -sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ -seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz -4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ -BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR -lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 -7M2CYfE45k+XmCpajQ== ------END CERTIFICATE----- - -# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only -# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only -# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" -# Serial: 63143484348153506665311985501458640051 -# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 -# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a -# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 ------BEGIN CERTIFICATE----- -MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW -ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp -U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y -aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp -U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg -SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln -biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm -GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve -fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ -aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj -aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW -kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC -4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga -FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== ------END CERTIFICATE----- - # Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) # Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) # Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" @@ -1499,47 +910,6 @@ uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- -# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden -# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden -# Label: "Staat der Nederlanden Root CA - G2" -# Serial: 10000012 -# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a -# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 -# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f ------BEGIN CERTIFICATE----- -MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX -DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl -ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv -b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 -qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp -uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU -Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE -pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp -5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M -UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN -GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy -5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv -6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK -eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 -B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ -BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov -L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG -SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS -CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen -5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 -IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK -gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL -+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL -vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm -bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk -N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC -Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z -ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== ------END CERTIFICATE----- - # Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post # Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post # Label: "Hongkong Post Root CA 1" @@ -1743,105 +1113,6 @@ naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE----- -# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. -# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. -# Label: "Chambers of Commerce Root - 2008" -# Serial: 11806822484801597146 -# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 -# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c -# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 ------BEGIN CERTIFICATE----- -MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD -VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 -IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 -MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz -IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz -MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj -dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw -EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp -MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 -28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq -VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q -DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR -5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL -ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a -Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl -UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s -+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 -Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj -ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx -hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV -HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 -+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN -YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t -L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy -ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt -IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV -HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w -DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW -PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF -5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 -glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH -FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 -pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD -xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG -tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq -jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De -fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg -OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ -d0jQ ------END CERTIFICATE----- - -# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. -# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. -# Label: "Global Chambersign Root - 2008" -# Serial: 14541511773111788494 -# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 -# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c -# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD -VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 -IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 -MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx -MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy -cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG -A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl -BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed -KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 -G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 -zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 -ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG -HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 -Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V -yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e -beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r -6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog -zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW -BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr -ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp -ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk -cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt -YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC -CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow -KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI -hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ -UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz -X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x -fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz -a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd -Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd -SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O -AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso -M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge -v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- - # Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. # Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. # Label: "Go Daddy Root Certificate Authority - G2" @@ -2253,35 +1524,6 @@ LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== -----END CERTIFICATE----- -# Issuer: O=Trustis Limited OU=Trustis FPS Root CA -# Subject: O=Trustis Limited OU=Trustis FPS Root CA -# Label: "Trustis FPS Root CA" -# Serial: 36053640375399034304724988975563710553 -# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d -# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 -# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF -MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL -ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx -MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc -MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ -AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH -iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj -vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA -0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB -OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ -BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E -FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 -GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW -zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 -1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE -f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F -jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN -ZetX2fNXlrtIzYE= ------END CERTIFICATE----- - # Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 # Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 # Label: "Buypass Class 2 Root CA" @@ -2391,38 +1633,6 @@ e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p TpPDpFQUWw== -----END CERTIFICATE----- -# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus -# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus -# Label: "EE Certification Centre Root CA" -# Serial: 112324828676200291871926431888494945866 -# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f -# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 -# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 ------BEGIN CERTIFICATE----- -MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 -MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 -czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG -CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy -MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl -ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS -b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy -euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO -bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw -WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d -MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE -1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ -zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB -BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF -BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV -v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG -E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u -uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW -iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v -GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= ------END CERTIFICATE----- - # Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH # Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH # Label: "D-TRUST Root Class 3 CA 2 2009" @@ -3175,46 +2385,6 @@ KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg xwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- -# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden -# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden -# Label: "Staat der Nederlanden Root CA - G3" -# Serial: 10003001 -# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 -# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc -# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX -DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl -ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv -b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP -cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW -IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX -xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy -KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR -9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az -5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 -6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 -Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP -bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt -BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt -XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF -MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd -INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD -U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp -LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 -Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp -gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh -/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw -0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A -fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq -4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR -1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ -QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM -94B7IWcnMFk= ------END CERTIFICATE----- - # Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden # Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden # Label: "Staat der Nederlanden EV Root CA" @@ -3788,47 +2958,6 @@ CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW 1KyLa2tJElMzrdfkviT8tQp21KW8EA== -----END CERTIFICATE----- -# Issuer: CN=LuxTrust Global Root 2 O=LuxTrust S.A. -# Subject: CN=LuxTrust Global Root 2 O=LuxTrust S.A. -# Label: "LuxTrust Global Root 2" -# Serial: 59914338225734147123941058376788110305822489521 -# MD5 Fingerprint: b2:e1:09:00:61:af:f7:f1:91:6f:c4:ad:8d:5e:3b:7c -# SHA1 Fingerprint: 1e:0e:56:19:0a:d1:8b:25:98:b2:04:44:ff:66:8a:04:17:99:5f:3f -# SHA256 Fingerprint: 54:45:5f:71:29:c2:0b:14:47:c4:18:f9:97:16:8f:24:c5:8f:c5:02:3b:f5:da:5b:e2:eb:6e:1d:d8:90:2e:d5 ------BEGIN CERTIFICATE----- -MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL -BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV -BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw -MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B -LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F -ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem -hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 -EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn -Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 -zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ -96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m -j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g -DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ -8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j -X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH -hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB -KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 -Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT -+Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL -BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 -BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO -jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 -loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c -qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ -2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ -JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre -zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf -LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ -x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 -oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr ------END CERTIFICATE----- - # Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM # Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM # Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" @@ -4639,3 +3768,490 @@ IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk 5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== -----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global Certification Authority" +# Serial: 1846098327275375458322922162 +# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e +# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 +# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw +CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x +ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 +c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx +OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI +SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn +swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu +7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 +1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW +80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP +JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l +RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw +hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 +coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc +BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n +twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud +DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W +0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe +uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q +lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB +aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE +sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT +MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe +qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh +VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 +h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 +EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK +yeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P256 Certification Authority" +# Serial: 4151900041497450638097112925 +# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 +# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf +# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN +FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w +DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw +CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh +DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P384 Certification Authority" +# Serial: 2704997926503831671788816187 +# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 +# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 +# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ +j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF +1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G +A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 +AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC +MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu +Sw== +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Label: "GLOBALTRUST 2020" +# Serial: 109160994242082918454945253 +# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 +# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 +# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG +A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw +FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx +MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u +aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b +RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z +YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 +QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw +yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ +BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ +SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH +r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 +4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me +dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw +q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 +nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu +H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC +XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd +6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf ++I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi +kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 +wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB +TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C +MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn +4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I +aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy +qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- diff --git a/venv/Lib/site-packages/pip/_vendor/certifi/core.py b/venv/Lib/site-packages/pip/_vendor/certifi/core.py index 56b52a3c8f4f66a6f1fb6be36dd25fc906303fef..b8140cf1ae7cd6d84a484668608ec6226db20e37 100644 --- a/venv/Lib/site-packages/pip/_vendor/certifi/core.py +++ b/venv/Lib/site-packages/pip/_vendor/certifi/core.py @@ -8,8 +8,53 @@ This module returns the installation location of cacert.pem or its contents. """ import os + +class _PipPatchedCertificate(Exception): + pass + + try: - from importlib.resources import read_text + # Return a certificate file on disk for a standalone pip zipapp running in + # an isolated build environment to use. Passing --cert to the standalone + # pip does not work since requests calls where() unconditionally on import. + _PIP_STANDALONE_CERT = os.environ.get("_PIP_STANDALONE_CERT") + if _PIP_STANDALONE_CERT: + def where(): + return _PIP_STANDALONE_CERT + raise _PipPatchedCertificate() + + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None + _CACERT_PATH = None + + def where(): + # This is slightly terrible, but we want to delay extracting the file + # in cases where we're inside of a zipimport situation until someone + # actually calls where(), but we don't want to re-extract the file + # on every call of where(), so we'll do it once then store it in a + # global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you to + # manage the cleanup of this file, so it doesn't actually return a + # path, it returns a context manager that will give you the path + # when you enter it and will do any cleanup when you leave it. In + # the common case of not needing a temporary file, it will just + # return the file system location and the __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = get_path("pip._vendor.certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + + return _CACERT_PATH + +except _PipPatchedCertificate: + pass + except ImportError: # This fallback will work for Python versions prior to 3.7 that lack the # importlib.resources module but relies on the existing `where` function @@ -19,11 +64,12 @@ except ImportError: with open(where(), "r", encoding=encoding) as data: return data.read() + # If we don't have importlib.resources, then we will just do the old logic + # of assuming we're on the filesystem and munge the path directly. + def where(): + f = os.path.dirname(__file__) -def where(): - f = os.path.dirname(__file__) - - return os.path.join(f, "cacert.pem") + return os.path.join(f, "cacert.pem") def contents(): diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py b/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py index 0f9f820ef6e5f21042efd0e9dc18d097388d7207..80ad2546d7981394b5f5d221336c9f00236b9d66 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/__init__.py @@ -16,11 +16,14 @@ ######################### END LICENSE BLOCK ######################### -from .compat import PY2, PY3 from .universaldetector import UniversalDetector +from .enums import InputState from .version import __version__, VERSION +__all__ = ['UniversalDetector', 'detect', 'detect_all', '__version__', 'VERSION'] + + def detect(byte_str): """ Detect the encoding of the given byte string. @@ -31,9 +34,50 @@ def detect(byte_str): if not isinstance(byte_str, bytearray): if not isinstance(byte_str, bytes): raise TypeError('Expected object of type bytes or bytearray, got: ' - '{0}'.format(type(byte_str))) + '{}'.format(type(byte_str))) else: byte_str = bytearray(byte_str) detector = UniversalDetector() detector.feed(byte_str) return detector.close() + + +def detect_all(byte_str): + """ + Detect all the possible encodings of the given byte string. + + :param byte_str: The byte sequence to examine. + :type byte_str: ``bytes`` or ``bytearray`` + """ + if not isinstance(byte_str, bytearray): + if not isinstance(byte_str, bytes): + raise TypeError('Expected object of type bytes or bytearray, got: ' + '{}'.format(type(byte_str))) + else: + byte_str = bytearray(byte_str) + + detector = UniversalDetector() + detector.feed(byte_str) + detector.close() + + if detector._input_state == InputState.HIGH_BYTE: + results = [] + for prober in detector._charset_probers: + if prober.get_confidence() > detector.MINIMUM_THRESHOLD: + charset_name = prober.charset_name + lower_charset_name = prober.charset_name.lower() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith('iso-8859'): + if detector._has_win_bytes: + charset_name = detector.ISO_WIN_MAP.get(lower_charset_name, + charset_name) + results.append({ + 'encoding': charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language, + }) + if len(results) > 0: + return sorted(results, key=lambda result: -result['confidence']) + + return [detector.result] diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py index 8b3738efd8ea1e42d196d381d2809beae7f4c649..5812cef0b5924db9af2da77f0abe4e63decee4cf 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -73,6 +73,7 @@ class CharSetGroupProber(CharSetProber): continue if state == ProbingState.FOUND_IT: self._best_guess_prober = prober + self._state = ProbingState.FOUND_IT return self.state elif state == ProbingState.NOT_ME: prober.active = False diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py b/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py index c61136b639e57a46acd72d2b6b7142a61979d694..6d6f93aabd44902caf086998226aa48fd3bbe221 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ Script which takes one or more file paths and reports on their detected encodings @@ -45,10 +44,10 @@ def description_of(lines, name='stdin'): if PY2: name = name.decode(sys.getfilesystemencoding(), 'ignore') if result['encoding']: - return '{0}: {1} with confidence {2}'.format(name, result['encoding'], + return '{}: {} with confidence {}'.format(name, result['encoding'], result['confidence']) else: - return '{0}: no result'.format(name) + return '{}: no result'.format(name) def main(argv=None): @@ -69,7 +68,7 @@ def main(argv=None): type=argparse.FileType('rb'), nargs='*', default=[sys.stdin if PY2 else sys.stdin.buffer]) parser.add_argument('--version', action='version', - version='%(prog)s {0}'.format(__version__)) + version='%(prog)s {}'.format(__version__)) args = parser.parse_args(argv) for f in args.input: diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/compat.py b/venv/Lib/site-packages/pip/_vendor/chardet/compat.py index ddd74687c02a12ecc296ee803997a345e984bc9d..8941572b3e6a2a2267659ed74e25099c37aae90b 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/compat.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/compat.py @@ -25,10 +25,12 @@ import sys if sys.version_info < (3, 0): PY2 = True PY3 = False - base_str = (str, unicode) + string_types = (str, unicode) text_type = unicode + iteritems = dict.iteritems else: PY2 = False PY3 = True - base_str = (bytes, str) + string_types = (bytes, str) text_type = str + iteritems = dict.items diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py index 2aa4fb2e22fc3bfa26b569273c6cb18c4c415dd9..e963a50979a0b3dd56558240e075ca0f889479df 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py @@ -1,228 +1,4650 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel -# Character Mapping Table: -# this table is modified base on win1251BulgarianCharToOrderMap, so -# only number <64 is sure valid -Latin5_BulgarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 -110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 -253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 -116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 -194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 -210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 - 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 - 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 - 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 - 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 - 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 - 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 -) +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative -win1251BulgarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 -110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 -253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 -116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 -206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 -221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 - 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 - 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 - 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 - 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 - 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 - 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 -) +BULGARIAN_LANG_MODEL = { + 63: { # 'e' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 1, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 45: { # '\xad' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 1, # 'М' + 36: 0, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 31: { # 'А' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 1, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 2, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 2, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 0, # 'и' + 26: 2, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 32: { # 'Б' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 1, # 'Щ' + 61: 2, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 35: { # 'В' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 2, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 2, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 43: { # 'Г' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 1, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 37: { # 'Д' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 2, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 44: { # 'Е' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 2, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 0, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 55: { # 'Ж' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 47: { # 'З' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 40: { # 'И' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 2, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 2, # 'Я' + 1: 1, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 3, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 0, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 59: { # 'Й' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 33: { # 'К' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 46: { # 'Л' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 2, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 38: { # 'М' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 36: { # 'Н' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 2, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 41: { # 'О' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 2, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 2, # 'ч' + 27: 0, # 'ш' + 24: 2, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 30: { # 'П' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 2, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 39: { # 'Р' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 2, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 28: { # 'С' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 3, # 'А' + 32: 2, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 34: { # 'Т' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 51: { # 'У' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 2, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 48: { # 'Ф' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 49: { # 'Х' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 53: { # 'Ц' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 50: { # 'Ч' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 54: { # 'Ш' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 57: { # 'Щ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 61: { # 'Ъ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 60: { # 'Ю' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 1, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 0, # 'е' + 23: 2, # 'ж' + 15: 1, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 56: { # 'Я' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 1, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 1: { # 'а' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 18: { # 'б' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 0, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 2, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 3, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 9: { # 'в' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 0, # 'в' + 20: 2, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 20: { # 'г' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 11: { # 'д' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 1, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 3: { # 'е' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 2, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 23: { # 'ж' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 15: { # 'з' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 2: { # 'и' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 1, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 26: { # 'й' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 12: { # 'к' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 3, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 10: { # 'л' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 1, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 3, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 14: { # 'м' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 6: { # 'н' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 2, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 3, # 'ф' + 25: 2, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 4: { # 'о' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 13: { # 'п' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 7: { # 'р' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 2, # 'ч' + 27: 3, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 8: { # 'с' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 5: { # 'т' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 19: { # 'у' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 2, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 2, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 29: { # 'ф' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 2, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 25: { # 'х' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 22: { # 'ц' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 21: { # 'ч' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 27: { # 'ш' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 24: { # 'щ' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 17: { # 'ъ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 1, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 3, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 2, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 52: { # 'ь' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 42: { # 'ю' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 1, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 1, # 'е' + 23: 2, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 1, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 16: { # 'я' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 1, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 3, # 'х' + 22: 2, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 2, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 58: { # 'є' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 62: { # '№' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, +} -# Model Table: -# total sequences: 100% -# first 512 sequences: 96.9392% -# first 1024 sequences:3.0618% -# rest sequences: 0.2992% -# negative sequences: 0.0020% -BulgarianLangModel = ( -0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, -3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, -0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, -0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, -0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, -0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, -0,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,0,1, -3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, -0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, -0,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,0,1, -3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, -0,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,0,0, -3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, -0,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,0,1, -3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, -2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, -3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, -1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, -3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, -1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, -2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, -2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, -3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, -1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, -2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, -2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, -3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, -1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, -2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, -2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, -2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, -1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, -2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, -1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, -3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, -1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, -3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, -1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, -2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, -1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, -2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, -1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, -2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, -1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, -1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, -1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, -2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, -1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, -2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, -1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, -0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, -1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, -1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, -1,0,0,1,3,1,1,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, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, -0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, -0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, -0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, -1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, -0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, -1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, -1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, -1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -) +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters -Latin5BulgarianModel = { - 'char_to_order_map': Latin5_BulgarianCharToOrderMap, - 'precedence_matrix': BulgarianLangModel, - 'typical_positive_ratio': 0.969392, - 'keep_english_letter': False, - 'charset_name': "ISO-8859-5", - 'language': 'Bulgairan', +# Character Mapping Table(s): +ISO_8859_5_BULGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 194, # '\x80' + 129: 195, # '\x81' + 130: 196, # '\x82' + 131: 197, # '\x83' + 132: 198, # '\x84' + 133: 199, # '\x85' + 134: 200, # '\x86' + 135: 201, # '\x87' + 136: 202, # '\x88' + 137: 203, # '\x89' + 138: 204, # '\x8a' + 139: 205, # '\x8b' + 140: 206, # '\x8c' + 141: 207, # '\x8d' + 142: 208, # '\x8e' + 143: 209, # '\x8f' + 144: 210, # '\x90' + 145: 211, # '\x91' + 146: 212, # '\x92' + 147: 213, # '\x93' + 148: 214, # '\x94' + 149: 215, # '\x95' + 150: 216, # '\x96' + 151: 217, # '\x97' + 152: 218, # '\x98' + 153: 219, # '\x99' + 154: 220, # '\x9a' + 155: 221, # '\x9b' + 156: 222, # '\x9c' + 157: 223, # '\x9d' + 158: 224, # '\x9e' + 159: 225, # '\x9f' + 160: 81, # '\xa0' + 161: 226, # 'Ё' + 162: 227, # 'Ђ' + 163: 228, # 'Ѓ' + 164: 229, # 'Є' + 165: 230, # 'Ѕ' + 166: 105, # 'І' + 167: 231, # 'Ї' + 168: 232, # 'Ј' + 169: 233, # 'Љ' + 170: 234, # 'Њ' + 171: 235, # 'Ћ' + 172: 236, # 'Ќ' + 173: 45, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 31, # 'А' + 177: 32, # 'Б' + 178: 35, # 'В' + 179: 43, # 'Г' + 180: 37, # 'Д' + 181: 44, # 'Е' + 182: 55, # 'Ж' + 183: 47, # 'З' + 184: 40, # 'И' + 185: 59, # 'Й' + 186: 33, # 'К' + 187: 46, # 'Л' + 188: 38, # 'М' + 189: 36, # 'Н' + 190: 41, # 'О' + 191: 30, # 'П' + 192: 39, # 'Р' + 193: 28, # 'С' + 194: 34, # 'Т' + 195: 51, # 'У' + 196: 48, # 'Ф' + 197: 49, # 'Х' + 198: 53, # 'Ц' + 199: 50, # 'Ч' + 200: 54, # 'Ш' + 201: 57, # 'Щ' + 202: 61, # 'Ъ' + 203: 239, # 'Ы' + 204: 67, # 'Ь' + 205: 240, # 'Э' + 206: 60, # 'Ю' + 207: 56, # 'Я' + 208: 1, # 'а' + 209: 18, # 'б' + 210: 9, # 'в' + 211: 20, # 'г' + 212: 11, # 'д' + 213: 3, # 'е' + 214: 23, # 'ж' + 215: 15, # 'з' + 216: 2, # 'и' + 217: 26, # 'й' + 218: 12, # 'к' + 219: 10, # 'л' + 220: 14, # 'м' + 221: 6, # 'н' + 222: 4, # 'о' + 223: 13, # 'п' + 224: 7, # 'р' + 225: 8, # 'с' + 226: 5, # 'т' + 227: 19, # 'у' + 228: 29, # 'ф' + 229: 25, # 'х' + 230: 22, # 'ц' + 231: 21, # 'ч' + 232: 27, # 'ш' + 233: 24, # 'щ' + 234: 17, # 'ъ' + 235: 75, # 'ы' + 236: 52, # 'ь' + 237: 241, # 'э' + 238: 42, # 'ю' + 239: 16, # 'я' + 240: 62, # '№' + 241: 242, # 'ё' + 242: 243, # 'ђ' + 243: 244, # 'ѓ' + 244: 58, # 'є' + 245: 245, # 'ѕ' + 246: 98, # 'і' + 247: 246, # 'ї' + 248: 247, # 'ј' + 249: 248, # 'љ' + 250: 249, # 'њ' + 251: 250, # 'ћ' + 252: 251, # 'ќ' + 253: 91, # '§' + 254: 252, # 'ў' + 255: 253, # 'џ' } -Win1251BulgarianModel = { - 'char_to_order_map': win1251BulgarianCharToOrderMap, - 'precedence_matrix': BulgarianLangModel, - 'typical_positive_ratio': 0.969392, - 'keep_english_letter': False, - 'charset_name': "windows-1251", - 'language': 'Bulgarian', +ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-5', + language='Bulgarian', + char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя') + +WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 206, # 'Ђ' + 129: 207, # 'Ѓ' + 130: 208, # '‚' + 131: 209, # 'ѓ' + 132: 210, # '„' + 133: 211, # '…' + 134: 212, # '†' + 135: 213, # '‡' + 136: 120, # '€' + 137: 214, # '‰' + 138: 215, # 'Љ' + 139: 216, # '‹' + 140: 217, # 'Њ' + 141: 218, # 'Ќ' + 142: 219, # 'Ћ' + 143: 220, # 'Џ' + 144: 221, # 'ђ' + 145: 78, # '‘' + 146: 64, # '’' + 147: 83, # '“' + 148: 121, # '”' + 149: 98, # '•' + 150: 117, # '–' + 151: 105, # '—' + 152: 222, # None + 153: 223, # '™' + 154: 224, # 'љ' + 155: 225, # '›' + 156: 226, # 'њ' + 157: 227, # 'ќ' + 158: 228, # 'ћ' + 159: 229, # 'џ' + 160: 88, # '\xa0' + 161: 230, # 'Ў' + 162: 231, # 'ў' + 163: 232, # 'Ј' + 164: 233, # '¤' + 165: 122, # 'Ґ' + 166: 89, # '¦' + 167: 106, # '§' + 168: 234, # 'Ё' + 169: 235, # '©' + 170: 236, # 'Є' + 171: 237, # '«' + 172: 238, # '¬' + 173: 45, # '\xad' + 174: 239, # '®' + 175: 240, # 'Ї' + 176: 73, # '°' + 177: 80, # '±' + 178: 118, # 'І' + 179: 114, # 'і' + 180: 241, # 'ґ' + 181: 242, # 'µ' + 182: 243, # '¶' + 183: 244, # '·' + 184: 245, # 'ё' + 185: 62, # '№' + 186: 58, # 'є' + 187: 246, # '»' + 188: 247, # 'ј' + 189: 248, # 'Ѕ' + 190: 249, # 'ѕ' + 191: 250, # 'ї' + 192: 31, # 'А' + 193: 32, # 'Б' + 194: 35, # 'В' + 195: 43, # 'Г' + 196: 37, # 'Д' + 197: 44, # 'Е' + 198: 55, # 'Ж' + 199: 47, # 'З' + 200: 40, # 'И' + 201: 59, # 'Й' + 202: 33, # 'К' + 203: 46, # 'Л' + 204: 38, # 'М' + 205: 36, # 'Н' + 206: 41, # 'О' + 207: 30, # 'П' + 208: 39, # 'Р' + 209: 28, # 'С' + 210: 34, # 'Т' + 211: 51, # 'У' + 212: 48, # 'Ф' + 213: 49, # 'Х' + 214: 53, # 'Ц' + 215: 50, # 'Ч' + 216: 54, # 'Ш' + 217: 57, # 'Щ' + 218: 61, # 'Ъ' + 219: 251, # 'Ы' + 220: 67, # 'Ь' + 221: 252, # 'Э' + 222: 60, # 'Ю' + 223: 56, # 'Я' + 224: 1, # 'а' + 225: 18, # 'б' + 226: 9, # 'в' + 227: 20, # 'г' + 228: 11, # 'д' + 229: 3, # 'е' + 230: 23, # 'ж' + 231: 15, # 'з' + 232: 2, # 'и' + 233: 26, # 'й' + 234: 12, # 'к' + 235: 10, # 'л' + 236: 14, # 'м' + 237: 6, # 'н' + 238: 4, # 'о' + 239: 13, # 'п' + 240: 7, # 'р' + 241: 8, # 'с' + 242: 5, # 'т' + 243: 19, # 'у' + 244: 29, # 'ф' + 245: 25, # 'х' + 246: 22, # 'ц' + 247: 21, # 'ч' + 248: 27, # 'ш' + 249: 24, # 'щ' + 250: 17, # 'ъ' + 251: 75, # 'ы' + 252: 52, # 'ь' + 253: 253, # 'э' + 254: 42, # 'ю' + 255: 16, # 'я' } + +WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1251', + language='Bulgarian', + char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя') + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py deleted file mode 100644 index e5f9a1fd19cca472d785bf90c6395cd11b524766..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langcyrillicmodel.py +++ /dev/null @@ -1,333 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# KOI8-R language model -# Character Mapping Table: -KOI8R_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 -223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 -238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 - 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 - 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 - 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 - 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 -) - -win1251_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, -239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, -) - -latin5_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, -239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, -) - -macCyrillic_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, -239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, -) - -IBM855_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, -206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, - 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, -220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, -230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, - 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, - 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, -250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, -) - -IBM866_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, -239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 97.6601% -# first 1024 sequences: 2.3389% -# rest sequences: 0.1237% -# negative sequences: 0.0009% -RussianLangModel = ( -0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, -3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, -0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, -0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, -1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, -1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, -2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, -1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, -3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, -1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, -2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, -1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, -1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, -1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, -2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, -1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, -3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, -1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, -2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, -1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, -2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, -1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, -1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, -1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, -3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, -2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, -3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, -1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, -1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, -0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, -2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, -1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, -1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, -0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, -1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, -2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, -1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, -1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, -2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, -1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, -0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, -2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, -1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, -1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, -0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, -0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, -0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, -0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, -0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, -1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, -0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, -2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, -0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, -) - -Koi8rModel = { - 'char_to_order_map': KOI8R_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "KOI8-R", - 'language': 'Russian', -} - -Win1251CyrillicModel = { - 'char_to_order_map': win1251_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "windows-1251", - 'language': 'Russian', -} - -Latin5CyrillicModel = { - 'char_to_order_map': latin5_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "ISO-8859-5", - 'language': 'Russian', -} - -MacCyrillicModel = { - 'char_to_order_map': macCyrillic_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "MacCyrillic", - 'language': 'Russian', -} - -Ibm866Model = { - 'char_to_order_map': IBM866_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "IBM866", - 'language': 'Russian', -} - -Ibm855Model = { - 'char_to_order_map': IBM855_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "IBM855", - 'language': 'Russian', -} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py index 533222166cca9fce442655d9f3098126f50e6140..d99528ede7357a287df5f03236f9f7e391f6f35a 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py @@ -1,225 +1,4398 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +GREEK_LANG_MODEL = { + 60: { # 'e' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 55: { # 'o' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 58: { # 't' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 1, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 36: { # '·' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 61: { # 'Ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 1, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 1, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 46: { # 'Έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 1, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 54: { # 'Ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 31: { # 'Α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 2, # 'Β' + 43: 2, # 'Γ' + 41: 1, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 51: { # 'Β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 1, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 43: { # 'Γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 1, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 41: { # 'Δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 34: { # 'Ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 0, # 'ώ' + }, + 40: { # 'Η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 52: { # 'Θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 1, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 47: { # 'Ι' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 1, # 'Β' + 43: 1, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 1, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 44: { # 'Κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 1, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 53: { # 'Λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 1, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 38: { # 'Μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 2, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 2, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 49: { # 'Ν' + 60: 2, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 59: { # 'Ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 39: { # 'Ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 1, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 2, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 35: { # 'Π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 1, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 48: { # 'Ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 37: { # 'Σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 2, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 33: { # 'Τ' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 2, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 45: { # 'Υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 2, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 1, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 56: { # 'Φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 1, # 'ώ' + }, + 50: { # 'Χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 1, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 57: { # 'Ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 17: { # 'ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 18: { # 'έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 22: { # 'ή' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 15: { # 'ί' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 1: { # 'α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 3, # 'ζ' + 13: 1, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 29: { # 'β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 20: { # 'γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 21: { # 'δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 3: { # 'ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 32: { # 'ζ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 13: { # 'η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 25: { # 'θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 5: { # 'ι' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 11: { # 'κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 16: { # 'λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 1, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 10: { # 'μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 3, # 'φ' + 23: 0, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 6: { # 'ν' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 30: { # 'ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 1, # 'ώ' + }, + 4: { # 'ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 9: { # 'π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 8: { # 'ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 14: { # 'ς' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 7: { # 'σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 2: { # 'τ' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 12: { # 'υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 28: { # 'φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 1, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 23: { # 'χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 42: { # 'ψ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 24: { # 'ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 1, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 19: { # 'ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 1, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 26: { # 'ύ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 27: { # 'ώ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 1, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 1, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -Latin7_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 - 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 -253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 - 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 -253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 -253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 -110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 - 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 -124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 - 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 -) - -win1253_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 - 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 -253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 - 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 -253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 -253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 -110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 - 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 -124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 - 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 -) +# Character Mapping Table(s): +WINDOWS_1253_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '€' + 129: 255, # None + 130: 255, # '‚' + 131: 255, # 'ƒ' + 132: 255, # '„' + 133: 255, # '…' + 134: 255, # '†' + 135: 255, # '‡' + 136: 255, # None + 137: 255, # '‰' + 138: 255, # None + 139: 255, # '‹' + 140: 255, # None + 141: 255, # None + 142: 255, # None + 143: 255, # None + 144: 255, # None + 145: 255, # '‘' + 146: 255, # '’' + 147: 255, # '“' + 148: 255, # '”' + 149: 255, # '•' + 150: 255, # '–' + 151: 255, # '—' + 152: 255, # None + 153: 255, # '™' + 154: 255, # None + 155: 255, # '›' + 156: 255, # None + 157: 255, # None + 158: 255, # None + 159: 255, # None + 160: 253, # '\xa0' + 161: 233, # '΅' + 162: 61, # 'Ά' + 163: 253, # '£' + 164: 253, # '¤' + 165: 253, # '¥' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # None + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # '®' + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 253, # 'µ' + 182: 253, # '¶' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None +} -# Model Table: -# total sequences: 100% -# first 512 sequences: 98.2851% -# first 1024 sequences:1.7001% -# rest sequences: 0.0359% -# negative sequences: 0.0148% -GreekLangModel = ( -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, -3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, -2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, -0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, -2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, -2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, -0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, -2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, -0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, -3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, -3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, -2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, -2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, -0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, -0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, -0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, -0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, -0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, -0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, -0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, -0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, -0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, -0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, -0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, -0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, -0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, -0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, -0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, -0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, -0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, -0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, -0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, -0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, -0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, -0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, -0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, -0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, -0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, -0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, -0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, -0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, -0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, -0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -) +WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel(charset_name='windows-1253', + language='Greek', + char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ') -Latin7GreekModel = { - 'char_to_order_map': Latin7_char_to_order_map, - 'precedence_matrix': GreekLangModel, - 'typical_positive_ratio': 0.982851, - 'keep_english_letter': False, - 'charset_name': "ISO-8859-7", - 'language': 'Greek', +ISO_8859_7_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '\x80' + 129: 255, # '\x81' + 130: 255, # '\x82' + 131: 255, # '\x83' + 132: 255, # '\x84' + 133: 255, # '\x85' + 134: 255, # '\x86' + 135: 255, # '\x87' + 136: 255, # '\x88' + 137: 255, # '\x89' + 138: 255, # '\x8a' + 139: 255, # '\x8b' + 140: 255, # '\x8c' + 141: 255, # '\x8d' + 142: 255, # '\x8e' + 143: 255, # '\x8f' + 144: 255, # '\x90' + 145: 255, # '\x91' + 146: 255, # '\x92' + 147: 255, # '\x93' + 148: 255, # '\x94' + 149: 255, # '\x95' + 150: 255, # '\x96' + 151: 255, # '\x97' + 152: 255, # '\x98' + 153: 255, # '\x99' + 154: 255, # '\x9a' + 155: 255, # '\x9b' + 156: 255, # '\x9c' + 157: 255, # '\x9d' + 158: 255, # '\x9e' + 159: 255, # '\x9f' + 160: 253, # '\xa0' + 161: 233, # '‘' + 162: 90, # '’' + 163: 253, # '£' + 164: 253, # '€' + 165: 253, # '₯' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # 'ͺ' + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # None + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 248, # '΅' + 182: 61, # 'Ά' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None } -Win1253GreekModel = { - 'char_to_order_map': win1253_char_to_order_map, - 'precedence_matrix': GreekLangModel, - 'typical_positive_ratio': 0.982851, - 'keep_english_letter': False, - 'charset_name': "windows-1253", - 'language': 'Greek', -} +ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-7', + language='Greek', + char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ') + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py index 58f4c875ec926b85256fd3866369fc8a81a14350..484c652a48e0c97826acabfc475d1bab7ea4763a 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py @@ -1,200 +1,4383 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Simon Montagu -# Portions created by the Initial Developer are Copyright (C) 2005 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# Shoshannah Forbes - original C code (?) -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +HEBREW_LANG_MODEL = { + 50: { # 'a' + 50: 0, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 2, # 'l' + 54: 2, # 'n' + 49: 0, # 'o' + 51: 2, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 0, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 60: { # 'c' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 61: { # 'd' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 0, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 42: { # 'e' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 2, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 2, # 'l' + 54: 2, # 'n' + 49: 1, # 'o' + 51: 2, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 1, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 53: { # 'i' + 50: 1, # 'a' + 60: 2, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 0, # 'i' + 56: 1, # 'l' + 54: 2, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 56: { # 'l' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 2, # 'e' + 53: 2, # 'i' + 56: 2, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 54: { # 'n' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 49: { # 'o' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 2, # 'n' + 49: 1, # 'o' + 51: 2, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 51: { # 'r' + 50: 2, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 2, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 43: { # 's' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 2, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 2, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 44: { # 't' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 2, # 'e' + 53: 2, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 63: { # 'u' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 34: { # '\xa0' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 1, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 2, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 55: { # '´' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 2, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 1, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 48: { # '¼' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 39: { # '½' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 57: { # '¾' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 30: { # 'ְ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 2, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 0, # 'ף' + 18: 2, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 59: { # 'ֱ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 1, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 41: { # 'ֲ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 33: { # 'ִ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 2, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 37: { # 'ֵ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 1, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 36: { # 'ֶ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 1, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 2, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 31: { # 'ַ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 29: { # 'ָ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 2, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 35: { # 'ֹ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 62: { # 'ֻ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 1, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 28: { # 'ּ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 3, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 3, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 3, # 'ַ' + 29: 3, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 2, # 'ׁ' + 45: 1, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 2, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 2, # 'מ' + 23: 1, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 38: { # 'ׁ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 2, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 45: { # 'ׂ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 2, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 9: { # 'א' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 2, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 8: { # 'ב' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 1, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 20: { # 'ג' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 2, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 16: { # 'ד' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 3: { # 'ה' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 3, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 0, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 2: { # 'ו' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 3, # 'ֹ' + 62: 0, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 24: { # 'ז' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 1, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 1, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 14: { # 'ח' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 1, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 22: { # 'ט' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 1, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 2, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 1: { # 'י' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 25: { # 'ך' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 15: { # 'כ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 4: { # 'ל' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 3, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 11: { # 'ם' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 6: { # 'מ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 0, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 23: { # 'ן' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 12: { # 'נ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 19: { # 'ס' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 2, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 1, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 13: { # 'ע' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 1, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 26: { # 'ף' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 18: { # 'פ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 2, # 'ב' + 20: 3, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 27: { # 'ץ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 21: { # 'צ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 1, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 0, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 17: { # 'ק' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 7: { # 'ר' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 2, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 10: { # 'ש' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 3, # 'ׁ' + 45: 2, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 5: { # 'ת' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 1, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 32: { # '–' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 52: { # '’' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 47: { # '“' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 46: { # '”' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 58: { # '†' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 2, # '†' + 40: 0, # '…' + }, + 40: { # '…' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Windows-1255 language model -# Character Mapping Table: -WIN1255_CHAR_TO_ORDER_MAP = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 - 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 -253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 - 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 -124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, -215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, - 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, -106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, - 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, -238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, - 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, - 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, -) +# Character Mapping Table(s): +WINDOWS_1255_HEBREW_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 69, # 'A' + 66: 91, # 'B' + 67: 79, # 'C' + 68: 80, # 'D' + 69: 92, # 'E' + 70: 89, # 'F' + 71: 97, # 'G' + 72: 90, # 'H' + 73: 68, # 'I' + 74: 111, # 'J' + 75: 112, # 'K' + 76: 82, # 'L' + 77: 73, # 'M' + 78: 95, # 'N' + 79: 85, # 'O' + 80: 78, # 'P' + 81: 121, # 'Q' + 82: 86, # 'R' + 83: 71, # 'S' + 84: 67, # 'T' + 85: 102, # 'U' + 86: 107, # 'V' + 87: 84, # 'W' + 88: 114, # 'X' + 89: 103, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 50, # 'a' + 98: 74, # 'b' + 99: 60, # 'c' + 100: 61, # 'd' + 101: 42, # 'e' + 102: 76, # 'f' + 103: 70, # 'g' + 104: 64, # 'h' + 105: 53, # 'i' + 106: 105, # 'j' + 107: 93, # 'k' + 108: 56, # 'l' + 109: 65, # 'm' + 110: 54, # 'n' + 111: 49, # 'o' + 112: 66, # 'p' + 113: 110, # 'q' + 114: 51, # 'r' + 115: 43, # 's' + 116: 44, # 't' + 117: 63, # 'u' + 118: 81, # 'v' + 119: 77, # 'w' + 120: 98, # 'x' + 121: 75, # 'y' + 122: 108, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 124, # '€' + 129: 202, # None + 130: 203, # '‚' + 131: 204, # 'ƒ' + 132: 205, # '„' + 133: 40, # '…' + 134: 58, # '†' + 135: 206, # '‡' + 136: 207, # 'ˆ' + 137: 208, # '‰' + 138: 209, # None + 139: 210, # '‹' + 140: 211, # None + 141: 212, # None + 142: 213, # None + 143: 214, # None + 144: 215, # None + 145: 83, # '‘' + 146: 52, # '’' + 147: 47, # '“' + 148: 46, # '”' + 149: 72, # '•' + 150: 32, # '–' + 151: 94, # '—' + 152: 216, # '˜' + 153: 113, # '™' + 154: 217, # None + 155: 109, # '›' + 156: 218, # None + 157: 219, # None + 158: 220, # None + 159: 221, # None + 160: 34, # '\xa0' + 161: 116, # '¡' + 162: 222, # '¢' + 163: 118, # '£' + 164: 100, # '₪' + 165: 223, # '¥' + 166: 224, # '¦' + 167: 117, # '§' + 168: 119, # '¨' + 169: 104, # '©' + 170: 125, # '×' + 171: 225, # '«' + 172: 226, # '¬' + 173: 87, # '\xad' + 174: 99, # '®' + 175: 227, # '¯' + 176: 106, # '°' + 177: 122, # '±' + 178: 123, # '²' + 179: 228, # '³' + 180: 55, # '´' + 181: 229, # 'µ' + 182: 230, # '¶' + 183: 101, # '·' + 184: 231, # '¸' + 185: 232, # '¹' + 186: 120, # '÷' + 187: 233, # '»' + 188: 48, # '¼' + 189: 39, # '½' + 190: 57, # '¾' + 191: 234, # '¿' + 192: 30, # 'ְ' + 193: 59, # 'ֱ' + 194: 41, # 'ֲ' + 195: 88, # 'ֳ' + 196: 33, # 'ִ' + 197: 37, # 'ֵ' + 198: 36, # 'ֶ' + 199: 31, # 'ַ' + 200: 29, # 'ָ' + 201: 35, # 'ֹ' + 202: 235, # None + 203: 62, # 'ֻ' + 204: 28, # 'ּ' + 205: 236, # 'ֽ' + 206: 126, # '־' + 207: 237, # 'ֿ' + 208: 238, # '׀' + 209: 38, # 'ׁ' + 210: 45, # 'ׂ' + 211: 239, # '׃' + 212: 240, # 'װ' + 213: 241, # 'ױ' + 214: 242, # 'ײ' + 215: 243, # '׳' + 216: 127, # '״' + 217: 244, # None + 218: 245, # None + 219: 246, # None + 220: 247, # None + 221: 248, # None + 222: 249, # None + 223: 250, # None + 224: 9, # 'א' + 225: 8, # 'ב' + 226: 20, # 'ג' + 227: 16, # 'ד' + 228: 3, # 'ה' + 229: 2, # 'ו' + 230: 24, # 'ז' + 231: 14, # 'ח' + 232: 22, # 'ט' + 233: 1, # 'י' + 234: 25, # 'ך' + 235: 15, # 'כ' + 236: 4, # 'ל' + 237: 11, # 'ם' + 238: 6, # 'מ' + 239: 23, # 'ן' + 240: 12, # 'נ' + 241: 19, # 'ס' + 242: 13, # 'ע' + 243: 26, # 'ף' + 244: 18, # 'פ' + 245: 27, # 'ץ' + 246: 21, # 'צ' + 247: 17, # 'ק' + 248: 7, # 'ר' + 249: 10, # 'ש' + 250: 5, # 'ת' + 251: 251, # None + 252: 252, # None + 253: 128, # '\u200e' + 254: 96, # '\u200f' + 255: 253, # None +} -# Model Table: -# total sequences: 100% -# first 512 sequences: 98.4004% -# first 1024 sequences: 1.5981% -# rest sequences: 0.087% -# negative sequences: 0.0015% -HEBREW_LANG_MODEL = ( -0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, -3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, -1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, -1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, -1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, -1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, -1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, -0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, -0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, -1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, -0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, -0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, -0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, -0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, -0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, -0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, -0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, -0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, -0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, -0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, -0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, -0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, -0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, -0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, -1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, -0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, -0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, -0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, -0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, -0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, -0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, -2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, -0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, -0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, -0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, -1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, -0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, -2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, -1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, -2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, -1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, -2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, -0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, -1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, -0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, -) +WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel(charset_name='windows-1255', + language='Hebrew', + char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER, + language_model=HEBREW_LANG_MODEL, + typical_positive_ratio=0.984004, + keep_ascii_letters=False, + alphabet='אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ') -Win1255HebrewModel = { - 'char_to_order_map': WIN1255_CHAR_TO_ORDER_MAP, - 'precedence_matrix': HEBREW_LANG_MODEL, - 'typical_positive_ratio': 0.984004, - 'keep_english_letter': False, - 'charset_name': "windows-1255", - 'language': 'Hebrew', -} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py index bb7c095e1ea6523bd00365384e4c662954c678a0..bbc5cda6441fc696beea666edd8ad6818ef7ea95 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py @@ -1,225 +1,4650 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +HUNGARIAN_LANG_MODEL = { + 28: { # 'A' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 2, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 2, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 2, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 1, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 40: { # 'B' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 3, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 54: { # 'C' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 3, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 45: { # 'D' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 32: { # 'E' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 2, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 50: { # 'F' + 28: 1, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 0, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 49: { # 'G' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 2, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 38: { # 'H' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 0, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 1, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 2, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 39: { # 'I' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 2, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 53: { # 'J' + 28: 2, # 'A' + 40: 0, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 0, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 36: { # 'K' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 2, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 41: { # 'L' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 34: { # 'M' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 3, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 1, # 'ű' + }, + 35: { # 'N' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 2, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 2, # 'Y' + 52: 1, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 47: { # 'O' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 2, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 46: { # 'P' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 3, # 'á' + 15: 2, # 'é' + 30: 0, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 43: { # 'R' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 2, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 2, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 33: { # 'S' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 3, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 1, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 37: { # 'T' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 57: { # 'U' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 48: { # 'V' + 28: 2, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 55: { # 'Y' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 2, # 'Z' + 2: 1, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 52: { # 'Z' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 1, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 2: { # 'a' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 18: { # 'b' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 26: { # 'c' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 1, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 2, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 2, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 17: { # 'd' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 2, # 'k' + 6: 1, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 1: { # 'e' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 2, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 27: { # 'f' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 3, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 3, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 12: { # 'g' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 20: { # 'h' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 9: { # 'i' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 3, # 'ó' + 24: 1, # 'ö' + 31: 2, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 1, # 'ű' + }, + 22: { # 'j' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 1, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 1, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 7: { # 'k' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 1, # 'ú' + 29: 3, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 6: { # 'l' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 3, # 'ő' + 56: 1, # 'ű' + }, + 13: { # 'm' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 1, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 3, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 2, # 'ű' + }, + 4: { # 'n' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 1, # 'x' + 16: 3, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 8: { # 'o' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 23: { # 'p' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 10: { # 'r' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 2, # 'ű' + }, + 5: { # 's' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 3: { # 't' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 1, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 3, # 'ő' + 56: 2, # 'ű' + }, + 21: { # 'u' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 2, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 1, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 1, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 19: { # 'v' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 2, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 62: { # 'x' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 16: { # 'y' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 2, # 'ű' + }, + 11: { # 'z' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 51: { # 'Á' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 44: { # 'É' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 61: { # 'Í' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 0, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 58: { # 'Ó' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 2, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 59: { # 'Ö' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 60: { # 'Ú' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 2, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 63: { # 'Ü' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 14: { # 'á' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 15: { # 'é' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 30: { # 'í' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 25: { # 'ó' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 1, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 24: { # 'ö' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 0, # 'a' + 18: 3, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 31: { # 'ú' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 3, # 'j' + 7: 1, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 29: { # 'ü' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 0, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 42: { # 'ő' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 56: { # 'ű' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -Latin2_HungarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, - 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, -253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, - 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, -159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, -175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, -191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, - 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, -221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, -232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, - 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, -245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, -) - -win1250HungarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, - 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, -253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, - 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, -161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, -177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, -191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, - 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, -221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, -232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, - 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, -245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, -) +# Character Mapping Table(s): +WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 72, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 161, # '€' + 129: 162, # None + 130: 163, # '‚' + 131: 164, # None + 132: 165, # '„' + 133: 166, # '…' + 134: 167, # '†' + 135: 168, # '‡' + 136: 169, # None + 137: 170, # '‰' + 138: 171, # 'Š' + 139: 172, # '‹' + 140: 173, # 'Ś' + 141: 174, # 'Ť' + 142: 175, # 'Ž' + 143: 176, # 'Ź' + 144: 177, # None + 145: 178, # '‘' + 146: 179, # '’' + 147: 180, # '“' + 148: 78, # '”' + 149: 181, # '•' + 150: 69, # '–' + 151: 182, # '—' + 152: 183, # None + 153: 184, # '™' + 154: 185, # 'š' + 155: 186, # '›' + 156: 187, # 'ś' + 157: 188, # 'ť' + 158: 189, # 'ž' + 159: 190, # 'ź' + 160: 191, # '\xa0' + 161: 192, # 'ˇ' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ą' + 166: 197, # '¦' + 167: 76, # '§' + 168: 198, # '¨' + 169: 199, # '©' + 170: 200, # 'Ş' + 171: 201, # '«' + 172: 202, # '¬' + 173: 203, # '\xad' + 174: 204, # '®' + 175: 205, # 'Ż' + 176: 81, # '°' + 177: 206, # '±' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'µ' + 182: 211, # '¶' + 183: 212, # '·' + 184: 213, # '¸' + 185: 214, # 'ą' + 186: 215, # 'ş' + 187: 216, # '»' + 188: 217, # 'Ľ' + 189: 218, # '˝' + 190: 219, # 'ľ' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 83, # 'Â' + 195: 222, # 'Ă' + 196: 80, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 70, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 84, # 'ŕ' + 225: 14, # 'á' + 226: 75, # 'â' + 227: 242, # 'ă' + 228: 71, # 'ä' + 229: 82, # 'ĺ' + 230: 243, # 'ć' + 231: 73, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 85, # 'ę' + 235: 79, # 'ë' + 236: 86, # 'ě' + 237: 30, # 'í' + 238: 77, # 'î' + 239: 87, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 74, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' +} -# Model Table: -# total sequences: 100% -# first 512 sequences: 94.7368% -# first 1024 sequences:5.2623% -# rest sequences: 0.8894% -# negative sequences: 0.0009% -HungarianLangModel = ( -0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, -3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, -3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, -3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, -0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, -0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, -1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, -1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, -1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, -3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, -2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, -2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, -2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, -2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, -2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, -3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, -2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, -2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, -2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, -1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, -1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, -3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, -1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, -1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, -2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, -2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, -2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, -3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, -2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, -1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, -1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, -2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, -2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, -1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, -1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, -2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, -1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, -1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, -2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, -2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, -2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, -1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, -1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, -1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, -0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, -2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, -2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, -1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, -2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, -1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, -1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, -2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, -2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, -2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, -1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, -2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, -0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, -0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, -0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, -2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, -0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, -) +WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1250', + language='Hungarian', + char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű') -Latin2HungarianModel = { - 'char_to_order_map': Latin2_HungarianCharToOrderMap, - 'precedence_matrix': HungarianLangModel, - 'typical_positive_ratio': 0.947368, - 'keep_english_letter': True, - 'charset_name': "ISO-8859-2", - 'language': 'Hungarian', +ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 71, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 159, # '\x80' + 129: 160, # '\x81' + 130: 161, # '\x82' + 131: 162, # '\x83' + 132: 163, # '\x84' + 133: 164, # '\x85' + 134: 165, # '\x86' + 135: 166, # '\x87' + 136: 167, # '\x88' + 137: 168, # '\x89' + 138: 169, # '\x8a' + 139: 170, # '\x8b' + 140: 171, # '\x8c' + 141: 172, # '\x8d' + 142: 173, # '\x8e' + 143: 174, # '\x8f' + 144: 175, # '\x90' + 145: 176, # '\x91' + 146: 177, # '\x92' + 147: 178, # '\x93' + 148: 179, # '\x94' + 149: 180, # '\x95' + 150: 181, # '\x96' + 151: 182, # '\x97' + 152: 183, # '\x98' + 153: 184, # '\x99' + 154: 185, # '\x9a' + 155: 186, # '\x9b' + 156: 187, # '\x9c' + 157: 188, # '\x9d' + 158: 189, # '\x9e' + 159: 190, # '\x9f' + 160: 191, # '\xa0' + 161: 192, # 'Ą' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ľ' + 166: 197, # 'Ś' + 167: 75, # '§' + 168: 198, # '¨' + 169: 199, # 'Š' + 170: 200, # 'Ş' + 171: 201, # 'Ť' + 172: 202, # 'Ź' + 173: 203, # '\xad' + 174: 204, # 'Ž' + 175: 205, # 'Ż' + 176: 79, # '°' + 177: 206, # 'ą' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'ľ' + 182: 211, # 'ś' + 183: 212, # 'ˇ' + 184: 213, # '¸' + 185: 214, # 'š' + 186: 215, # 'ş' + 187: 216, # 'ť' + 188: 217, # 'ź' + 189: 218, # '˝' + 190: 219, # 'ž' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 81, # 'Â' + 195: 222, # 'Ă' + 196: 78, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 69, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 82, # 'ŕ' + 225: 14, # 'á' + 226: 74, # 'â' + 227: 242, # 'ă' + 228: 70, # 'ä' + 229: 80, # 'ĺ' + 230: 243, # 'ć' + 231: 72, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 83, # 'ę' + 235: 77, # 'ë' + 236: 84, # 'ě' + 237: 30, # 'í' + 238: 76, # 'î' + 239: 85, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 73, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' } -Win1250HungarianModel = { - 'char_to_order_map': win1250HungarianCharToOrderMap, - 'precedence_matrix': HungarianLangModel, - 'typical_positive_ratio': 0.947368, - 'keep_english_letter': True, - 'charset_name': "windows-1250", - 'language': 'Hungarian', -} +ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-2', + language='Hungarian', + char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű') + diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py index 15f94c2df021c9cccc761ebeec80146edbb000c9..9a37db573881e426acc756db236be0eb052ef0d9 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py @@ -1,199 +1,4383 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +THAI_LANG_MODEL = { + 5: { # 'ก' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 3, # 'ฎ' + 57: 2, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 1, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 30: { # 'ข' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 2, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 24: { # 'ค' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 3, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 8: { # 'ง' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 1, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 2, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 3, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 26: { # 'จ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 52: { # 'ฉ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 34: { # 'ช' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 1, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 51: { # 'ซ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 47: { # 'ญ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 58: { # 'ฎ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 57: { # 'ฏ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 49: { # 'ฐ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 53: { # 'ฑ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 55: { # 'ฒ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 43: { # 'ณ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 3, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 3, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 20: { # 'ด' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 2, # '็' + 6: 1, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 19: { # 'ต' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 2, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 44: { # 'ถ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 14: { # 'ท' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 3, # 'ศ' + 46: 1, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 48: { # 'ธ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 2, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 3: { # 'น' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 1, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 3, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 3, # 'โ' + 29: 3, # 'ใ' + 33: 3, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 17: { # 'บ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 25: { # 'ป' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 1, # 'ฎ' + 57: 3, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 1, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 2, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 39: { # 'ผ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 0, # 'ุ' + 35: 3, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 62: { # 'ฝ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 2, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 31: { # 'พ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 3, # 'ื' + 32: 1, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 0, # '่' + 7: 1, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 54: { # 'ฟ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 45: { # 'ภ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 9: { # 'ม' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 2, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 16: { # 'ย' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 2: { # 'ร' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 3, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 3, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 3, # 'เ' + 28: 3, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 61: { # 'ฤ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 2, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 15: { # 'ล' + 5: 2, # 'ก' + 30: 3, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 2, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 12: { # 'ว' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 42: { # 'ศ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 3, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 46: { # 'ษ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 2, # 'ฎ' + 57: 1, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 18: { # 'ส' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 3, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 21: { # 'ห' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 4: { # 'อ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 63: { # 'ฯ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 22: { # 'ะ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 10: { # 'ั' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 3, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 1: { # 'า' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 1, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 2, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 36: { # 'ำ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 23: { # 'ิ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 3, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 13: { # 'ี' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 40: { # 'ึ' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 27: { # 'ื' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 32: { # 'ุ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 1, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 35: { # 'ู' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 11: { # 'เ' + 5: 3, # 'ก' + 30: 3, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 3, # 'ฉ' + 34: 3, # 'ช' + 51: 2, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 3, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 28: { # 'แ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 3, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 41: { # 'โ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 29: { # 'ใ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 33: { # 'ไ' + 5: 1, # 'ก' + 30: 2, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 50: { # 'ๆ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 37: { # '็' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 6: { # '่' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 7: { # '้' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 38: { # '์' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 56: { # '๑' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 1, # '๕' + }, + 59: { # '๒' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 1, # '๑' + 59: 1, # '๒' + 60: 3, # '๕' + }, + 60: { # '๕' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 0, # '๕' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# The following result for thai was collected from a limited sample (1M). - -# Character Mapping Table: -TIS620CharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 -188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 -253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 - 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 -209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, -223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, -236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, - 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, - 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, - 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, - 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, - 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, -) +# Character Mapping Table(s): +TIS_620_THAI_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 182, # 'A' + 66: 106, # 'B' + 67: 107, # 'C' + 68: 100, # 'D' + 69: 183, # 'E' + 70: 184, # 'F' + 71: 185, # 'G' + 72: 101, # 'H' + 73: 94, # 'I' + 74: 186, # 'J' + 75: 187, # 'K' + 76: 108, # 'L' + 77: 109, # 'M' + 78: 110, # 'N' + 79: 111, # 'O' + 80: 188, # 'P' + 81: 189, # 'Q' + 82: 190, # 'R' + 83: 89, # 'S' + 84: 95, # 'T' + 85: 112, # 'U' + 86: 113, # 'V' + 87: 191, # 'W' + 88: 192, # 'X' + 89: 193, # 'Y' + 90: 194, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 64, # 'a' + 98: 72, # 'b' + 99: 73, # 'c' + 100: 114, # 'd' + 101: 74, # 'e' + 102: 115, # 'f' + 103: 116, # 'g' + 104: 102, # 'h' + 105: 81, # 'i' + 106: 201, # 'j' + 107: 117, # 'k' + 108: 90, # 'l' + 109: 103, # 'm' + 110: 78, # 'n' + 111: 82, # 'o' + 112: 96, # 'p' + 113: 202, # 'q' + 114: 91, # 'r' + 115: 79, # 's' + 116: 84, # 't' + 117: 104, # 'u' + 118: 105, # 'v' + 119: 97, # 'w' + 120: 98, # 'x' + 121: 92, # 'y' + 122: 203, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 209, # '\x80' + 129: 210, # '\x81' + 130: 211, # '\x82' + 131: 212, # '\x83' + 132: 213, # '\x84' + 133: 88, # '\x85' + 134: 214, # '\x86' + 135: 215, # '\x87' + 136: 216, # '\x88' + 137: 217, # '\x89' + 138: 218, # '\x8a' + 139: 219, # '\x8b' + 140: 220, # '\x8c' + 141: 118, # '\x8d' + 142: 221, # '\x8e' + 143: 222, # '\x8f' + 144: 223, # '\x90' + 145: 224, # '\x91' + 146: 99, # '\x92' + 147: 85, # '\x93' + 148: 83, # '\x94' + 149: 225, # '\x95' + 150: 226, # '\x96' + 151: 227, # '\x97' + 152: 228, # '\x98' + 153: 229, # '\x99' + 154: 230, # '\x9a' + 155: 231, # '\x9b' + 156: 232, # '\x9c' + 157: 233, # '\x9d' + 158: 234, # '\x9e' + 159: 235, # '\x9f' + 160: 236, # None + 161: 5, # 'ก' + 162: 30, # 'ข' + 163: 237, # 'ฃ' + 164: 24, # 'ค' + 165: 238, # 'ฅ' + 166: 75, # 'ฆ' + 167: 8, # 'ง' + 168: 26, # 'จ' + 169: 52, # 'ฉ' + 170: 34, # 'ช' + 171: 51, # 'ซ' + 172: 119, # 'ฌ' + 173: 47, # 'ญ' + 174: 58, # 'ฎ' + 175: 57, # 'ฏ' + 176: 49, # 'ฐ' + 177: 53, # 'ฑ' + 178: 55, # 'ฒ' + 179: 43, # 'ณ' + 180: 20, # 'ด' + 181: 19, # 'ต' + 182: 44, # 'ถ' + 183: 14, # 'ท' + 184: 48, # 'ธ' + 185: 3, # 'น' + 186: 17, # 'บ' + 187: 25, # 'ป' + 188: 39, # 'ผ' + 189: 62, # 'ฝ' + 190: 31, # 'พ' + 191: 54, # 'ฟ' + 192: 45, # 'ภ' + 193: 9, # 'ม' + 194: 16, # 'ย' + 195: 2, # 'ร' + 196: 61, # 'ฤ' + 197: 15, # 'ล' + 198: 239, # 'ฦ' + 199: 12, # 'ว' + 200: 42, # 'ศ' + 201: 46, # 'ษ' + 202: 18, # 'ส' + 203: 21, # 'ห' + 204: 76, # 'ฬ' + 205: 4, # 'อ' + 206: 66, # 'ฮ' + 207: 63, # 'ฯ' + 208: 22, # 'ะ' + 209: 10, # 'ั' + 210: 1, # 'า' + 211: 36, # 'ำ' + 212: 23, # 'ิ' + 213: 13, # 'ี' + 214: 40, # 'ึ' + 215: 27, # 'ื' + 216: 32, # 'ุ' + 217: 35, # 'ู' + 218: 86, # 'ฺ' + 219: 240, # None + 220: 241, # None + 221: 242, # None + 222: 243, # None + 223: 244, # '฿' + 224: 11, # 'เ' + 225: 28, # 'แ' + 226: 41, # 'โ' + 227: 29, # 'ใ' + 228: 33, # 'ไ' + 229: 245, # 'ๅ' + 230: 50, # 'ๆ' + 231: 37, # '็' + 232: 6, # '่' + 233: 7, # '้' + 234: 67, # '๊' + 235: 77, # '๋' + 236: 38, # '์' + 237: 93, # 'ํ' + 238: 246, # '๎' + 239: 247, # '๏' + 240: 68, # '๐' + 241: 56, # '๑' + 242: 59, # '๒' + 243: 65, # '๓' + 244: 69, # '๔' + 245: 60, # '๕' + 246: 70, # '๖' + 247: 80, # '๗' + 248: 71, # '๘' + 249: 87, # '๙' + 250: 248, # '๚' + 251: 249, # '๛' + 252: 250, # None + 253: 251, # None + 254: 252, # None + 255: 253, # None +} -# Model Table: -# total sequences: 100% -# first 512 sequences: 92.6386% -# first 1024 sequences:7.3177% -# rest sequences: 1.0230% -# negative sequences: 0.0436% -ThaiLangModel = ( -0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, -0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, -3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, -0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, -3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, -3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, -3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, -3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, -3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, -3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, -3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, -2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, -3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, -0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, -0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, -3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, -1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, -3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, -3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, -1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, -0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, -2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, -0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, -3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, -2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, -3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, -0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, -3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, -3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, -2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, -3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, -2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, -3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, -3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, -3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, -3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, -1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, -0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, -0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, -3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, -3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, -1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, -3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, -3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, -0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, -0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, -1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, -1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, -3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, -0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, -3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, -0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, -0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, -0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, -0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, -0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, -0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, -0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, -3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, -0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, -0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, -3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, -2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, -0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, -3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, -1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, -1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, -1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -) +TIS_620_THAI_MODEL = SingleByteCharSetModel(charset_name='TIS-620', + language='Thai', + char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, + language_model=THAI_LANG_MODEL, + typical_positive_ratio=0.926386, + keep_ascii_letters=False, + alphabet='กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛') -TIS620ThaiModel = { - 'char_to_order_map': TIS620CharToOrderMap, - 'precedence_matrix': ThaiLangModel, - 'typical_positive_ratio': 0.926386, - 'keep_english_letter': False, - 'charset_name': "TIS-620", - 'language': 'Thai', -} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py b/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py index a427a457398de8076cdcefb5a6c391e89500bce8..43f4230aead2fd8c9f685bd0a726cf0723a9d98d 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py @@ -1,193 +1,4383 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Özgür Baskın - Turkish Language Model -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### -# 255: Control characters that usually does not exist in any text +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +TURKISH_LANG_MODEL = { + 23: { # 'A' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 37: { # 'B' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 47: { # 'C' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 39: { # 'D' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 29: { # 'E' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 52: { # 'F' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 36: { # 'G' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 45: { # 'H' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 2, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 2, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 53: { # 'I' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 60: { # 'J' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 16: { # 'K' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 49: { # 'L' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 2, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 20: { # 'M' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 46: { # 'N' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 42: { # 'O' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 48: { # 'P' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 44: { # 'R' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 35: { # 'S' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 31: { # 'T' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 51: { # 'U' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 38: { # 'V' + 23: 1, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 62: { # 'W' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 43: { # 'Y' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 56: { # 'Z' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 1: { # 'a' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 21: { # 'b' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 28: { # 'c' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 3, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 1, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 12: { # 'd' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 2: { # 'e' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 18: { # 'f' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 1, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 27: { # 'g' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 25: { # 'h' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 3: { # 'i' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 24: { # 'j' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 10: { # 'k' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 5: { # 'l' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 13: { # 'm' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 4: { # 'n' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 15: { # 'o' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 2, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 2, # 'ş' + }, + 26: { # 'p' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 7: { # 'r' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 8: { # 's' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 9: { # 't' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 14: { # 'u' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 32: { # 'v' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 57: { # 'w' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 1, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 58: { # 'x' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 11: { # 'y' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 22: { # 'z' + 23: 2, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 2, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 3, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 2, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 63: { # '·' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 54: { # 'Ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 50: { # 'Ö' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 55: { # 'Ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 59: { # 'â' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 33: { # 'ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 61: { # 'î' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 34: { # 'ö' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 3, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 17: { # 'ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 30: { # 'ğ' + 23: 0, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 41: { # 'İ' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 6: { # 'ı' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 40: { # 'Ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 2, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 3, # 'f' + 27: 0, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 1, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 19: { # 'ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -Latin5_TurkishCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255, 23, 37, 47, 39, 29, 52, 36, 45, 53, 60, 16, 49, 20, 46, 42, - 48, 69, 44, 35, 31, 51, 38, 62, 65, 43, 56,255,255,255,255,255, -255, 1, 21, 28, 12, 2, 18, 27, 25, 3, 24, 10, 5, 13, 4, 15, - 26, 64, 7, 8, 9, 14, 32, 57, 58, 11, 22,255,255,255,255,255, -180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165, -164,163,162,161,160,159,101,158,157,156,155,154,153,152,151,106, -150,149,148,147,146,145,144,100,143,142,141,140,139,138,137,136, - 94, 80, 93,135,105,134,133, 63,132,131,130,129,128,127,126,125, -124,104, 73, 99, 79, 85,123, 54,122, 98, 92,121,120, 91,103,119, - 68,118,117, 97,116,115, 50, 90,114,113,112,111, 55, 41, 40, 86, - 89, 70, 59, 78, 71, 82, 88, 33, 77, 66, 84, 83,110, 75, 61, 96, - 30, 67,109, 74, 87,102, 34, 95, 81,108, 76, 72, 17, 6, 19,107, -) +# Character Mapping Table(s): +ISO_8859_9_TURKISH_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 255, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 255, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 255, # ' ' + 33: 255, # '!' + 34: 255, # '"' + 35: 255, # '#' + 36: 255, # '$' + 37: 255, # '%' + 38: 255, # '&' + 39: 255, # "'" + 40: 255, # '(' + 41: 255, # ')' + 42: 255, # '*' + 43: 255, # '+' + 44: 255, # ',' + 45: 255, # '-' + 46: 255, # '.' + 47: 255, # '/' + 48: 255, # '0' + 49: 255, # '1' + 50: 255, # '2' + 51: 255, # '3' + 52: 255, # '4' + 53: 255, # '5' + 54: 255, # '6' + 55: 255, # '7' + 56: 255, # '8' + 57: 255, # '9' + 58: 255, # ':' + 59: 255, # ';' + 60: 255, # '<' + 61: 255, # '=' + 62: 255, # '>' + 63: 255, # '?' + 64: 255, # '@' + 65: 23, # 'A' + 66: 37, # 'B' + 67: 47, # 'C' + 68: 39, # 'D' + 69: 29, # 'E' + 70: 52, # 'F' + 71: 36, # 'G' + 72: 45, # 'H' + 73: 53, # 'I' + 74: 60, # 'J' + 75: 16, # 'K' + 76: 49, # 'L' + 77: 20, # 'M' + 78: 46, # 'N' + 79: 42, # 'O' + 80: 48, # 'P' + 81: 69, # 'Q' + 82: 44, # 'R' + 83: 35, # 'S' + 84: 31, # 'T' + 85: 51, # 'U' + 86: 38, # 'V' + 87: 62, # 'W' + 88: 65, # 'X' + 89: 43, # 'Y' + 90: 56, # 'Z' + 91: 255, # '[' + 92: 255, # '\\' + 93: 255, # ']' + 94: 255, # '^' + 95: 255, # '_' + 96: 255, # '`' + 97: 1, # 'a' + 98: 21, # 'b' + 99: 28, # 'c' + 100: 12, # 'd' + 101: 2, # 'e' + 102: 18, # 'f' + 103: 27, # 'g' + 104: 25, # 'h' + 105: 3, # 'i' + 106: 24, # 'j' + 107: 10, # 'k' + 108: 5, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 15, # 'o' + 112: 26, # 'p' + 113: 64, # 'q' + 114: 7, # 'r' + 115: 8, # 's' + 116: 9, # 't' + 117: 14, # 'u' + 118: 32, # 'v' + 119: 57, # 'w' + 120: 58, # 'x' + 121: 11, # 'y' + 122: 22, # 'z' + 123: 255, # '{' + 124: 255, # '|' + 125: 255, # '}' + 126: 255, # '~' + 127: 255, # '\x7f' + 128: 180, # '\x80' + 129: 179, # '\x81' + 130: 178, # '\x82' + 131: 177, # '\x83' + 132: 176, # '\x84' + 133: 175, # '\x85' + 134: 174, # '\x86' + 135: 173, # '\x87' + 136: 172, # '\x88' + 137: 171, # '\x89' + 138: 170, # '\x8a' + 139: 169, # '\x8b' + 140: 168, # '\x8c' + 141: 167, # '\x8d' + 142: 166, # '\x8e' + 143: 165, # '\x8f' + 144: 164, # '\x90' + 145: 163, # '\x91' + 146: 162, # '\x92' + 147: 161, # '\x93' + 148: 160, # '\x94' + 149: 159, # '\x95' + 150: 101, # '\x96' + 151: 158, # '\x97' + 152: 157, # '\x98' + 153: 156, # '\x99' + 154: 155, # '\x9a' + 155: 154, # '\x9b' + 156: 153, # '\x9c' + 157: 152, # '\x9d' + 158: 151, # '\x9e' + 159: 106, # '\x9f' + 160: 150, # '\xa0' + 161: 149, # '¡' + 162: 148, # '¢' + 163: 147, # '£' + 164: 146, # '¤' + 165: 145, # '¥' + 166: 144, # '¦' + 167: 100, # '§' + 168: 143, # '¨' + 169: 142, # '©' + 170: 141, # 'ª' + 171: 140, # '«' + 172: 139, # '¬' + 173: 138, # '\xad' + 174: 137, # '®' + 175: 136, # '¯' + 176: 94, # '°' + 177: 80, # '±' + 178: 93, # '²' + 179: 135, # '³' + 180: 105, # '´' + 181: 134, # 'µ' + 182: 133, # '¶' + 183: 63, # '·' + 184: 132, # '¸' + 185: 131, # '¹' + 186: 130, # 'º' + 187: 129, # '»' + 188: 128, # '¼' + 189: 127, # '½' + 190: 126, # '¾' + 191: 125, # '¿' + 192: 124, # 'À' + 193: 104, # 'Á' + 194: 73, # 'Â' + 195: 99, # 'Ã' + 196: 79, # 'Ä' + 197: 85, # 'Å' + 198: 123, # 'Æ' + 199: 54, # 'Ç' + 200: 122, # 'È' + 201: 98, # 'É' + 202: 92, # 'Ê' + 203: 121, # 'Ë' + 204: 120, # 'Ì' + 205: 91, # 'Í' + 206: 103, # 'Î' + 207: 119, # 'Ï' + 208: 68, # 'Ğ' + 209: 118, # 'Ñ' + 210: 117, # 'Ò' + 211: 97, # 'Ó' + 212: 116, # 'Ô' + 213: 115, # 'Õ' + 214: 50, # 'Ö' + 215: 90, # '×' + 216: 114, # 'Ø' + 217: 113, # 'Ù' + 218: 112, # 'Ú' + 219: 111, # 'Û' + 220: 55, # 'Ü' + 221: 41, # 'İ' + 222: 40, # 'Ş' + 223: 86, # 'ß' + 224: 89, # 'à' + 225: 70, # 'á' + 226: 59, # 'â' + 227: 78, # 'ã' + 228: 71, # 'ä' + 229: 82, # 'å' + 230: 88, # 'æ' + 231: 33, # 'ç' + 232: 77, # 'è' + 233: 66, # 'é' + 234: 84, # 'ê' + 235: 83, # 'ë' + 236: 110, # 'ì' + 237: 75, # 'í' + 238: 61, # 'î' + 239: 96, # 'ï' + 240: 30, # 'ğ' + 241: 67, # 'ñ' + 242: 109, # 'ò' + 243: 74, # 'ó' + 244: 87, # 'ô' + 245: 102, # 'õ' + 246: 34, # 'ö' + 247: 95, # '÷' + 248: 81, # 'ø' + 249: 108, # 'ù' + 250: 76, # 'ú' + 251: 72, # 'û' + 252: 17, # 'ü' + 253: 6, # 'ı' + 254: 19, # 'ş' + 255: 107, # 'ÿ' +} -TurkishLangModel = ( -3,2,3,3,3,1,3,3,3,3,3,3,3,3,2,1,1,3,3,1,3,3,0,3,3,3,3,3,0,3,1,3, -3,2,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, -3,2,2,3,3,0,3,3,3,3,3,3,3,2,3,1,0,3,3,1,3,3,0,3,3,3,3,3,0,3,0,3, -3,1,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,0,1,0,1, -3,3,2,3,3,0,3,3,3,3,3,3,3,2,3,1,1,3,3,0,3,3,1,2,3,3,3,3,0,3,0,3, -3,1,1,0,0,0,1,0,0,0,0,1,1,0,1,2,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,1, -3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,1,3,3,2,0,3,2,1,2,2,1,3,3,0,0,0,2, -2,2,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1, -3,3,3,2,3,3,1,2,3,3,3,3,3,3,3,1,3,2,1,0,3,2,0,1,2,3,3,2,1,0,0,2, -2,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0, -1,0,1,3,3,1,3,3,3,3,3,3,3,1,2,0,0,2,3,0,2,3,0,0,2,2,2,3,0,3,0,1, -2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,0,3,2,0,2,3,2,3,3,1,0,0,2, -3,2,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,1, -3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,0,3,3,0,0,2,1,0,0,2,3,2,2,0,0,0,2, -2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,2,0,0,1, -3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,0,1,3,2,1,1,3,2,3,2,1,0,0,2, -2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, -3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,2,0,2,3,0,0,2,2,2,2,0,0,0,2, -3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, -3,3,3,3,3,3,3,2,2,2,2,3,2,3,3,0,3,3,1,1,2,2,0,0,2,2,3,2,0,0,1,3, -0,3,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1, -3,3,3,2,3,3,3,2,1,2,2,3,2,3,3,0,3,2,0,0,1,1,0,1,1,2,1,2,0,0,0,1, -0,3,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0, -3,3,3,2,3,3,2,3,2,2,2,3,3,3,3,1,3,1,1,0,3,2,1,1,3,3,2,3,1,0,0,1, -1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1, -3,2,2,3,3,0,3,3,3,3,3,3,3,2,2,1,0,3,3,1,3,3,0,1,3,3,2,3,0,3,0,3, -2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -2,2,2,3,3,0,3,3,3,3,3,3,3,3,3,0,0,3,2,0,3,3,0,3,2,3,3,3,0,3,1,3, -2,0,0,0,0,0,0,0,0,0,0,1,0,1,2,0,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, -3,3,3,1,2,3,3,1,0,0,1,0,0,3,3,2,3,0,0,2,0,0,2,0,2,0,0,0,2,0,2,0, -0,3,1,0,1,0,0,0,2,2,1,0,1,1,2,1,2,2,2,0,2,1,1,0,0,0,2,0,0,0,0,0, -1,2,1,3,3,0,3,3,3,3,3,2,3,0,0,0,0,2,3,0,2,3,1,0,2,3,1,3,0,3,0,2, -3,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,1,3,3,2,2,3,2,2,0,1,2,3,0,1,2,1,0,1,0,0,0,1,0,2,2,0,0,0,1, -1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0, -3,3,3,1,3,3,1,1,3,3,1,1,3,3,1,0,2,1,2,0,2,1,0,0,1,1,2,1,0,0,0,2, -2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,1,0,2,1,3,0,0,2,0,0,3,3,0,3,0,0,1,0,1,2,0,0,1,1,2,2,0,1,0, -0,1,2,1,1,0,1,0,1,1,1,1,1,0,1,1,1,2,2,1,2,0,1,0,0,0,0,0,0,1,0,0, -3,3,3,2,3,2,3,3,0,2,2,2,3,3,3,0,3,0,0,0,2,2,0,1,2,1,1,1,0,0,0,1, -0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -3,3,3,3,3,3,2,1,2,2,3,3,3,3,2,0,2,0,0,0,2,2,0,0,2,1,3,3,0,0,1,1, -1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0, -1,1,2,3,3,0,3,3,3,3,3,3,2,2,0,2,0,2,3,2,3,2,2,2,2,2,2,2,1,3,2,3, -2,0,2,1,2,2,2,2,1,1,2,2,1,2,2,1,2,0,0,2,1,1,0,2,1,0,0,1,0,0,0,1, -2,3,3,1,1,1,0,1,1,1,2,3,2,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0, -0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,2,2,3,2,3,2,2,1,3,3,3,0,2,1,2,0,2,1,0,0,1,1,1,1,1,0,0,1, -2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, -3,3,3,2,3,3,3,3,3,2,3,1,2,3,3,1,2,0,0,0,0,0,0,0,3,2,1,1,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -3,3,3,2,2,3,3,2,1,1,1,1,1,3,3,0,3,1,0,0,1,1,0,0,3,1,2,1,0,0,0,0, -0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, -3,3,3,2,2,3,2,2,2,3,2,1,1,3,3,0,3,0,0,0,0,1,0,0,3,1,1,2,0,0,0,1, -1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,1,1,3,3,0,3,3,3,3,3,2,2,2,1,2,0,2,1,2,2,1,1,0,1,2,2,2,2,2,2,2, -0,0,2,1,2,1,2,1,0,1,1,3,1,2,1,1,2,0,0,2,0,1,0,1,0,1,0,0,0,1,0,1, -3,3,3,1,3,3,3,0,1,1,0,2,2,3,1,0,3,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,0,0,2,2,1,0,0,1,0,0,3,3,1,3,0,0,1,1,0,2,0,3,0,0,0,2,0,1,1, -0,1,2,0,1,2,2,0,2,2,2,2,1,0,2,1,1,0,2,0,2,1,2,0,0,0,0,0,0,0,0,0, -3,3,3,1,3,2,3,2,0,2,2,2,1,3,2,0,2,1,2,0,1,2,0,0,1,0,2,2,0,0,0,2, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0, -3,3,3,0,3,3,1,1,2,3,1,0,3,2,3,0,3,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0, -1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,3,3,0,3,3,2,3,3,2,2,0,0,0,0,1,2,0,1,3,0,0,0,3,1,1,0,3,0,2, -2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,1,2,2,1,0,3,1,1,1,1,3,3,2,3,0,0,1,0,1,2,0,2,2,0,2,2,0,2,1, -0,2,2,1,1,1,1,0,2,1,1,0,1,1,1,1,2,1,2,1,2,0,1,0,1,0,0,0,0,0,0,0, -3,3,3,0,1,1,3,0,0,1,1,0,0,2,2,0,3,0,0,1,1,0,1,0,0,0,0,0,2,0,0,0, -0,3,1,0,1,0,1,0,2,0,0,1,0,1,0,1,1,1,2,1,1,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,0,2,0,2,0,1,1,1,0,0,3,3,0,2,0,0,1,0,0,2,1,1,0,1,0,1,0,1,0, -0,2,0,1,2,0,2,0,2,1,1,0,1,0,2,1,1,0,2,1,1,0,1,0,0,0,1,1,0,0,0,0, -3,2,3,0,1,0,0,0,0,0,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,0,2,0,0,0, -0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,2,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,0,0,2,3,0,0,1,0,1,0,2,3,2,3,0,0,1,3,0,2,1,0,0,0,0,2,0,1,0, -0,2,1,0,0,1,1,0,2,1,0,0,1,0,0,1,1,0,1,1,2,0,1,0,0,0,0,1,0,0,0,0, -3,2,2,0,0,1,1,0,0,0,0,0,0,3,1,1,1,0,0,0,0,0,1,0,0,0,0,0,2,0,1,0, -0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,3,3,0,2,3,2,2,1,2,2,1,1,2,0,1,3,2,2,2,0,0,2,2,0,0,0,1,2,1, -3,0,2,1,1,0,1,1,1,0,1,2,2,2,1,1,2,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0, -0,1,1,2,3,0,3,3,3,2,2,2,2,1,0,1,0,1,0,1,2,2,0,0,2,2,1,3,1,1,2,1, -0,0,1,1,2,0,1,1,0,0,1,2,0,2,1,1,2,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0, -3,3,2,0,0,3,1,0,0,0,0,0,0,3,2,1,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, -0,2,1,1,0,0,1,0,1,2,0,0,1,1,0,0,2,1,1,1,1,0,2,0,0,0,0,0,0,0,0,0, -3,3,2,0,0,1,0,0,0,0,1,0,0,3,3,2,2,0,0,1,0,0,2,0,1,0,0,0,2,0,1,0, -0,0,1,1,0,0,2,0,2,1,0,0,1,1,2,1,2,0,2,1,2,1,1,1,0,0,1,1,0,0,0,0, -3,3,2,0,0,2,2,0,0,0,1,1,0,2,2,1,3,1,0,1,0,1,2,0,0,0,0,0,1,0,1,0, -0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,0,0,0,1,0,0,1,0,0,2,3,1,2,0,0,1,0,0,2,0,0,0,1,0,2,0,2,0, -0,1,1,2,2,1,2,0,2,1,1,0,0,1,1,0,1,1,1,1,2,1,1,0,0,0,0,0,0,0,0,0, -3,3,3,0,2,1,2,1,0,0,1,1,0,3,3,1,2,0,0,1,0,0,2,0,2,0,1,1,2,0,0,0, -0,0,1,1,1,1,2,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0, -3,3,3,0,2,2,3,2,0,0,1,0,0,2,3,1,0,0,0,0,0,0,2,0,2,0,0,0,2,0,0,0, -0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,0,0,0,0,0,0,0,1,0,0,2,2,2,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, -0,0,2,1,1,0,1,0,2,1,1,0,0,1,1,2,1,0,2,0,2,0,1,0,0,0,2,0,0,0,0,0, -0,0,0,2,2,0,2,1,1,1,1,2,2,0,0,1,0,1,0,0,1,3,0,0,0,0,1,0,0,2,1,0, -0,0,1,0,1,0,0,0,0,0,2,1,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -2,0,0,2,3,0,2,3,1,2,2,0,2,0,0,2,0,2,1,1,1,2,1,0,0,1,2,1,1,2,1,0, -1,0,2,0,1,0,1,1,0,0,2,2,1,2,1,1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,0,2,1,2,0,0,0,1,0,0,3,2,0,1,0,0,1,0,0,2,0,0,0,1,2,1,0,1,0, -0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,2,2,0,2,2,1,1,0,1,1,1,1,1,0,0,1,2,1,1,1,0,1,0,0,0,1,1,1,1, -0,0,2,1,0,1,1,1,0,1,1,2,1,2,1,1,2,0,1,1,2,1,0,2,0,0,0,0,0,0,0,0, -3,2,2,0,0,2,0,0,0,0,0,0,0,2,2,0,2,0,0,1,0,0,2,0,0,0,0,0,2,0,0,0, -0,2,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,3,2,0,2,2,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0, -2,0,1,0,1,0,1,1,0,0,1,2,0,1,0,1,1,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0, -2,2,2,0,1,1,0,0,0,1,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,1,2,0,1,0, -0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,1,0,1,1,1,0,0,0,0,1,2,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -1,1,2,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1, -0,0,1,2,2,0,2,1,2,1,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,0,0,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -2,2,2,0,0,0,1,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -) +ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-9', + language='Turkish', + char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, + language_model=TURKISH_LANG_MODEL, + typical_positive_ratio=0.97029, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş') -Latin5TurkishModel = { - 'char_to_order_map': Latin5_TurkishCharToOrderMap, - 'precedence_matrix': TurkishLangModel, - 'typical_positive_ratio': 0.970290, - 'keep_english_letter': True, - 'charset_name': "ISO-8859-9", - 'language': 'Turkish', -} diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py index 0adb51de5a210aa36849cd149ed0f4ae424fce42..46ba835c66c9f4c3b15b0a0671447d33b3b240d1 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -26,10 +26,22 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from collections import namedtuple + from .charsetprober import CharSetProber from .enums import CharacterCategory, ProbingState, SequenceLikelihood +SingleByteCharSetModel = namedtuple('SingleByteCharSetModel', + ['charset_name', + 'language', + 'char_to_order_map', + 'language_model', + 'typical_positive_ratio', + 'keep_ascii_letters', + 'alphabet']) + + class SingleByteCharSetProber(CharSetProber): SAMPLE_SIZE = 64 SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 @@ -65,25 +77,25 @@ class SingleByteCharSetProber(CharSetProber): if self._name_prober: return self._name_prober.charset_name else: - return self._model['charset_name'] + return self._model.charset_name @property def language(self): if self._name_prober: return self._name_prober.language else: - return self._model.get('language') + return self._model.language def feed(self, byte_str): - if not self._model['keep_english_letter']: + # TODO: Make filter_international_words keep things in self.alphabet + if not self._model.keep_ascii_letters: byte_str = self.filter_international_words(byte_str) if not byte_str: return self.state - char_to_order_map = self._model['char_to_order_map'] - for i, c in enumerate(byte_str): - # XXX: Order is in range 1-64, so one would think we want 0-63 here, - # but that leads to 27 more test failures than before. - order = char_to_order_map[c] + char_to_order_map = self._model.char_to_order_map + language_model = self._model.language_model + for char in byte_str: + order = char_to_order_map.get(char, CharacterCategory.UNDEFINED) # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but # CharacterCategory.SYMBOL is actually 253, so we use CONTROL # to make it closer to the original intent. The only difference @@ -91,20 +103,21 @@ class SingleByteCharSetProber(CharSetProber): # _total_char purposes. if order < CharacterCategory.CONTROL: self._total_char += 1 + # TODO: Follow uchardet's lead and discount confidence for frequent + # control characters. + # See https://github.com/BYVoid/uchardet/commit/55b4f23971db61 if order < self.SAMPLE_SIZE: self._freq_char += 1 if self._last_order < self.SAMPLE_SIZE: self._total_seqs += 1 if not self._reversed: - i = (self._last_order * self.SAMPLE_SIZE) + order - model = self._model['precedence_matrix'][i] - else: # reverse the order of the letters in the lookup - i = (order * self.SAMPLE_SIZE) + self._last_order - model = self._model['precedence_matrix'][i] - self._seq_counters[model] += 1 + lm_cat = language_model[self._last_order][order] + else: + lm_cat = language_model[order][self._last_order] + self._seq_counters[lm_cat] += 1 self._last_order = order - charset_name = self._model['charset_name'] + charset_name = self._model.charset_name if self.state == ProbingState.DETECTING: if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: confidence = self.get_confidence() @@ -125,7 +138,7 @@ class SingleByteCharSetProber(CharSetProber): r = 0.01 if self._total_seqs > 0: r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / - self._total_seqs / self._model['typical_positive_ratio']) + self._total_seqs / self._model.typical_positive_ratio) r = r * self._freq_char / self._total_char if r >= 1.0: r = 0.99 diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py index 98e95dc1a3cbc65e97bc726ab7000955132719dd..bdeef4e15b0dc5a68220b14c9dcec1a019401106 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -27,47 +27,57 @@ ######################### END LICENSE BLOCK ######################### from .charsetgroupprober import CharSetGroupProber -from .sbcharsetprober import SingleByteCharSetProber -from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel, - Latin5CyrillicModel, MacCyrillicModel, - Ibm866Model, Ibm855Model) -from .langgreekmodel import Latin7GreekModel, Win1253GreekModel -from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel -# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel -from .langthaimodel import TIS620ThaiModel -from .langhebrewmodel import Win1255HebrewModel from .hebrewprober import HebrewProber -from .langturkishmodel import Latin5TurkishModel +from .langbulgarianmodel import (ISO_8859_5_BULGARIAN_MODEL, + WINDOWS_1251_BULGARIAN_MODEL) +from .langgreekmodel import ISO_8859_7_GREEK_MODEL, WINDOWS_1253_GREEK_MODEL +from .langhebrewmodel import WINDOWS_1255_HEBREW_MODEL +# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL, +# WINDOWS_1250_HUNGARIAN_MODEL) +from .langrussianmodel import (IBM855_RUSSIAN_MODEL, IBM866_RUSSIAN_MODEL, + ISO_8859_5_RUSSIAN_MODEL, KOI8_R_RUSSIAN_MODEL, + MACCYRILLIC_RUSSIAN_MODEL, + WINDOWS_1251_RUSSIAN_MODEL) +from .langthaimodel import TIS_620_THAI_MODEL +from .langturkishmodel import ISO_8859_9_TURKISH_MODEL +from .sbcharsetprober import SingleByteCharSetProber class SBCSGroupProber(CharSetGroupProber): def __init__(self): super(SBCSGroupProber, self).__init__() + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber(WINDOWS_1255_HEBREW_MODEL, + False, hebrew_prober) + # TODO: See if using ISO-8859-8 Hebrew model works better here, since + # it's actually the visual one + visual_hebrew_prober = SingleByteCharSetProber(WINDOWS_1255_HEBREW_MODEL, + True, hebrew_prober) + hebrew_prober.set_model_probers(logical_hebrew_prober, + visual_hebrew_prober) + # TODO: ORDER MATTERS HERE. I changed the order vs what was in master + # and several tests failed that did not before. Some thought + # should be put into the ordering, and we should consider making + # order not matter here, because that is very counter-intuitive. self.probers = [ - SingleByteCharSetProber(Win1251CyrillicModel), - SingleByteCharSetProber(Koi8rModel), - SingleByteCharSetProber(Latin5CyrillicModel), - SingleByteCharSetProber(MacCyrillicModel), - SingleByteCharSetProber(Ibm866Model), - SingleByteCharSetProber(Ibm855Model), - SingleByteCharSetProber(Latin7GreekModel), - SingleByteCharSetProber(Win1253GreekModel), - SingleByteCharSetProber(Latin5BulgarianModel), - SingleByteCharSetProber(Win1251BulgarianModel), + SingleByteCharSetProber(WINDOWS_1251_RUSSIAN_MODEL), + SingleByteCharSetProber(KOI8_R_RUSSIAN_MODEL), + SingleByteCharSetProber(ISO_8859_5_RUSSIAN_MODEL), + SingleByteCharSetProber(MACCYRILLIC_RUSSIAN_MODEL), + SingleByteCharSetProber(IBM866_RUSSIAN_MODEL), + SingleByteCharSetProber(IBM855_RUSSIAN_MODEL), + SingleByteCharSetProber(ISO_8859_7_GREEK_MODEL), + SingleByteCharSetProber(WINDOWS_1253_GREEK_MODEL), + SingleByteCharSetProber(ISO_8859_5_BULGARIAN_MODEL), + SingleByteCharSetProber(WINDOWS_1251_BULGARIAN_MODEL), # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) # after we retrain model. - # SingleByteCharSetProber(Latin2HungarianModel), - # SingleByteCharSetProber(Win1250HungarianModel), - SingleByteCharSetProber(TIS620ThaiModel), - SingleByteCharSetProber(Latin5TurkishModel), + # SingleByteCharSetProber(ISO_8859_2_HUNGARIAN_MODEL), + # SingleByteCharSetProber(WINDOWS_1250_HUNGARIAN_MODEL), + SingleByteCharSetProber(TIS_620_THAI_MODEL), + SingleByteCharSetProber(ISO_8859_9_TURKISH_MODEL), + hebrew_prober, + logical_hebrew_prober, + visual_hebrew_prober, ] - hebrew_prober = HebrewProber() - logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, - False, hebrew_prober) - visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True, - hebrew_prober) - hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) - self.probers.extend([hebrew_prober, logical_hebrew_prober, - visual_hebrew_prober]) - self.reset() diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py b/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py index 7b4e92d6158527736e3d14d6f725edada8f94b00..055a8ac1b1d21ed8d6d8c9a1217e79df9d1396c1 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py @@ -266,7 +266,7 @@ class UniversalDetector(object): 'language': max_prober.language} # Log all prober confidences if none met MINIMUM_THRESHOLD - if self.logger.getEffectiveLevel() == logging.DEBUG: + if self.logger.getEffectiveLevel() <= logging.DEBUG: if self.result['encoding'] is None: self.logger.debug('no probers hit minimum threshold') for group_prober in self._charset_probers: @@ -280,7 +280,7 @@ class UniversalDetector(object): prober.get_confidence()) else: self.logger.debug('%s %s confidence = %s', - prober.charset_name, - prober.language, - prober.get_confidence()) + group_prober.charset_name, + group_prober.language, + group_prober.get_confidence()) return self.result diff --git a/venv/Lib/site-packages/pip/_vendor/chardet/version.py b/venv/Lib/site-packages/pip/_vendor/chardet/version.py index bb2a34a70ea760ad1f58979acfc8fa466e30c511..70369b9d663414c24f1e042c5b30a8f8c7bbd2b2 100644 --- a/venv/Lib/site-packages/pip/_vendor/chardet/version.py +++ b/venv/Lib/site-packages/pip/_vendor/chardet/version.py @@ -5,5 +5,5 @@ from within setup.py and from chardet subpackages. :author: Dan Blanchard (dan.blanchard@gmail.com) """ -__version__ = "3.0.4" +__version__ = "4.0.0" VERSION = __version__.split('.') diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py b/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py index 34c263cc8bb4a12d99b9d375193d4eb634ed094a..b149ed79b0a1d5808a7e392876c2f5aae4b5057c 100644 --- a/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/colorama/__init__.py @@ -3,4 +3,4 @@ from .initialise import init, deinit, reinit, colorama_text from .ansi import Fore, Back, Style, Cursor from .ansitowin32 import AnsiToWin32 -__version__ = '0.4.3' +__version__ = '0.4.4' diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py b/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py index 78776588db9410924d8e4af0922fbc3960a37624..11ec695ff79627463a0282d25079527562de9e42 100644 --- a/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py +++ b/venv/Lib/site-packages/pip/_vendor/colorama/ansi.py @@ -6,7 +6,7 @@ See: http://en.wikipedia.org/wiki/ANSI_escape_code CSI = '\033[' OSC = '\033]' -BEL = '\007' +BEL = '\a' def code_to_chars(code): diff --git a/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py b/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py index 359c92be50ee3c97aafbc8d31c16e5fbf2e6d022..6039a0543204739849335ad27894cf64224ad828 100644 --- a/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py +++ b/venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -3,7 +3,7 @@ import re import sys import os -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL from .winterm import WinTerm, WinColor, WinStyle from .win32 import windll, winapi_test @@ -68,7 +68,7 @@ class AnsiToWin32(object): win32 function calls. ''' ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # The wrapped stream (normally sys.stdout or sys.stderr) @@ -247,11 +247,12 @@ class AnsiToWin32(object): start, end = match.span() text = text[:start] + text[end:] paramstring, command = match.groups() - if command in '\x07': # \x07 = BEL - params = paramstring.split(";") - # 0 - change title and icon (we will only change title) - # 1 - change icon (we don't support this) - # 2 - change title - if params[0] in '02': - winterm.set_title(params[1]) + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) return text diff --git a/venv/Lib/site-packages/pip/_vendor/contextlib2.py b/venv/Lib/site-packages/pip/_vendor/contextlib2.py deleted file mode 100644 index 3aae8f4117cf8824748f5b7bc74b0aca4516bb6a..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/contextlib2.py +++ /dev/null @@ -1,518 +0,0 @@ -"""contextlib2 - backports and enhancements to the contextlib module""" - -import abc -import sys -import warnings -from collections import deque -from functools import wraps - -__all__ = ["contextmanager", "closing", "nullcontext", - "AbstractContextManager", - "ContextDecorator", "ExitStack", - "redirect_stdout", "redirect_stderr", "suppress"] - -# Backwards compatibility -__all__ += ["ContextStack"] - - -# Backport abc.ABC -if sys.version_info[:2] >= (3, 4): - _abc_ABC = abc.ABC -else: - _abc_ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) - - -# Backport classic class MRO -def _classic_mro(C, result): - if C in result: - return - result.append(C) - for B in C.__bases__: - _classic_mro(B, result) - return result - - -# Backport _collections_abc._check_methods -def _check_methods(C, *methods): - try: - mro = C.__mro__ - except AttributeError: - mro = tuple(_classic_mro(C, [])) - - for method in methods: - for B in mro: - if method in B.__dict__: - if B.__dict__[method] is None: - return NotImplemented - break - else: - return NotImplemented - return True - - -class AbstractContextManager(_abc_ABC): - """An abstract base class for context managers.""" - - def __enter__(self): - """Return `self` upon entering the runtime context.""" - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - """Raise any exception triggered within the runtime context.""" - return None - - @classmethod - def __subclasshook__(cls, C): - """Check whether subclass is considered a subclass of this ABC.""" - if cls is AbstractContextManager: - return _check_methods(C, "__enter__", "__exit__") - return NotImplemented - - -class ContextDecorator(object): - """A base class or mixin that enables context managers to work as decorators.""" - - def refresh_cm(self): - """Returns the context manager used to actually wrap the call to the - decorated function. - - The default implementation just returns *self*. - - Overriding this method allows otherwise one-shot context managers - like _GeneratorContextManager to support use as decorators via - implicit recreation. - - DEPRECATED: refresh_cm was never added to the standard library's - ContextDecorator API - """ - warnings.warn("refresh_cm was never added to the standard library", - DeprecationWarning) - return self._recreate_cm() - - def _recreate_cm(self): - """Return a recreated instance of self. - - Allows an otherwise one-shot context manager like - _GeneratorContextManager to support use as - a decorator via implicit recreation. - - This is a private interface just for _GeneratorContextManager. - See issue #11647 for details. - """ - return self - - def __call__(self, func): - @wraps(func) - def inner(*args, **kwds): - with self._recreate_cm(): - return func(*args, **kwds) - return inner - - -class _GeneratorContextManager(ContextDecorator): - """Helper for @contextmanager decorator.""" - - def __init__(self, func, args, kwds): - self.gen = func(*args, **kwds) - self.func, self.args, self.kwds = func, args, kwds - # Issue 19330: ensure context manager instances have good docstrings - doc = getattr(func, "__doc__", None) - if doc is None: - doc = type(self).__doc__ - self.__doc__ = doc - # Unfortunately, this still doesn't provide good help output when - # inspecting the created context manager instances, since pydoc - # currently bypasses the instance docstring and shows the docstring - # for the class instead. - # See http://bugs.python.org/issue19404 for more details. - - def _recreate_cm(self): - # _GCM instances are one-shot context managers, so the - # CM must be recreated each time a decorated function is - # called - return self.__class__(self.func, self.args, self.kwds) - - def __enter__(self): - try: - return next(self.gen) - except StopIteration: - raise RuntimeError("generator didn't yield") - - def __exit__(self, type, value, traceback): - if type is None: - try: - next(self.gen) - except StopIteration: - return - else: - raise RuntimeError("generator didn't stop") - else: - if value is None: - # Need to force instantiation so we can reliably - # tell if we get the same exception back - value = type() - try: - self.gen.throw(type, value, traceback) - raise RuntimeError("generator didn't stop after throw()") - except StopIteration as exc: - # Suppress StopIteration *unless* it's the same exception that - # was passed to throw(). This prevents a StopIteration - # raised inside the "with" statement from being suppressed. - return exc is not value - except RuntimeError as exc: - # Don't re-raise the passed in exception - if exc is value: - return False - # Likewise, avoid suppressing if a StopIteration exception - # was passed to throw() and later wrapped into a RuntimeError - # (see PEP 479). - if _HAVE_EXCEPTION_CHAINING and exc.__cause__ is value: - return False - raise - except: - # only re-raise if it's *not* the exception that was - # passed to throw(), because __exit__() must not raise - # an exception unless __exit__() itself failed. But throw() - # has to raise the exception to signal propagation, so this - # fixes the impedance mismatch between the throw() protocol - # and the __exit__() protocol. - # - if sys.exc_info()[1] is not value: - raise - - -def contextmanager(func): - """@contextmanager decorator. - - Typical usage: - - @contextmanager - def some_generator(): - - try: - yield - finally: - - - This makes this: - - with some_generator() as : - - - equivalent to this: - - - try: - = - - finally: - - - """ - @wraps(func) - def helper(*args, **kwds): - return _GeneratorContextManager(func, args, kwds) - return helper - - -class closing(object): - """Context to automatically close something at the end of a block. - - Code like this: - - with closing(.open()) as f: - - - is equivalent to this: - - f = .open() - try: - - finally: - f.close() - - """ - def __init__(self, thing): - self.thing = thing - - def __enter__(self): - return self.thing - - def __exit__(self, *exc_info): - self.thing.close() - - -class _RedirectStream(object): - - _stream = None - - def __init__(self, new_target): - self._new_target = new_target - # We use a list of old targets to make this CM re-entrant - self._old_targets = [] - - def __enter__(self): - self._old_targets.append(getattr(sys, self._stream)) - setattr(sys, self._stream, self._new_target) - return self._new_target - - def __exit__(self, exctype, excinst, exctb): - setattr(sys, self._stream, self._old_targets.pop()) - - -class redirect_stdout(_RedirectStream): - """Context manager for temporarily redirecting stdout to another file. - - # How to send help() to stderr - with redirect_stdout(sys.stderr): - help(dir) - - # How to write help() to a file - with open('help.txt', 'w') as f: - with redirect_stdout(f): - help(pow) - """ - - _stream = "stdout" - - -class redirect_stderr(_RedirectStream): - """Context manager for temporarily redirecting stderr to another file.""" - - _stream = "stderr" - - -class suppress(object): - """Context manager to suppress specified exceptions - - After the exception is suppressed, execution proceeds with the next - statement following the with statement. - - with suppress(FileNotFoundError): - os.remove(somefile) - # Execution still resumes here if the file was already removed - """ - - def __init__(self, *exceptions): - self._exceptions = exceptions - - def __enter__(self): - pass - - def __exit__(self, exctype, excinst, exctb): - # Unlike isinstance and issubclass, CPython exception handling - # currently only looks at the concrete type hierarchy (ignoring - # the instance and subclass checking hooks). While Guido considers - # that a bug rather than a feature, it's a fairly hard one to fix - # due to various internal implementation details. suppress provides - # the simpler issubclass based semantics, rather than trying to - # exactly reproduce the limitations of the CPython interpreter. - # - # See http://bugs.python.org/issue12029 for more details - return exctype is not None and issubclass(exctype, self._exceptions) - - -# Context manipulation is Python 3 only -_HAVE_EXCEPTION_CHAINING = sys.version_info[0] >= 3 -if _HAVE_EXCEPTION_CHAINING: - def _make_context_fixer(frame_exc): - def _fix_exception_context(new_exc, old_exc): - # Context may not be correct, so find the end of the chain - while 1: - exc_context = new_exc.__context__ - if exc_context is old_exc: - # Context is already set correctly (see issue 20317) - return - if exc_context is None or exc_context is frame_exc: - break - new_exc = exc_context - # Change the end of the chain to point to the exception - # we expect it to reference - new_exc.__context__ = old_exc - return _fix_exception_context - - def _reraise_with_existing_context(exc_details): - try: - # bare "raise exc_details[1]" replaces our carefully - # set-up context - fixed_ctx = exc_details[1].__context__ - raise exc_details[1] - except BaseException: - exc_details[1].__context__ = fixed_ctx - raise -else: - # No exception context in Python 2 - def _make_context_fixer(frame_exc): - return lambda new_exc, old_exc: None - - # Use 3 argument raise in Python 2, - # but use exec to avoid SyntaxError in Python 3 - def _reraise_with_existing_context(exc_details): - exc_type, exc_value, exc_tb = exc_details - exec("raise exc_type, exc_value, exc_tb") - -# Handle old-style classes if they exist -try: - from types import InstanceType -except ImportError: - # Python 3 doesn't have old-style classes - _get_type = type -else: - # Need to handle old-style context managers on Python 2 - def _get_type(obj): - obj_type = type(obj) - if obj_type is InstanceType: - return obj.__class__ # Old-style class - return obj_type # New-style class - - -# Inspired by discussions on http://bugs.python.org/issue13585 -class ExitStack(object): - """Context manager for dynamic management of a stack of exit callbacks - - For example: - - with ExitStack() as stack: - files = [stack.enter_context(open(fname)) for fname in filenames] - # All opened files will automatically be closed at the end of - # the with statement, even if attempts to open files later - # in the list raise an exception - - """ - def __init__(self): - self._exit_callbacks = deque() - - def pop_all(self): - """Preserve the context stack by transferring it to a new instance""" - new_stack = type(self)() - new_stack._exit_callbacks = self._exit_callbacks - self._exit_callbacks = deque() - return new_stack - - def _push_cm_exit(self, cm, cm_exit): - """Helper to correctly register callbacks to __exit__ methods""" - def _exit_wrapper(*exc_details): - return cm_exit(cm, *exc_details) - _exit_wrapper.__self__ = cm - self.push(_exit_wrapper) - - def push(self, exit): - """Registers a callback with the standard __exit__ method signature - - Can suppress exceptions the same way __exit__ methods can. - - Also accepts any object with an __exit__ method (registering a call - to the method instead of the object itself) - """ - # We use an unbound method rather than a bound method to follow - # the standard lookup behaviour for special methods - _cb_type = _get_type(exit) - try: - exit_method = _cb_type.__exit__ - except AttributeError: - # Not a context manager, so assume its a callable - self._exit_callbacks.append(exit) - else: - self._push_cm_exit(exit, exit_method) - return exit # Allow use as a decorator - - def callback(self, callback, *args, **kwds): - """Registers an arbitrary callback and arguments. - - Cannot suppress exceptions. - """ - def _exit_wrapper(exc_type, exc, tb): - callback(*args, **kwds) - # We changed the signature, so using @wraps is not appropriate, but - # setting __wrapped__ may still help with introspection - _exit_wrapper.__wrapped__ = callback - self.push(_exit_wrapper) - return callback # Allow use as a decorator - - def enter_context(self, cm): - """Enters the supplied context manager - - If successful, also pushes its __exit__ method as a callback and - returns the result of the __enter__ method. - """ - # We look up the special methods on the type to match the with statement - _cm_type = _get_type(cm) - _exit = _cm_type.__exit__ - result = _cm_type.__enter__(cm) - self._push_cm_exit(cm, _exit) - return result - - def close(self): - """Immediately unwind the context stack""" - self.__exit__(None, None, None) - - def __enter__(self): - return self - - def __exit__(self, *exc_details): - received_exc = exc_details[0] is not None - - # We manipulate the exception state so it behaves as though - # we were actually nesting multiple with statements - frame_exc = sys.exc_info()[1] - _fix_exception_context = _make_context_fixer(frame_exc) - - # Callbacks are invoked in LIFO order to match the behaviour of - # nested context managers - suppressed_exc = False - pending_raise = False - while self._exit_callbacks: - cb = self._exit_callbacks.pop() - try: - if cb(*exc_details): - suppressed_exc = True - pending_raise = False - exc_details = (None, None, None) - except: - new_exc_details = sys.exc_info() - # simulate the stack of exceptions by setting the context - _fix_exception_context(new_exc_details[1], exc_details[1]) - pending_raise = True - exc_details = new_exc_details - if pending_raise: - _reraise_with_existing_context(exc_details) - return received_exc and suppressed_exc - - -# Preserve backwards compatibility -class ContextStack(ExitStack): - """Backwards compatibility alias for ExitStack""" - - def __init__(self): - warnings.warn("ContextStack has been renamed to ExitStack", - DeprecationWarning) - super(ContextStack, self).__init__() - - def register_exit(self, callback): - return self.push(callback) - - def register(self, callback, *args, **kwds): - return self.callback(callback, *args, **kwds) - - def preserve(self): - return self.pop_all() - - -class nullcontext(AbstractContextManager): - """Context manager that does no additional processing. - Used as a stand-in for a normal context manager, when a particular - block of code is only sometimes used with a normal context manager: - cm = optional_cm if condition else nullcontext() - with cm: - # Perform operation, using optional_cm if condition is True - """ - - def __init__(self, enter_result=None): - self.enter_result = enter_result - - def __enter__(self): - return self.enter_result - - def __exit__(self, *excinfo): - pass diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py b/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py index e19aebdc4cc8700bb740176baca1cda6dce1c99c..11549481074d8545dc59d718594b8a2548c5e81f 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/__init__.py @@ -6,7 +6,7 @@ # import logging -__version__ = '0.3.0' +__version__ = '0.3.3' class DistlibException(Exception): pass diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py index 159e49ee8c2aa698aea9a191db91a14716187324..10ed362539718aed693f8155ce7ad55c64163aff 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/_backport/shutil.py @@ -14,7 +14,10 @@ import sys import stat from os.path import abspath import fnmatch -import collections +try: + from collections.abc import Callable +except ImportError: + from collections import Callable import errno from . import tarfile @@ -528,7 +531,7 @@ def register_archive_format(name, function, extra_args=None, description=''): """ if extra_args is None: extra_args = [] - if not isinstance(function, collections.Callable): + if not isinstance(function, Callable): raise TypeError('The %s object is not callable' % function) if not isinstance(extra_args, (tuple, list)): raise TypeError('extra_args needs to be a sequence') @@ -621,7 +624,7 @@ def _check_unpack_options(extensions, function, extra_args): raise RegistryError(msg % (extension, existing_extensions[extension])) - if not isinstance(function, collections.Callable): + if not isinstance(function, Callable): raise TypeError('The registered function must be a callable') diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/compat.py b/venv/Lib/site-packages/pip/_vendor/distlib/compat.py index ff328c8ee491f9f3c943cf637dc7ea17ea33ee3b..e594106956f4ed5f0c2394eb50a02c304e3ba167 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/compat.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/compat.py @@ -48,17 +48,18 @@ if sys.version_info[0] < 3: # pragma: no cover from itertools import ifilter as filter from itertools import ifilterfalse as filterfalse - _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 + # Leaving this around for now, in case it needs resurrecting in some way + # _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 else: # pragma: no cover from io import StringIO @@ -68,7 +69,7 @@ else: # pragma: no cover import builtins import configparser import shutil - from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, + from urllib.parse import (urlparse, urlunparse, urljoin, quote, unquote, urlsplit, urlunsplit, splittype) from urllib.request import (urlopen, urlretrieve, Request, url2pathname, pathname2url, @@ -88,6 +89,7 @@ else: # pragma: no cover from itertools import filterfalse filter = filter + try: from ssl import match_hostname, CertificateError except ImportError: # pragma: no cover @@ -319,7 +321,7 @@ except ImportError: # pragma: no cover try: callable = callable except NameError: # pragma: no cover - from collections import Callable + from collections.abc import Callable def callable(obj): return isinstance(obj, Callable) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/database.py b/venv/Lib/site-packages/pip/_vendor/distlib/database.py index c16c0c8d9edd833e2a2bcf5322766c4709fc1e64..0a90c300ba83967a50ca0fe7dfbcd5c1406914eb 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/database.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/database.py @@ -550,7 +550,7 @@ class InstalledDistribution(BaseInstalledDistribution): r = finder.find(WHEEL_METADATA_FILENAME) # Temporary - for legacy support if r is None: - r = finder.find('METADATA') + r = finder.find(LEGACY_METADATA_FILENAME) if r is None: raise ValueError('no %s found in %s' % (METADATA_FILENAME, path)) diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/index.py b/venv/Lib/site-packages/pip/_vendor/distlib/index.py index 7a87cdcf7a19987ad0344b8c5d161027b345e212..b1fbbf8e8d2a47d0a7d2fe0b4568fd11f8be4c36 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/index.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/index.py @@ -18,7 +18,7 @@ except ImportError: from . import DistlibException from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, urlparse, build_opener, string_types) -from .util import cached_property, zip_dir, ServerProxy +from .util import zip_dir, ServerProxy logger = logging.getLogger(__name__) @@ -67,21 +67,17 @@ class PackageIndex(object): Get the distutils command for interacting with PyPI configurations. :return: the command. """ - from distutils.core import Distribution - from distutils.config import PyPIRCCommand - d = Distribution() - return PyPIRCCommand(d) + from .util import _get_pypirc_command as cmd + return cmd() def read_configuration(self): """ - Read the PyPI access configuration as supported by distutils, getting - PyPI to do the actual work. This populates ``username``, ``password``, - ``realm`` and ``url`` attributes from the configuration. + Read the PyPI access configuration as supported by distutils. This populates + ``username``, ``password``, ``realm`` and ``url`` attributes from the + configuration. """ - # get distutils to do the work - c = self._get_pypirc_command() - c.repository = self.url - cfg = c._read_pypirc() + from .util import _load_pypirc + cfg = _load_pypirc(self) self.username = cfg.get('username') self.password = cfg.get('password') self.realm = cfg.get('realm', 'pypi') @@ -91,13 +87,10 @@ class PackageIndex(object): """ Save the PyPI access configuration. You must have set ``username`` and ``password`` attributes before calling this method. - - Again, distutils is used to do the actual work. """ self.check_credentials() - # get distutils to do the work - c = self._get_pypirc_command() - c._store_pypirc(self.username, self.password) + from .util import _store_pypirc + _store_pypirc(self) def check_credentials(self): """ diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/locators.py b/venv/Lib/site-packages/pip/_vendor/distlib/locators.py index 12a1d06351e50b52c8672974d4a9053b7641abaf..0c7d6391438b32042e437868fc2e4aa86f60c23b 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/locators.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/locators.py @@ -20,14 +20,14 @@ import zlib from . import DistlibException from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, - queue, quote, unescape, string_types, build_opener, + queue, quote, unescape, build_opener, HTTPRedirectHandler as BaseRedirectHandler, text_type, Request, HTTPError, URLError) from .database import Distribution, DistributionPath, make_dist from .metadata import Metadata, MetadataInvalidError -from .util import (cached_property, parse_credentials, ensure_slash, - split_filename, get_project_data, parse_requirement, - parse_name_and_version, ServerProxy, normalize_name) +from .util import (cached_property, ensure_slash, split_filename, get_project_data, + parse_requirement, parse_name_and_version, ServerProxy, + normalize_name) from .version import get_scheme, UnsupportedVersionError from .wheel import Wheel, is_compatible @@ -378,13 +378,13 @@ class Locator(object): continue try: if not matcher.match(k): - logger.debug('%s did not match %r', matcher, k) + pass # logger.debug('%s did not match %r', matcher, k) else: if prereleases or not vcls(k).is_prerelease: slist.append(k) - else: - logger.debug('skipping pre-release ' - 'version %s of %s', k, matcher.name) + # else: + # logger.debug('skipping pre-release ' + # 'version %s of %s', k, matcher.name) except Exception: # pragma: no cover logger.warning('error matching %s with %r', matcher, k) pass # slist.append(k) @@ -593,7 +593,7 @@ class SimpleScrapingLocator(Locator): # These are used to deal with various Content-Encoding schemes. decoders = { 'deflate': zlib.decompress, - 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(d)).read(), + 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(b)).read(), 'none': lambda b: b, } @@ -1062,8 +1062,6 @@ default_locator = AggregatingLocator( locate = default_locator.locate -NAME_VERSION_RE = re.compile(r'(?P[\w-]+)\s*' - r'\(\s*(==\s*)?(?P[^)]+)\)$') class DependencyFinder(object): """ diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/markers.py b/venv/Lib/site-packages/pip/_vendor/distlib/markers.py index ee1f3e23655b53293cc2d5bf50e835ac2662cc7f..b43136fa11e8204063835801c75819a2fe750492 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/markers.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/markers.py @@ -13,20 +13,29 @@ Parser for the environment markers micro-language defined in PEP 508. # as ~= and === which aren't in Python, necessitating a different approach. import os +import re import sys import platform -import re -from .compat import python_implementation, urlparse, string_types +from .compat import string_types from .util import in_venv, parse_marker +from .version import NormalizedVersion as NV __all__ = ['interpret'] +_VERSION_PATTERN = re.compile(r'((\d+(\.\d+)*\w*)|\'(\d+(\.\d+)*\w*)\'|\"(\d+(\.\d+)*\w*)\")') + def _is_literal(o): if not isinstance(o, string_types) or not o: return False return o[0] in '\'"' +def _get_versions(s): + result = [] + for m in _VERSION_PATTERN.finditer(s): + result.append(NV(m.groups()[0])) + return set(result) + class Evaluator(object): """ This class is used to evaluate marker expessions. @@ -71,6 +80,13 @@ class Evaluator(object): lhs = self.evaluate(elhs, context) rhs = self.evaluate(erhs, context) + if ((elhs == 'python_version' or erhs == 'python_version') and + op in ('<', '<=', '>', '>=', '===', '==', '!=', '~=')): + lhs = NV(lhs) + rhs = NV(rhs) + elif elhs == 'python_version' and op in ('in', 'not in'): + lhs = NV(lhs) + rhs = _get_versions(rhs) result = self.operations[op](lhs, rhs) return result diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py b/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py index 2d61378e9942fb67c15544db57bac502fa50376b..6a26b0ab232e6c474dc3309a1a64bfce790e98a6 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py @@ -5,7 +5,7 @@ # """Implementation of the Metadata for Python packages PEPs. -Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental). +Supports all metadata formats (1.0, 1.1, 1.2, 1.3/2.1 and withdrawn 2.0). """ from __future__ import unicode_literals @@ -94,8 +94,9 @@ _426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', # See issue #106: Sometimes 'Requires' and 'Provides' occur wrongly in # the metadata. Include them in the tuple literal below to allow them # (for now). +# Ditto for Obsoletes - see issue #140. _566_FIELDS = _426_FIELDS + ('Description-Content-Type', - 'Requires', 'Provides') + 'Requires', 'Provides', 'Obsoletes') _566_MARKERS = ('Description-Content-Type',) @@ -117,7 +118,8 @@ def _version2fieldlist(version): elif version == '1.2': return _345_FIELDS elif version in ('1.3', '2.1'): - return _345_FIELDS + _566_FIELDS + # avoid adding field names if already there + return _345_FIELDS + tuple(f for f in _566_FIELDS if f not in _345_FIELDS) elif version == '2.0': return _426_FIELDS raise MetadataUnrecognizedVersionError(version) @@ -194,38 +196,12 @@ def _best_version(fields): return '2.0' +# This follows the rules about transforming keys as described in +# https://www.python.org/dev/peps/pep-0566/#id17 _ATTR2FIELD = { - 'metadata_version': 'Metadata-Version', - 'name': 'Name', - 'version': 'Version', - 'platform': 'Platform', - 'supported_platform': 'Supported-Platform', - 'summary': 'Summary', - 'description': 'Description', - 'keywords': 'Keywords', - 'home_page': 'Home-page', - 'author': 'Author', - 'author_email': 'Author-email', - 'maintainer': 'Maintainer', - 'maintainer_email': 'Maintainer-email', - 'license': 'License', - 'classifier': 'Classifier', - 'download_url': 'Download-URL', - 'obsoletes_dist': 'Obsoletes-Dist', - 'provides_dist': 'Provides-Dist', - 'requires_dist': 'Requires-Dist', - 'setup_requires_dist': 'Setup-Requires-Dist', - 'requires_python': 'Requires-Python', - 'requires_external': 'Requires-External', - 'requires': 'Requires', - 'provides': 'Provides', - 'obsoletes': 'Obsoletes', - 'project_url': 'Project-URL', - 'private_version': 'Private-Version', - 'obsoleted_by': 'Obsoleted-By', - 'extension': 'Extension', - 'provides_extra': 'Provides-Extra', + name.lower().replace("-", "_"): name for name in _ALL_FIELDS } +_FIELD2ATTR = {field: attr for attr, field in _ATTR2FIELD.items()} _PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') _VERSIONS_FIELDS = ('Requires-Python',) @@ -262,7 +238,7 @@ def _get_name_and_version(name, version, for_filename=False): class LegacyMetadata(object): """The legacy metadata of a release. - Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + Supports versions 1.0, 1.1, 1.2, 2.0 and 1.3/2.1 (auto-detected). You can instantiate the class with one of these arguments (or none): - *path*, the path to a metadata file - *fileobj* give a file-like object with metadata as content @@ -381,6 +357,11 @@ class LegacyMetadata(object): value = msg[field] if value is not None and value != 'UNKNOWN': self.set(field, value) + + # PEP 566 specifies that the body be used for the description, if + # available + body = msg.get_payload() + self["Description"] = body if body else self["Description"] # logger.debug('Attempting to set metadata for %s', self) # self.set_metadata_version() @@ -567,57 +548,21 @@ class LegacyMetadata(object): Field names will be converted to use the underscore-lowercase style instead of hyphen-mixed case (i.e. home_page instead of Home-page). + This is as per https://www.python.org/dev/peps/pep-0566/#id17. """ self.set_metadata_version() - mapping_1_0 = ( - ('metadata_version', 'Metadata-Version'), - ('name', 'Name'), - ('version', 'Version'), - ('summary', 'Summary'), - ('home_page', 'Home-page'), - ('author', 'Author'), - ('author_email', 'Author-email'), - ('license', 'License'), - ('description', 'Description'), - ('keywords', 'Keywords'), - ('platform', 'Platform'), - ('classifiers', 'Classifier'), - ('download_url', 'Download-URL'), - ) + fields = _version2fieldlist(self['Metadata-Version']) data = {} - for key, field_name in mapping_1_0: + + for field_name in fields: if not skip_missing or field_name in self._fields: - data[key] = self[field_name] - - if self['Metadata-Version'] == '1.2': - mapping_1_2 = ( - ('requires_dist', 'Requires-Dist'), - ('requires_python', 'Requires-Python'), - ('requires_external', 'Requires-External'), - ('provides_dist', 'Provides-Dist'), - ('obsoletes_dist', 'Obsoletes-Dist'), - ('project_url', 'Project-URL'), - ('maintainer', 'Maintainer'), - ('maintainer_email', 'Maintainer-email'), - ) - for key, field_name in mapping_1_2: - if not skip_missing or field_name in self._fields: - if key != 'project_url': - data[key] = self[field_name] - else: - data[key] = [','.join(u) for u in self[field_name]] - - elif self['Metadata-Version'] == '1.1': - mapping_1_1 = ( - ('provides', 'Provides'), - ('requires', 'Requires'), - ('obsoletes', 'Obsoletes'), - ) - for key, field_name in mapping_1_1: - if not skip_missing or field_name in self._fields: + key = _FIELD2ATTR[field_name] + if key != 'project_url': data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] return data @@ -1003,10 +948,14 @@ class Metadata(object): LEGACY_MAPPING = { 'name': 'Name', 'version': 'Version', - 'license': 'License', + ('extensions', 'python.details', 'license'): 'License', 'summary': 'Summary', 'description': 'Description', - 'classifiers': 'Classifier', + ('extensions', 'python.project', 'project_urls', 'Home'): 'Home-page', + ('extensions', 'python.project', 'contacts', 0, 'name'): 'Author', + ('extensions', 'python.project', 'contacts', 0, 'email'): 'Author-email', + 'source_url': 'Download-URL', + ('extensions', 'python.details', 'classifiers'): 'Classifier', } def _to_legacy(self): @@ -1034,16 +983,29 @@ class Metadata(object): assert self._data and not self._legacy result = LegacyMetadata() nmd = self._data + # import pdb; pdb.set_trace() for nk, ok in self.LEGACY_MAPPING.items(): - if nk in nmd: - result[ok] = nmd[nk] + if not isinstance(nk, tuple): + if nk in nmd: + result[ok] = nmd[nk] + else: + d = nmd + found = True + for k in nk: + try: + d = d[k] + except (KeyError, IndexError): + found = False + break + if found: + result[ok] = d r1 = process_entries(self.run_requires + self.meta_requires) r2 = process_entries(self.build_requires + self.dev_requires) if self.extras: result['Provides-Extra'] = sorted(self.extras) result['Requires-Dist'] = sorted(r1) result['Setup-Requires-Dist'] = sorted(r2) - # TODO: other fields such as contacts + # TODO: any other fields wanted return result def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/resources.py b/venv/Lib/site-packages/pip/_vendor/distlib/resources.py index 18840167a9e3ea1cf443b0803230e41481df41d2..fef52aa103ea369c96567b9af2a5a0ba14db5cb9 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/resources.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/resources.py @@ -11,13 +11,12 @@ import io import logging import os import pkgutil -import shutil import sys import types import zipimport from . import DistlibException -from .util import cached_property, get_cache_base, path_to_cache_dir, Cache +from .util import cached_property, get_cache_base, Cache logger = logging.getLogger(__name__) @@ -283,6 +282,7 @@ class ZipResourceFinder(ResourceFinder): result = False return result + _finder_registry = { type(None): ResourceFinder, zipimport.zipimporter: ZipResourceFinder @@ -296,6 +296,8 @@ try: import _frozen_importlib as _fi _finder_registry[_fi.SourceFileLoader] = ResourceFinder _finder_registry[_fi.FileFinder] = ResourceFinder + # See issue #146 + _finder_registry[_fi.SourcelessFileLoader] = ResourceFinder del _fi except (ImportError, AttributeError): pass @@ -304,6 +306,7 @@ except (ImportError, AttributeError): def register_finder(loader, finder_maker): _finder_registry[type(loader)] = finder_maker + _finder_cache = {} diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py b/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py index 51859741867d195f91d8ba84b87baa37d0c4a8f0..913912c7b8e0c2dcbf142f81991dfec0d26f4f41 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/scripts.py @@ -14,7 +14,7 @@ import sys from .compat import sysconfig, detect_encoding, ZipFile from .resources import finder from .util import (FileOperator, get_export_entry, convert_path, - get_executable, in_venv) + get_executable, get_platform, in_venv) logger = logging.getLogger(__name__) @@ -48,7 +48,7 @@ if __name__ == '__main__': ''' -def _enquote_executable(executable): +def enquote_executable(executable): if ' ' in executable: # make sure we quote only the executable in case of env # for example /usr/bin/env "/dir with spaces/bin/jython" @@ -63,6 +63,8 @@ def _enquote_executable(executable): executable = '"%s"' % executable return executable +# Keep the old name around (for now), as there is at least one project using it! +_enquote_executable = enquote_executable class ScriptMaker(object): """ @@ -88,6 +90,7 @@ class ScriptMaker(object): self._is_nt = os.name == 'nt' or ( os.name == 'java' and os._name == 'nt') + self.version_info = sys.version_info def _get_alternate_executable(self, executable, options): if options.get('gui', False) and self._is_nt: # pragma: no cover @@ -167,6 +170,11 @@ class ScriptMaker(object): sysconfig.get_config_var('BINDIR'), 'python%s%s' % (sysconfig.get_config_var('VERSION'), sysconfig.get_config_var('EXE'))) + if not os.path.isfile(executable): + # for Python builds from source on Windows, no Python executables with + # a version suffix are created, so we use python.exe + executable = os.path.join(sysconfig.get_config_var('BINDIR'), + 'python%s' % (sysconfig.get_config_var('EXE'))) if options: executable = self._get_alternate_executable(executable, options) @@ -185,7 +193,7 @@ class ScriptMaker(object): # If the user didn't specify an executable, it may be necessary to # cater for executable paths with spaces (not uncommon on Windows) if enquote: - executable = _enquote_executable(executable) + executable = enquote_executable(executable) # Issue #51: don't use fsencode, since we later try to # check that the shebang is decodable using utf-8. executable = executable.encode('utf-8') @@ -279,6 +287,19 @@ class ScriptMaker(object): self._fileop.set_executable_mode([outname]) filenames.append(outname) + variant_separator = '-' + + def get_script_filenames(self, name): + result = set() + if '' in self.variants: + result.add(name) + if 'X' in self.variants: + result.add('%s%s' % (name, self.version_info[0])) + if 'X.Y' in self.variants: + result.add('%s%s%s.%s' % (name, self.variant_separator, + self.version_info[0], self.version_info[1])) + return result + def _make_script(self, entry, filenames, options=None): post_interp = b'' if options: @@ -288,15 +309,7 @@ class ScriptMaker(object): post_interp = args.encode('utf-8') shebang = self._get_shebang('utf-8', post_interp, options=options) script = self._get_script_text(entry).encode('utf-8') - name = entry.name - scriptnames = set() - if '' in self.variants: - scriptnames.add(name) - if 'X' in self.variants: - scriptnames.add('%s%s' % (name, sys.version_info[0])) - if 'X.Y' in self.variants: - scriptnames.add('%s-%s.%s' % (name, sys.version_info[0], - sys.version_info[1])) + scriptnames = self.get_script_filenames(entry.name) if options and options.get('gui', False): ext = 'pyw' else: @@ -323,8 +336,7 @@ class ScriptMaker(object): else: first_line = f.readline() if not first_line: # pragma: no cover - logger.warning('%s: %s is an empty file (skipping)', - self.get_command_name(), script) + logger.warning('%s is an empty file (skipping)', script) return match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) @@ -372,7 +384,8 @@ class ScriptMaker(object): bits = '64' else: bits = '32' - name = '%s%s.exe' % (kind, bits) + platform_suffix = '-arm' if get_platform() == 'win-arm64' else '' + name = '%s%s%s.exe' % (kind, bits, platform_suffix) # Issue 31: don't hardcode an absolute package name, but # determine it relative to the current package distlib_package = __name__.rsplit('.', 1)[0] diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/util.py b/venv/Lib/site-packages/pip/_vendor/distlib/util.py index 01324eae462f3384358a77e649b9c59a26cdfa9e..80bfc864bcb4aec4e40127cf5c336432f0378f09 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/util.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/util.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2012-2017 The Python Software Foundation. +# Copyright (C) 2012-2021 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. # import codecs @@ -215,6 +215,10 @@ def parse_requirement(req): if not ver_remaining or ver_remaining[0] != ',': break ver_remaining = ver_remaining[1:].lstrip() + # Some packages have a trailing comma which would break things + # See issue #148 + if not ver_remaining: + break m = COMPARE_OP.match(ver_remaining) if not m: raise SyntaxError('invalid constraint: %s' % ver_remaining) @@ -309,7 +313,9 @@ def get_executable(): # else: # result = sys.executable # return result - result = os.path.normcase(sys.executable) + # Avoid normcasing: see issue #143 + # result = os.path.normcase(sys.executable) + result = sys.executable if not isinstance(result, text_type): result = fsdecode(result) return result @@ -1570,7 +1576,8 @@ class ServerProxy(xmlrpclib.ServerProxy): # The above classes only come into play if a timeout # is specified if timeout is not None: - scheme, _ = splittype(uri) + # scheme = splittype(uri) # deprecated as of Python 3.8 + scheme = urlparse(uri)[0] use_datetime = kwargs.get('use_datetime', 0) if scheme == 'https': tcls = SafeTransport @@ -1759,3 +1766,204 @@ def normalize_name(name): """Normalize a python package name a la PEP 503""" # https://www.python.org/dev/peps/pep-0503/#normalized-names return re.sub('[-_.]+', '-', name).lower() + +# def _get_pypirc_command(): + # """ + # Get the distutils command for interacting with PyPI configurations. + # :return: the command. + # """ + # from distutils.core import Distribution + # from distutils.config import PyPIRCCommand + # d = Distribution() + # return PyPIRCCommand(d) + +class PyPIRCFile(object): + + DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' + DEFAULT_REALM = 'pypi' + + def __init__(self, fn=None, url=None): + if fn is None: + fn = os.path.join(os.path.expanduser('~'), '.pypirc') + self.filename = fn + self.url = url + + def read(self): + result = {} + + if os.path.exists(self.filename): + repository = self.url or self.DEFAULT_REPOSITORY + + config = configparser.RawConfigParser() + config.read(self.filename) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + for server in _servers: + result = {'server': server} + result['username'] = config.get(server, 'username') + + # optional params + for key, default in (('repository', self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM), + ('password', None)): + if config.has_option(server, key): + result[key] = config.get(server, key) + else: + result[key] = default + + # work around people having "repository" for the "pypi" + # section of their config set to the HTTP (rather than + # HTTPS) URL + if (server == 'pypi' and + repository in (self.DEFAULT_REPOSITORY, 'pypi')): + result['repository'] = self.DEFAULT_REPOSITORY + elif (result['server'] != repository and + result['repository'] != repository): + result = {} + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = self.DEFAULT_REPOSITORY + result = { + 'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM + } + return result + + def update(self, username, password): + # import pdb; pdb.set_trace() + config = configparser.RawConfigParser() + fn = self.filename + config.read(fn) + if not config.has_section('pypi'): + config.add_section('pypi') + config.set('pypi', 'username', username) + config.set('pypi', 'password', password) + with open(fn, 'w') as f: + config.write(f) + +def _load_pypirc(index): + """ + Read the PyPI access configuration as supported by distutils. + """ + return PyPIRCFile(url=index.url).read() + +def _store_pypirc(index): + PyPIRCFile().update(index.username, index.password) + +# +# get_platform()/get_host_platform() copied from Python 3.10.a0 source, with some minor +# tweaks +# + +def get_host_platform(): + """Return a string that identifies the current platform. This is used mainly to + distinguish platform-specific build directories and platform-specific built + distributions. Typically includes the OS name and version and the + architecture (as supplied by 'os.uname()'), although the exact information + included depends on the OS; eg. on Linux, the kernel version isn't + particularly important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + + """ + if os.name == 'nt': + if 'amd64' in sys.version.lower(): + return 'win-amd64' + if '(arm)' in sys.version.lower(): + return 'win-arm32' + if '(arm64)' in sys.version.lower(): + return 'win-arm64' + return sys.platform + + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + + if os.name != 'posix' or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + + (osname, host, release, version, machine) = os.uname() + + # Convert the OS name to lowercase, remove '/' characters, and translate + # spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_').replace('/', '-') + + if osname[:5] == 'linux': + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + + elif osname[:5] == 'sunos': + if release[0] >= '5': # SunOS 5 == Solaris 2 + osname = 'solaris' + release = '%d.%s' % (int(release[0]) - 3, release[2:]) + # We can't use 'platform.architecture()[0]' because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:'32bit', 9223372036854775807:'64bit'} + machine += '.%s' % bitness[sys.maxsize] + # fall through to standard osname-release-machine representation + elif osname[:3] == 'aix': + from _aix_support import aix_platform + return aix_platform() + elif osname[:6] == 'cygwin': + osname = 'cygwin' + rel_re = re.compile (r'[\d.]+', re.ASCII) + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == 'darwin': + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) + + return '%s-%s-%s' % (osname, release, machine) + + +_TARGET_TO_PLAT = { + 'x86' : 'win32', + 'x64' : 'win-amd64', + 'arm' : 'win-arm32', +} + + +def get_platform(): + if os.name != 'nt': + return get_host_platform() + cross_compilation_target = os.environ.get('VSCMD_ARG_TGT_ARCH') + if cross_compilation_target not in _TARGET_TO_PLAT: + return get_host_platform() + return _TARGET_TO_PLAT[cross_compilation_target] diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/version.py b/venv/Lib/site-packages/pip/_vendor/distlib/version.py index 3eebe18ee849dcafe39246ce2f4cabbe8e5a2b33..c7c8bb6ff4f8ed84e466a66cac6b953b901626ea 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/version.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/version.py @@ -194,7 +194,7 @@ def _pep_440_key(s): if not groups[0]: epoch = 0 else: - epoch = int(groups[0]) + epoch = int(groups[0][:-1]) pre = groups[4:6] post = groups[7:9] dev = groups[10:12] @@ -710,6 +710,9 @@ class VersionScheme(object): """ Used for processing some metadata fields """ + # See issue #140. Be tolerant of a single trailing comma. + if s.endswith(','): + s = s[:-1] return self.is_valid_matcher('dummy_name (%s)' % s) def suggest(self, s): diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py b/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py index bd179383ac98bb0b7ff3571666837e1f4a789c72..48abfde5b5285cb19c50757796dfd162e59c0d91 100644 --- a/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py +++ b/venv/Lib/site-packages/pip/_vendor/distlib/wheel.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2013-2017 Vinay Sajip. +# Copyright (C) 2013-2020 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # @@ -9,7 +9,6 @@ from __future__ import unicode_literals import base64 import codecs import datetime -import distutils.util from email import message_from_file import hashlib import imp @@ -26,9 +25,11 @@ import zipfile from . import __version__, DistlibException from .compat import sysconfig, ZipFile, fsdecode, text_type, filter from .database import InstalledDistribution -from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME +from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME) from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, - cached_property, get_cache_base, read_exports, tempdir) + cached_property, get_cache_base, read_exports, tempdir, + get_platform) from .version import NormalizedVersion, UnsupportedVersionError logger = logging.getLogger(__name__) @@ -50,11 +51,11 @@ if not VER_SUFFIX: # pragma: no cover PYVER = 'py' + VER_SUFFIX IMPVER = IMP_PREFIX + VER_SUFFIX -ARCH = distutils.util.get_platform().replace('-', '_').replace('.', '_') +ARCH = get_platform().replace('-', '_').replace('.', '_') ABI = sysconfig.get_config_var('SOABI') if ABI and ABI.startswith('cpython-'): - ABI = ABI.replace('cpython-', 'cp') + ABI = ABI.replace('cpython-', 'cp').split('-')[0] else: def _derive_abi(): parts = ['cp', VER_SUFFIX] @@ -221,10 +222,12 @@ class Wheel(object): wheel_metadata = self.get_wheel_metadata(zf) wv = wheel_metadata['Wheel-Version'].split('.', 1) file_version = tuple([int(i) for i in wv]) - if file_version < (1, 1): - fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, 'METADATA'] - else: - fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + # if file_version < (1, 1): + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, + # LEGACY_METADATA_FILENAME] + # else: + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME] result = None for fn in fns: try: @@ -299,10 +302,9 @@ class Wheel(object): return hash_kind, result def write_record(self, records, record_path, base): - records = list(records) # make a copy for sorting + records = list(records) # make a copy, as mutated p = to_posix(os.path.relpath(record_path, base)) records.append((p, '', '')) - records.sort() with CSVWriter(record_path) as writer: for row in records: writer.writerow(row) @@ -425,6 +427,18 @@ class Wheel(object): ap = to_posix(os.path.join(info_dir, 'WHEEL')) archive_paths.append((ap, p)) + # sort the entries by archive path. Not needed by any spec, but it + # keeps the archive listing and RECORD tidier than they would otherwise + # be. Use the number of path segments to keep directory entries together, + # and keep the dist-info stuff at the end. + def sorter(t): + ap = t[0] + n = ap.count('/') + if '.dist-info' in ap: + n += 10000 + return (n, ap) + archive_paths = sorted(archive_paths, key=sorter) + # Now, at last, RECORD. # Paths in here are archive paths - nothing else makes sense. self.write_records((distinfo, info_dir), libdir, archive_paths) @@ -476,7 +490,7 @@ class Wheel(object): data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver - metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') record_name = posixpath.join(info_dir, 'RECORD') @@ -562,6 +576,13 @@ class Wheel(object): if not is_script: with zf.open(arcname) as bf: fileop.copy_stream(bf, outfile) + # Issue #147: permission bits aren't preserved. Using + # zf.extract(zinfo, libdir) should have worked, but didn't, + # see https://www.thetopsites.net/article/53834422.shtml + # So ... manually preserve permission bits as given in zinfo + if os.name == 'posix': + # just set the normal permission bits + os.chmod(outfile, (zinfo.external_attr >> 16) & 0x1FF) outfiles.append(outfile) # Double check the digest of the written file if not dry_run and row[1]: @@ -619,7 +640,7 @@ class Wheel(object): for v in epdata[k].values(): s = '%s:%s' % (v.prefix, v.suffix) if v.flags: - s += ' %s' % v.flags + s += ' [%s]' % ','.join(v.flags) d[v.name] = s except Exception: logger.warning('Unable to read legacy script ' @@ -773,7 +794,7 @@ class Wheel(object): data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver - metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') record_name = posixpath.join(info_dir, 'RECORD') @@ -842,7 +863,7 @@ class Wheel(object): def get_version(path_map, info_dir): version = path = None - key = '%s/%s' % (info_dir, METADATA_FILENAME) + key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME) if key not in path_map: key = '%s/PKG-INFO' % info_dir if key in path_map: @@ -868,7 +889,7 @@ class Wheel(object): if updated: md = Metadata(path=path) md.version = updated - legacy = not path.endswith(METADATA_FILENAME) + legacy = path.endswith(LEGACY_METADATA_FILENAME) md.write(path=path, legacy=legacy) logger.debug('Version updated from %r to %r', version, updated) @@ -924,6 +945,16 @@ class Wheel(object): shutil.copyfile(newpath, pathname) return modified +def _get_glibc_version(): + import platform + ver = platform.libc_ver() + result = [] + if ver[0] == 'glibc': + for s in ver[1].split('.'): + result.append(int(s) if s.isdigit() else 0) + result = tuple(result) + return result + def compatible_tags(): """ Return (pyver, abi, arch) tuples compatible with this Python. @@ -971,6 +1002,23 @@ def compatible_tags(): for abi in abis: for arch in arches: result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) + # manylinux + if abi != 'none' and sys.platform.startswith('linux'): + arch = arch.replace('linux_', '') + parts = _get_glibc_version() + if len(parts) == 2: + if parts >= (2, 5): + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux1_%s' % arch)) + if parts >= (2, 12): + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux2010_%s' % arch)) + if parts >= (2, 17): + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux2014_%s' % arch)) + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux_%s_%s_%s' % (parts[0], parts[1], + arch))) # where no ABI / arch dependency, but IMP_PREFIX dependency for i, version in enumerate(versions): @@ -983,6 +1031,7 @@ def compatible_tags(): result.append((''.join(('py', version)), 'none', 'any')) if i == 0: result.append((''.join(('py', version[0])), 'none', 'any')) + return set(result) diff --git a/venv/Lib/site-packages/pip/_vendor/distro.py b/venv/Lib/site-packages/pip/_vendor/distro.py index 0611b62a3a80fc60b7c7cc4bae917245e0a82b7e..7892741347d632d48f3fbe11b417c4705f9968f3 100644 --- a/venv/Lib/site-packages/pip/_vendor/distro.py +++ b/venv/Lib/site-packages/pip/_vendor/distro.py @@ -20,26 +20,61 @@ machine-readable distro ID, or version information. It is the recommended replacement for Python's original :py:func:`platform.linux_distribution` function, but it provides much more functionality. An alternative implementation became necessary because Python -3.5 deprecated this function, and Python 3.8 will remove it altogether. -Its predecessor function :py:func:`platform.dist` was already -deprecated since Python 2.6 and will also be removed in Python 3.8. -Still, there are many cases in which access to OS distribution information -is needed. See `Python issue 1322 `_ for -more information. +3.5 deprecated this function, and Python 3.8 removed it altogether. Its +predecessor function :py:func:`platform.dist` was already deprecated since +Python 2.6 and removed in Python 3.8. Still, there are many cases in which +access to OS distribution information is needed. See `Python issue 1322 +`_ for more information. """ +import argparse +import json +import logging import os import re -import sys -import json import shlex -import logging -import argparse import subprocess +import sys +import warnings + +__version__ = "1.6.0" + +# Use `if False` to avoid an ImportError on Python 2. After dropping Python 2 +# support, can use typing.TYPE_CHECKING instead. See: +# https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING +if False: # pragma: nocover + from typing import ( + Any, + Callable, + Dict, + Iterable, + Optional, + Sequence, + TextIO, + Tuple, + Type, + TypedDict, + Union, + ) + + VersionDict = TypedDict( + "VersionDict", {"major": str, "minor": str, "build_number": str} + ) + InfoDict = TypedDict( + "InfoDict", + { + "id": str, + "version": str, + "version_parts": VersionDict, + "like": str, + "codename": str, + }, + ) -_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') -_OS_RELEASE_BASENAME = 'os-release' +_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") +_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") +_OS_RELEASE_BASENAME = "os-release" #: Translation table for normalizing the "ID" attribute defined in os-release #: files, for use by the :func:`distro.id` method. @@ -49,7 +84,7 @@ _OS_RELEASE_BASENAME = 'os-release' #: #: * Value: Normalized value. NORMALIZED_OS_ID = { - 'ol': 'oracle', # Oracle Linux + "ol": "oracle", # Oracle Linux } #: Translation table for normalizing the "Distributor ID" attribute returned by @@ -60,11 +95,11 @@ NORMALIZED_OS_ID = { #: #: * Value: Normalized value. NORMALIZED_LSB_ID = { - 'enterpriseenterpriseas': 'oracle', # Oracle Enterprise Linux 4 - 'enterpriseenterpriseserver': 'oracle', # Oracle Linux 5 - 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation - 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server - 'redhatenterprisecomputenode': 'rhel', # RHEL 6 ComputeNode + "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 + "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 + "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation + "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server + "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode } #: Translation table for normalizing the distro ID derived from the file name @@ -75,30 +110,39 @@ NORMALIZED_LSB_ID = { #: #: * Value: Normalized value. NORMALIZED_DISTRO_ID = { - 'redhat': 'rhel', # RHEL 6.x, 7.x + "redhat": "rhel", # RHEL 6.x, 7.x } # Pattern for content of distro release file (reversed) _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( - r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') + r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" +) # Pattern for base file name of distro release file -_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( - r'(\w+)[-_](release|version)$') +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") # Base file names to be ignored when searching for distro release file _DISTRO_RELEASE_IGNORE_BASENAMES = ( - 'debian_version', - 'lsb-release', - 'oem-release', + "debian_version", + "lsb-release", + "oem-release", _OS_RELEASE_BASENAME, - 'system-release', - 'plesk-release', + "system-release", + "plesk-release", + "iredmail-release", ) def linux_distribution(full_distribution_name=True): + # type: (bool) -> Tuple[str, str, str] """ + .. deprecated:: 1.6.0 + + :func:`distro.linux_distribution()` is deprecated. It should only be + used as a compatibility shim with Python's + :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, + :func:`distro.version` and :func:`distro.name` instead. + Return information about the current OS distribution as a tuple ``(id_name, version, codename)`` with items as follows: @@ -122,10 +166,18 @@ def linux_distribution(full_distribution_name=True): method normalizes the distro ID string to a reliable machine-readable value for a number of popular OS distributions. """ + warnings.warn( + "distro.linux_distribution() is deprecated. It should only be used as a " + "compatibility shim with Python's platform.linux_distribution(). Please use " + "distro.id(), distro.version() and distro.name() instead.", + DeprecationWarning, + stacklevel=2, + ) return _distro.linux_distribution(full_distribution_name) def id(): + # type: () -> str """ Return the distro ID of the current distribution, as a machine-readable string. @@ -205,6 +257,7 @@ def id(): def name(pretty=False): + # type: (bool) -> str """ Return the name of the current OS distribution, as a human-readable string. @@ -244,6 +297,7 @@ def name(pretty=False): def version(pretty=False, best=False): + # type: (bool, bool) -> str """ Return the version of the current OS distribution, as a human-readable string. @@ -288,6 +342,7 @@ def version(pretty=False, best=False): def version_parts(best=False): + # type: (bool) -> Tuple[str, str, str] """ Return the version of the current OS distribution as a tuple ``(major, minor, build_number)`` with items as follows: @@ -305,6 +360,7 @@ def version_parts(best=False): def major_version(best=False): + # type: (bool) -> str """ Return the major version of the current OS distribution, as a string, if provided. @@ -318,6 +374,7 @@ def major_version(best=False): def minor_version(best=False): + # type: (bool) -> str """ Return the minor version of the current OS distribution, as a string, if provided. @@ -331,6 +388,7 @@ def minor_version(best=False): def build_number(best=False): + # type: (bool) -> str """ Return the build number of the current OS distribution, as a string, if provided. @@ -344,6 +402,7 @@ def build_number(best=False): def like(): + # type: () -> str """ Return a space-separated list of distro IDs of distributions that are closely related to the current OS distribution in regards to packaging @@ -361,6 +420,7 @@ def like(): def codename(): + # type: () -> str """ Return the codename for the release of the current OS distribution, as a string. @@ -385,6 +445,7 @@ def codename(): def info(pretty=False, best=False): + # type: (bool, bool) -> InfoDict """ Return certain machine-readable information items about the current OS distribution in a dictionary, as shown in the following example: @@ -429,6 +490,7 @@ def info(pretty=False, best=False): def os_release_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the current OS distribution. @@ -439,6 +501,7 @@ def os_release_info(): def lsb_release_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the current OS distribution. @@ -450,6 +513,7 @@ def lsb_release_info(): def distro_release_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. @@ -460,6 +524,7 @@ def distro_release_info(): def uname_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. @@ -468,6 +533,7 @@ def uname_info(): def os_release_attr(attribute): + # type: (str) -> str """ Return a single named information item from the os-release file data source of the current OS distribution. @@ -487,6 +553,7 @@ def os_release_attr(attribute): def lsb_release_attr(attribute): + # type: (str) -> str """ Return a single named information item from the lsb_release command output data source of the current OS distribution. @@ -507,6 +574,7 @@ def lsb_release_attr(attribute): def distro_release_attr(attribute): + # type: (str) -> str """ Return a single named information item from the distro release file data source of the current OS distribution. @@ -526,6 +594,7 @@ def distro_release_attr(attribute): def uname_attr(attribute): + # type: (str) -> str """ Return a single named information item from the distro release file data source of the current OS distribution. @@ -542,19 +611,26 @@ def uname_attr(attribute): return _distro.uname_attr(attribute) -class cached_property(object): - """A version of @property which caches the value. On access, it calls the - underlying function and sets the value in `__dict__` so future accesses - will not re-call the property. - """ - def __init__(self, f): - self._fname = f.__name__ - self._f = f +try: + from functools import cached_property +except ImportError: + # Python < 3.8 + class cached_property(object): # type: ignore + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + + def __init__(self, f): + # type: (Callable[[Any], Any]) -> None + self._fname = f.__name__ + self._f = f - def __get__(self, obj, owner): - assert obj is not None, 'call {} on an instance'.format(self._fname) - ret = obj.__dict__[self._fname] = self._f(obj) - return ret + def __get__(self, obj, owner): + # type: (Any, Type[Any]) -> Any + assert obj is not None, "call {} on an instance".format(self._fname) + ret = obj.__dict__[self._fname] = self._f(obj) + return ret class LinuxDistribution(object): @@ -575,11 +651,15 @@ class LinuxDistribution(object): lsb_release command. """ - def __init__(self, - include_lsb=True, - os_release_file='', - distro_release_file='', - include_uname=True): + def __init__( + self, + include_lsb=True, + os_release_file="", + distro_release_file="", + include_uname=True, + root_dir=None, + ): + # type: (bool, str, str, bool, Optional[str]) -> None """ The initialization method of this class gathers information from the available data sources, and stores that in private instance attributes. @@ -618,6 +698,9 @@ class LinuxDistribution(object): the program execution path the data source for the uname command will be empty. + * ``root_dir`` (string): The absolute path to the root directory to use + to find distro-related information files. + Public instance attributes: * ``os_release_file`` (string): The path name of the @@ -647,28 +730,50 @@ class LinuxDistribution(object): * :py:exc:`UnicodeError`: A data source has unexpected characters or uses an unexpected encoding. """ - self.os_release_file = os_release_file or \ - os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) - self.distro_release_file = distro_release_file or '' # updated later + self.root_dir = root_dir + self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR + self.usr_lib_dir = ( + os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR + ) + + if os_release_file: + self.os_release_file = os_release_file + else: + etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) + usr_lib_os_release_file = os.path.join( + self.usr_lib_dir, _OS_RELEASE_BASENAME + ) + + # NOTE: The idea is to respect order **and** have it set + # at all times for API backwards compatibility. + if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( + usr_lib_os_release_file + ): + self.os_release_file = etc_dir_os_release_file + else: + self.os_release_file = usr_lib_os_release_file + + self.distro_release_file = distro_release_file or "" # updated later self.include_lsb = include_lsb self.include_uname = include_uname def __repr__(self): - """Return repr of all info - """ - return \ - "LinuxDistribution(" \ - "os_release_file={self.os_release_file!r}, " \ - "distro_release_file={self.distro_release_file!r}, " \ - "include_lsb={self.include_lsb!r}, " \ - "include_uname={self.include_uname!r}, " \ - "_os_release_info={self._os_release_info!r}, " \ - "_lsb_release_info={self._lsb_release_info!r}, " \ - "_distro_release_info={self._distro_release_info!r}, " \ - "_uname_info={self._uname_info!r})".format( - self=self) + # type: () -> str + """Return repr of all info""" + return ( + "LinuxDistribution(" + "os_release_file={self.os_release_file!r}, " + "distro_release_file={self.distro_release_file!r}, " + "include_lsb={self.include_lsb!r}, " + "include_uname={self.include_uname!r}, " + "_os_release_info={self._os_release_info!r}, " + "_lsb_release_info={self._lsb_release_info!r}, " + "_distro_release_info={self._distro_release_info!r}, " + "_uname_info={self._uname_info!r})".format(self=self) + ) def linux_distribution(self, full_distribution_name=True): + # type: (bool) -> Tuple[str, str, str] """ Return information about the OS distribution that is compatible with Python's :func:`platform.linux_distribution`, supporting a subset @@ -679,92 +784,102 @@ class LinuxDistribution(object): return ( self.name() if full_distribution_name else self.id(), self.version(), - self.codename() + self.codename(), ) def id(self): + # type: () -> str """Return the distro ID of the OS distribution, as a string. For details, see :func:`distro.id`. """ + def normalize(distro_id, table): - distro_id = distro_id.lower().replace(' ', '_') + # type: (str, Dict[str, str]) -> str + distro_id = distro_id.lower().replace(" ", "_") return table.get(distro_id, distro_id) - distro_id = self.os_release_attr('id') + distro_id = self.os_release_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_OS_ID) - distro_id = self.lsb_release_attr('distributor_id') + distro_id = self.lsb_release_attr("distributor_id") if distro_id: return normalize(distro_id, NORMALIZED_LSB_ID) - distro_id = self.distro_release_attr('id') + distro_id = self.distro_release_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) - distro_id = self.uname_attr('id') + distro_id = self.uname_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) - return '' + return "" def name(self, pretty=False): + # type: (bool) -> str """ Return the name of the OS distribution, as a string. For details, see :func:`distro.name`. """ - name = self.os_release_attr('name') \ - or self.lsb_release_attr('distributor_id') \ - or self.distro_release_attr('name') \ - or self.uname_attr('name') + name = ( + self.os_release_attr("name") + or self.lsb_release_attr("distributor_id") + or self.distro_release_attr("name") + or self.uname_attr("name") + ) if pretty: - name = self.os_release_attr('pretty_name') \ - or self.lsb_release_attr('description') + name = self.os_release_attr("pretty_name") or self.lsb_release_attr( + "description" + ) if not name: - name = self.distro_release_attr('name') \ - or self.uname_attr('name') + name = self.distro_release_attr("name") or self.uname_attr("name") version = self.version(pretty=True) if version: - name = name + ' ' + version - return name or '' + name = name + " " + version + return name or "" def version(self, pretty=False, best=False): + # type: (bool, bool) -> str """ Return the version of the OS distribution, as a string. For details, see :func:`distro.version`. """ versions = [ - self.os_release_attr('version_id'), - self.lsb_release_attr('release'), - self.distro_release_attr('version_id'), - self._parse_distro_release_content( - self.os_release_attr('pretty_name')).get('version_id', ''), + self.os_release_attr("version_id"), + self.lsb_release_attr("release"), + self.distro_release_attr("version_id"), + self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( + "version_id", "" + ), self._parse_distro_release_content( - self.lsb_release_attr('description')).get('version_id', ''), - self.uname_attr('release') + self.lsb_release_attr("description") + ).get("version_id", ""), + self.uname_attr("release"), ] - version = '' + version = "" if best: # This algorithm uses the last version in priority order that has # the best precision. If the versions are not in conflict, that # does not matter; otherwise, using the last one instead of the # first one might be considered a surprise. for v in versions: - if v.count(".") > version.count(".") or version == '': + if v.count(".") > version.count(".") or version == "": version = v else: for v in versions: - if v != '': + if v != "": version = v break if pretty and version and self.codename(): - version = '{0} ({1})'.format(version, self.codename()) + version = "{0} ({1})".format(version, self.codename()) return version def version_parts(self, best=False): + # type: (bool) -> Tuple[str, str, str] """ Return the version of the OS distribution, as a tuple of version numbers. @@ -773,14 +888,15 @@ class LinuxDistribution(object): """ version_str = self.version(best=best) if version_str: - version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') + version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") matches = version_regex.match(version_str) if matches: major, minor, build_number = matches.groups() - return major, minor or '', build_number or '' - return '', '', '' + return major, minor or "", build_number or "" + return "", "", "" def major_version(self, best=False): + # type: (bool) -> str """ Return the major version number of the current distribution. @@ -789,6 +905,7 @@ class LinuxDistribution(object): return self.version_parts(best)[0] def minor_version(self, best=False): + # type: (bool) -> str """ Return the minor version number of the current distribution. @@ -797,6 +914,7 @@ class LinuxDistribution(object): return self.version_parts(best)[1] def build_number(self, best=False): + # type: (bool) -> str """ Return the build number of the current distribution. @@ -805,14 +923,16 @@ class LinuxDistribution(object): return self.version_parts(best)[2] def like(self): + # type: () -> str """ Return the IDs of distributions that are like the OS distribution. For details, see :func:`distro.like`. """ - return self.os_release_attr('id_like') or '' + return self.os_release_attr("id_like") or "" def codename(self): + # type: () -> str """ Return the codename of the OS distribution. @@ -821,13 +941,16 @@ class LinuxDistribution(object): try: # Handle os_release specially since distros might purposefully set # this to empty string to have no codename - return self._os_release_info['codename'] + return self._os_release_info["codename"] except KeyError: - return self.lsb_release_attr('codename') \ - or self.distro_release_attr('codename') \ - or '' + return ( + self.lsb_release_attr("codename") + or self.distro_release_attr("codename") + or "" + ) def info(self, pretty=False, best=False): + # type: (bool, bool) -> InfoDict """ Return certain machine-readable information about the OS distribution. @@ -840,13 +963,14 @@ class LinuxDistribution(object): version_parts=dict( major=self.major_version(best), minor=self.minor_version(best), - build_number=self.build_number(best) + build_number=self.build_number(best), ), like=self.like(), codename=self.codename(), ) def os_release_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the OS distribution. @@ -856,6 +980,7 @@ class LinuxDistribution(object): return self._os_release_info def lsb_release_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the OS @@ -866,6 +991,7 @@ class LinuxDistribution(object): return self._lsb_release_info def distro_release_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the OS @@ -876,6 +1002,7 @@ class LinuxDistribution(object): return self._distro_release_info def uname_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the uname command data source of the OS distribution. @@ -885,43 +1012,48 @@ class LinuxDistribution(object): return self._uname_info def os_release_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the os-release file data source of the OS distribution. For details, see :func:`distro.os_release_attr`. """ - return self._os_release_info.get(attribute, '') + return self._os_release_info.get(attribute, "") def lsb_release_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the lsb_release command output data source of the OS distribution. For details, see :func:`distro.lsb_release_attr`. """ - return self._lsb_release_info.get(attribute, '') + return self._lsb_release_info.get(attribute, "") def distro_release_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the distro release file data source of the OS distribution. For details, see :func:`distro.distro_release_attr`. """ - return self._distro_release_info.get(attribute, '') + return self._distro_release_info.get(attribute, "") def uname_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the uname command output data source of the OS distribution. - For details, see :func:`distro.uname_release_attr`. + For details, see :func:`distro.uname_attr`. """ - return self._uname_info.get(attribute, '') + return self._uname_info.get(attribute, "") @cached_property def _os_release_info(self): + # type: () -> Dict[str, str] """ Get the information items from the specified os-release file. @@ -935,6 +1067,7 @@ class LinuxDistribution(object): @staticmethod def _parse_os_release_content(lines): + # type: (TextIO) -> Dict[str, str] """ Parse the lines of an os-release file. @@ -959,7 +1092,7 @@ class LinuxDistribution(object): # parsed content is a unicode object. The following fix resolves that # (... but it should be fixed in shlex...): if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): - lexer.wordchars = lexer.wordchars.decode('iso-8859-1') + lexer.wordchars = lexer.wordchars.decode("iso-8859-1") tokens = list(lexer) for token in tokens: @@ -969,37 +1102,38 @@ class LinuxDistribution(object): # stripped, etc.), so the tokens are now either: # * variable assignments: var=value # * commands or their arguments (not allowed in os-release) - if '=' in token: - k, v = token.split('=', 1) + if "=" in token: + k, v = token.split("=", 1) props[k.lower()] = v else: # Ignore any tokens that are not variable assignments pass - if 'version_codename' in props: + if "version_codename" in props: # os-release added a version_codename field. Use that in # preference to anything else Note that some distros purposefully # do not have code names. They should be setting # version_codename="" - props['codename'] = props['version_codename'] - elif 'ubuntu_codename' in props: + props["codename"] = props["version_codename"] + elif "ubuntu_codename" in props: # Same as above but a non-standard field name used on older Ubuntus - props['codename'] = props['ubuntu_codename'] - elif 'version' in props: + props["codename"] = props["ubuntu_codename"] + elif "version" in props: # If there is no version_codename, parse it from the version - codename = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version']) - if codename: - codename = codename.group() - codename = codename.strip('()') - codename = codename.strip(',') + match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"]) + if match: + codename = match.group() + codename = codename.strip("()") + codename = codename.strip(",") codename = codename.strip() # codename appears within paranthese. - props['codename'] = codename + props["codename"] = codename return props @cached_property def _lsb_release_info(self): + # type: () -> Dict[str, str] """ Get the information items from the lsb_release command output. @@ -1008,17 +1142,19 @@ class LinuxDistribution(object): """ if not self.include_lsb: return {} - with open(os.devnull, 'w') as devnull: + with open(os.devnull, "wb") as devnull: try: - cmd = ('lsb_release', '-a') + cmd = ("lsb_release", "-a") stdout = subprocess.check_output(cmd, stderr=devnull) - except OSError: # Command not found + # Command not found or lsb_release returned error + except (OSError, subprocess.CalledProcessError): return {} content = self._to_str(stdout).splitlines() return self._parse_lsb_release_content(content) @staticmethod def _parse_lsb_release_content(lines): + # type: (Iterable[str]) -> Dict[str, str] """ Parse the output of the lsb_release command. @@ -1033,19 +1169,20 @@ class LinuxDistribution(object): """ props = {} for line in lines: - kv = line.strip('\n').split(':', 1) + kv = line.strip("\n").split(":", 1) if len(kv) != 2: # Ignore lines without colon. continue k, v = kv - props.update({k.replace(' ', '_').lower(): v.strip()}) + props.update({k.replace(" ", "_").lower(): v.strip()}) return props @cached_property def _uname_info(self): - with open(os.devnull, 'w') as devnull: + # type: () -> Dict[str, str] + with open(os.devnull, "wb") as devnull: try: - cmd = ('uname', '-rs') + cmd = ("uname", "-rs") stdout = subprocess.check_output(cmd, stderr=devnull) except OSError: return {} @@ -1054,25 +1191,27 @@ class LinuxDistribution(object): @staticmethod def _parse_uname_content(lines): + # type: (Sequence[str]) -> Dict[str, str] props = {} - match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) + match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) if match: name, version = match.groups() # This is to prevent the Linux kernel version from # appearing as the 'best' version on otherwise # identifiable distributions. - if name == 'Linux': + if name == "Linux": return {} - props['id'] = name.lower() - props['name'] = name - props['release'] = version + props["id"] = name.lower() + props["name"] = name + props["release"] = version return props @staticmethod def _to_str(text): + # type: (Union[bytes, str]) -> str encoding = sys.getfilesystemencoding() - encoding = 'utf-8' if encoding == 'ascii' else encoding + encoding = "utf-8" if encoding == "ascii" else encoding if sys.version_info[0] >= 3: if isinstance(text, bytes): @@ -1085,6 +1224,7 @@ class LinuxDistribution(object): @cached_property def _distro_release_info(self): + # type: () -> Dict[str, str] """ Get the information items from the specified distro release file. @@ -1094,23 +1234,21 @@ class LinuxDistribution(object): if self.distro_release_file: # If it was specified, we use it and parse what we can, even if # its file name or content does not match the expected pattern. - distro_info = self._parse_distro_release_file( - self.distro_release_file) + distro_info = self._parse_distro_release_file(self.distro_release_file) basename = os.path.basename(self.distro_release_file) # The file name pattern for user-specified distro release files # is somewhat more tolerant (compared to when searching for the # file), because we want to use what was specified as best as # possible. match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if 'name' in distro_info \ - and 'cloudlinux' in distro_info['name'].lower(): - distro_info['id'] = 'cloudlinux' + if "name" in distro_info and "cloudlinux" in distro_info["name"].lower(): + distro_info["id"] = "cloudlinux" elif match: - distro_info['id'] = match.group(1) + distro_info["id"] = match.group(1) return distro_info else: try: - basenames = os.listdir(_UNIXCONFDIR) + basenames = os.listdir(self.etc_dir) # We sort for repeatability in cases where there are multiple # distro specific files; e.g. CentOS, Oracle, Enterprise all # containing `redhat-release` on top of their own. @@ -1120,38 +1258,41 @@ class LinuxDistribution(object): # sure about the *-release files. Check common entries of # /etc for information. If they turn out to not be there the # error is handled in `_parse_distro_release_file()`. - basenames = ['SuSE-release', - 'arch-release', - 'base-release', - 'centos-release', - 'fedora-release', - 'gentoo-release', - 'mageia-release', - 'mandrake-release', - 'mandriva-release', - 'mandrivalinux-release', - 'manjaro-release', - 'oracle-release', - 'redhat-release', - 'sl-release', - 'slackware-version'] + basenames = [ + "SuSE-release", + "arch-release", + "base-release", + "centos-release", + "fedora-release", + "gentoo-release", + "mageia-release", + "mandrake-release", + "mandriva-release", + "mandrivalinux-release", + "manjaro-release", + "oracle-release", + "redhat-release", + "sl-release", + "slackware-version", + ] for basename in basenames: if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: continue match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) if match: - filepath = os.path.join(_UNIXCONFDIR, basename) + filepath = os.path.join(self.etc_dir, basename) distro_info = self._parse_distro_release_file(filepath) - if 'name' in distro_info: + if "name" in distro_info: # The name is always present if the pattern matches self.distro_release_file = filepath - distro_info['id'] = match.group(1) - if 'cloudlinux' in distro_info['name'].lower(): - distro_info['id'] = 'cloudlinux' + distro_info["id"] = match.group(1) + if "cloudlinux" in distro_info["name"].lower(): + distro_info["id"] = "cloudlinux" return distro_info return {} def _parse_distro_release_file(self, filepath): + # type: (str) -> Dict[str, str] """ Parse a distro release file. @@ -1170,11 +1311,12 @@ class LinuxDistribution(object): except (OSError, IOError): # Ignore not being able to read a specific, seemingly version # related file. - # See https://github.com/nir0s/distro/issues/162 + # See https://github.com/python-distro/distro/issues/162 return {} @staticmethod def _parse_distro_release_content(line): + # type: (str) -> Dict[str, str] """ Parse a line from a distro release file. @@ -1185,18 +1327,17 @@ class LinuxDistribution(object): Returns: A dictionary containing all information items. """ - matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( - line.strip()[::-1]) + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1]) distro_info = {} if matches: # regexp ensures non-None - distro_info['name'] = matches.group(3)[::-1] + distro_info["name"] = matches.group(3)[::-1] if matches.group(2): - distro_info['version_id'] = matches.group(2)[::-1] + distro_info["version_id"] = matches.group(2)[::-1] if matches.group(1): - distro_info['codename'] = matches.group(1)[::-1] + distro_info["codename"] = matches.group(1)[::-1] elif line: - distro_info['name'] = line.strip() + distro_info["name"] = line.strip() return distro_info @@ -1204,27 +1345,42 @@ _distro = LinuxDistribution() def main(): + # type: () -> None logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) parser = argparse.ArgumentParser(description="OS distro info tool") parser.add_argument( - '--json', - '-j', - help="Output in machine readable format", - action="store_true") + "--json", "-j", help="Output in machine readable format", action="store_true" + ) + + parser.add_argument( + "--root-dir", + "-r", + type=str, + dest="root_dir", + help="Path to the root filesystem directory (defaults to /)", + ) + args = parser.parse_args() + if args.root_dir: + dist = LinuxDistribution( + include_lsb=False, include_uname=False, root_dir=args.root_dir + ) + else: + dist = _distro + if args.json: - logger.info(json.dumps(info(), indent=4, sort_keys=True)) + logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) else: - logger.info('Name: %s', name(pretty=True)) - distribution_version = version(pretty=True) - logger.info('Version: %s', distribution_version) - distribution_codename = codename() - logger.info('Codename: %s', distribution_codename) + logger.info("Name: %s", dist.name(pretty=True)) + distribution_version = dist.version(pretty=True) + logger.info("Version: %s", distribution_version) + distribution_codename = dist.codename() + logger.info("Codename: %s", distribution_codename) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py index 049123492e28952b521183e058b3a7ea301ac8d0..d1d82f157f884dc65160a41b436258d1aaf12e4c 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/__init__.py @@ -32,4 +32,4 @@ __all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", # this has to be at the top level, see how setup.py parses this #: Distribution version number. -__version__ = "1.0.1" +__version__ = "1.1" diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py index 4c77717bbc02c96907e1ce8c5bd36cc7bbed09cb..3ff803c195243984738c6f3f328c78d9c4999cfc 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_ihatexml.py @@ -136,6 +136,7 @@ def normaliseCharList(charList): i += j return rv + # We don't really support characters above the BMP :( max_unicode = int("FFFF", 16) @@ -254,7 +255,7 @@ class InfosetFilter(object): nameRest = name[1:] m = nonXmlNameFirstBMPRegexp.match(nameFirst) if m: - warnings.warn("Coercing non-XML name", DataLossWarning) + warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) nameFirstOutput = self.getReplacementCharacter(nameFirst) else: nameFirstOutput = nameFirst @@ -262,7 +263,7 @@ class InfosetFilter(object): nameRestOutput = nameRest replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) for char in replaceChars: - warnings.warn("Coercing non-XML name", DataLossWarning) + warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) replacement = self.getReplacementCharacter(char) nameRestOutput = nameRestOutput.replace(char, replacement) return nameFirstOutput + nameRestOutput diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py index a65e55f64bf433f98eb4ed331af91d84fb760ea9..e0bb37602c8e2f1f808ba8fdcb1b7f63451fa4f5 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_inputstream.py @@ -1,10 +1,11 @@ from __future__ import absolute_import, division, unicode_literals -from pip._vendor.six import text_type, binary_type +from pip._vendor.six import text_type from pip._vendor.six.moves import http_client, urllib import codecs import re +from io import BytesIO, StringIO from pip._vendor import webencodings @@ -12,13 +13,6 @@ from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase from .constants import _ReparseException from . import _utils -from io import StringIO - -try: - from io import BytesIO -except ImportError: - BytesIO = StringIO - # Non-unicode versions of constants for use in the pre-parser spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) @@ -40,13 +34,13 @@ if _utils.supports_lone_surrogates: else: invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) -non_bmp_invalid_codepoints = set([0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, - 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, - 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, - 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, - 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, - 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, - 0x10FFFE, 0x10FFFF]) +non_bmp_invalid_codepoints = {0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF} ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]") @@ -367,7 +361,7 @@ class HTMLUnicodeInputStream(object): def unget(self, char): # Only one character is allowed to be ungotten at once - it must # be consumed again before any further call to unget - if char is not None: + if char is not EOF: if self.chunkOffset == 0: # unget is called quite rarely, so it's a good idea to do # more work here if it saves a bit of work in the frequently @@ -449,7 +443,7 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): try: stream.seek(stream.tell()) - except: # pylint:disable=bare-except + except Exception: stream = BufferedStream(stream) return stream @@ -461,7 +455,7 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): if charEncoding[0] is not None: return charEncoding - # If we've been overriden, we've been overriden + # If we've been overridden, we've been overridden charEncoding = lookupEncoding(self.override_encoding), "certain" if charEncoding[0] is not None: return charEncoding @@ -664,9 +658,7 @@ class EncodingBytes(bytes): """Look for a sequence of bytes at the start of a string. If the bytes are found return True and advance the position to the byte after the match. Otherwise return False and leave the position alone""" - p = self.position - data = self[p:p + len(bytes)] - rv = data.startswith(bytes) + rv = self.startswith(bytes, self.position) if rv: self.position += len(bytes) return rv @@ -674,15 +666,11 @@ class EncodingBytes(bytes): def jumpTo(self, bytes): """Look for the next sequence of bytes matching a given sequence. If a match is found advance the position to the last byte of the match""" - newPosition = self[self.position:].find(bytes) - if newPosition > -1: - # XXX: This is ugly, but I can't see a nicer way to fix this. - if self._position == -1: - self._position = 0 - self._position += (newPosition + len(bytes) - 1) - return True - else: + try: + self._position = self.index(bytes, self.position) + len(bytes) - 1 + except ValueError: raise StopIteration + return True class EncodingParser(object): @@ -694,6 +682,9 @@ class EncodingParser(object): self.encoding = None def getEncoding(self): + if b"= (3, 7): + attributeMap = dict +else: + attributeMap = OrderedDict + class HTMLTokenizer(object): """ This class takes care of tokenizing HTML. @@ -228,6 +234,14 @@ class HTMLTokenizer(object): # Add token to the queue to be yielded if (token["type"] in tagTokenTypes): token["name"] = token["name"].translate(asciiUpper2Lower) + if token["type"] == tokenTypes["StartTag"]: + raw = token["data"] + data = attributeMap(raw) + if len(raw) > len(data): + # we had some duplicated attribute, fix so first wins + data.update(raw[::-1]) + token["data"] = data + if token["type"] == tokenTypes["EndTag"]: if token["data"]: self.tokenQueue.append({"type": tokenTypes["ParseError"], diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py index a5ba4bf123aa585ec8a47dce6838b09b4dfa0b1a..07bad5d31c1ec7aa7ac081dac5586db91f3c5a99 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/__init__.py @@ -1,14 +1,5 @@ from __future__ import absolute_import, division, unicode_literals -from .py import Trie as PyTrie +from .py import Trie -Trie = PyTrie - -# pylint:disable=wrong-import-position -try: - from .datrie import Trie as DATrie -except ImportError: - pass -else: - Trie = DATrie -# pylint:enable=wrong-import-position +__all__ = ["Trie"] diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py deleted file mode 100644 index e2e5f86621c3881d1d812814aec9d153dca34bd1..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/_trie/datrie.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals - -from datrie import Trie as DATrie -from pip._vendor.six import text_type - -from ._base import Trie as ABCTrie - - -class Trie(ABCTrie): - def __init__(self, data): - chars = set() - for key in data.keys(): - if not isinstance(key, text_type): - raise TypeError("All keys must be strings") - for char in key: - chars.add(char) - - self._data = DATrie("".join(chars)) - for key, value in data.items(): - self._data[key] = value - - def __contains__(self, key): - return key in self._data - - def __len__(self): - return len(self._data) - - def __iter__(self): - raise NotImplementedError() - - def __getitem__(self, key): - return self._data[key] - - def keys(self, prefix=None): - return self._data.keys(prefix) - - def has_keys_with_prefix(self, prefix): - return self._data.has_keys_with_prefix(prefix) - - def longest_prefix(self, prefix): - return self._data.longest_prefix(prefix) - - def longest_prefix_item(self, prefix): - return self._data.longest_prefix_item(prefix) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py b/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py index 0703afb38b13bf56998d43aa542dcea9839d8132..d7c4926afce32cad6a36379f6e159e7fe6215c75 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/_utils.py @@ -2,12 +2,20 @@ from __future__ import absolute_import, division, unicode_literals from types import ModuleType -from pip._vendor.six import text_type - try: - import xml.etree.cElementTree as default_etree + from collections.abc import Mapping except ImportError: + from collections import Mapping + +from pip._vendor.six import text_type, PY3 + +if PY3: import xml.etree.ElementTree as default_etree +else: + try: + import xml.etree.cElementTree as default_etree + except ImportError: + import xml.etree.ElementTree as default_etree __all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", @@ -27,7 +35,7 @@ try: # We need this with u"" because of http://bugs.jython.org/issue2039 _x = eval('u"\\uD800"') # pylint:disable=eval-used assert isinstance(_x, text_type) -except: # pylint:disable=bare-except +except Exception: supports_lone_surrogates = False else: supports_lone_surrogates = True @@ -47,9 +55,6 @@ class MethodDispatcher(dict): """ def __init__(self, items=()): - # Using _dictEntries instead of directly assigning to self is about - # twice as fast. Please do careful performance testing before changing - # anything here. _dictEntries = [] for name, value in items: if isinstance(name, (list, tuple, frozenset, set)): @@ -64,6 +69,36 @@ class MethodDispatcher(dict): def __getitem__(self, key): return dict.get(self, key, self.default) + def __get__(self, instance, owner=None): + return BoundMethodDispatcher(instance, self) + + +class BoundMethodDispatcher(Mapping): + """Wraps a MethodDispatcher, binding its return values to `instance`""" + def __init__(self, instance, dispatcher): + self.instance = instance + self.dispatcher = dispatcher + + def __getitem__(self, key): + # see https://docs.python.org/3/reference/datamodel.html#object.__get__ + # on a function, __get__ is used to bind a function to an instance as a bound method + return self.dispatcher[key].__get__(self.instance) + + def get(self, key, default): + if key in self.dispatcher: + return self[key] + else: + return default + + def __iter__(self): + return iter(self.dispatcher) + + def __len__(self): + return len(self.dispatcher) + + def __contains__(self, key): + return key in self.dispatcher + # Some utility functions to deal with weirdness around UCS2 vs UCS4 # python builds diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py b/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py index 1ff804190cd890e68be62fccb1a907ff9122f78a..fe3e237cd8a118da1c707412fe8251d2e19477c5 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/constants.py @@ -519,8 +519,8 @@ adjustForeignAttributes = { "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) } -unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in - adjustForeignAttributes.items()]) +unadjustForeignAttributes = {(ns, local): qname for qname, (prefix, local, ns) in + adjustForeignAttributes.items()} spaceCharacters = frozenset([ "\t", @@ -544,8 +544,7 @@ asciiLetters = frozenset(string.ascii_letters) digits = frozenset(string.digits) hexDigits = frozenset(string.hexdigits) -asciiUpper2Lower = dict([(ord(c), ord(c.lower())) - for c in string.ascii_uppercase]) +asciiUpper2Lower = {ord(c): ord(c.lower()) for c in string.ascii_uppercase} # Heading elements need to be ordered headingElements = ( @@ -2934,7 +2933,7 @@ tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], tokenTypes["EmptyTag"]]) -prefixes = dict([(v, k) for k, v in namespaces.items()]) +prefixes = {v: k for k, v in namespaces.items()} prefixes["http://www.w3.org/1998/Math/MathML"] = "math" diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py index af8e77b81e373c4639114f764daa6e182dce1422..aa7431d131213f85ab36cacc54b000e88898080b 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -1,6 +1,15 @@ +"""Deprecated from html5lib 1.1. + +See `here `_ for +information about its deprecation; `Bleach `_ +is recommended as a replacement. Please let us know in the aforementioned issue +if Bleach is unsuitable for your needs. + +""" from __future__ import absolute_import, division, unicode_literals import re +import warnings from xml.sax.saxutils import escape, unescape from pip._vendor.six.moves import urllib_parse as urlparse @@ -11,6 +20,14 @@ from ..constants import namespaces, prefixes __all__ = ["Filter"] +_deprecation_msg = ( + "html5lib's sanitizer is deprecated; see " + + "https://github.com/html5lib/html5lib-python/issues/443 and please let " + + "us know if Bleach is unsuitable for your needs" +) + +warnings.warn(_deprecation_msg, DeprecationWarning) + allowed_elements = frozenset(( (namespaces['html'], 'a'), (namespaces['html'], 'abbr'), @@ -750,6 +767,9 @@ class Filter(base.Filter): """ super(Filter, self).__init__(source) + + warnings.warn(_deprecation_msg, DeprecationWarning) + self.allowed_elements = allowed_elements self.allowed_attributes = allowed_attributes self.allowed_css_properties = allowed_css_properties diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py b/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py index ae41a133761c02adcec54d500a66da0a49dd7009..d06784f3d254176d1bd125cfd4d3af7f13005387 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/html5parser.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, division, unicode_literals from pip._vendor.six import with_metaclass, viewkeys import types -from collections import OrderedDict from . import _inputstream from . import _tokenizer @@ -119,8 +118,8 @@ class HTMLParser(object): self.tree = tree(namespaceHTMLElements) self.errors = [] - self.phases = dict([(name, cls(self, self.tree)) for name, cls in - getPhases(debug).items()]) + self.phases = {name: cls(self, self.tree) for name, cls in + getPhases(debug).items()} def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): @@ -202,7 +201,7 @@ class HTMLParser(object): DoctypeToken = tokenTypes["Doctype"] ParseErrorToken = tokenTypes["ParseError"] - for token in self.normalizedTokens(): + for token in self.tokenizer: prev_token = None new_token = token while new_token is not None: @@ -260,10 +259,6 @@ class HTMLParser(object): if reprocess: assert self.phase not in phases - def normalizedTokens(self): - for token in self.tokenizer: - yield self.normalizeToken(token) - def parse(self, stream, *args, **kwargs): """Parse a HTML document into a well-formed tree @@ -325,17 +320,6 @@ class HTMLParser(object): if self.strict: raise ParseError(E[errorcode] % datavars) - def normalizeToken(self, token): - # HTML5 specific normalizations to the token stream - if token["type"] == tokenTypes["StartTag"]: - raw = token["data"] - token["data"] = OrderedDict(raw) - if len(raw) > len(token["data"]): - # we had some duplicated attribute, fix so first wins - token["data"].update(raw[::-1]) - - return token - def adjustMathMLAttributes(self, token): adjust_attributes(token, adjustMathMLAttributes) @@ -413,16 +397,12 @@ class HTMLParser(object): def getPhases(debug): def log(function): """Logger that records which phase processes each token""" - type_names = dict((value, key) for key, value in - tokenTypes.items()) + type_names = {value: key for key, value in tokenTypes.items()} def wrapped(self, *args, **kwargs): if function.__name__.startswith("process") and len(args) > 0: token = args[0] - try: - info = {"type": type_names[token['type']]} - except: - raise + info = {"type": type_names[token['type']]} if token['type'] in tagTokenTypes: info["name"] = token['name'] @@ -446,10 +426,13 @@ def getPhases(debug): class Phase(with_metaclass(getMetaclass(debug, log))): """Base class for helper object that implements each phase of processing """ + __slots__ = ("parser", "tree", "__startTagCache", "__endTagCache") def __init__(self, parser, tree): self.parser = parser self.tree = tree + self.__startTagCache = {} + self.__endTagCache = {} def processEOF(self): raise NotImplementedError @@ -469,7 +452,21 @@ def getPhases(debug): self.tree.insertText(token["data"]) def processStartTag(self, token): - return self.startTagHandler[token["name"]](token) + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__startTagCache: + func = self.__startTagCache[name] + else: + func = self.__startTagCache[name] = self.startTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__startTagCache) > len(self.startTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__startTagCache.pop(next(iter(self.__startTagCache))) + return func(token) def startTagHtml(self, token): if not self.parser.firstStartTag and token["name"] == "html": @@ -482,9 +479,25 @@ def getPhases(debug): self.parser.firstStartTag = False def processEndTag(self, token): - return self.endTagHandler[token["name"]](token) + # Note the caching is done here rather than BoundMethodDispatcher as doing it there + # requires a circular reference to the Phase, and this ends up with a significant + # (CPython 2.7, 3.8) GC cost when parsing many short inputs + name = token["name"] + # In Py2, using `in` is quicker in general than try/except KeyError + # In Py3, `in` is quicker when there are few cache hits (typically short inputs) + if name in self.__endTagCache: + func = self.__endTagCache[name] + else: + func = self.__endTagCache[name] = self.endTagHandler[name] + # bound the cache size in case we get loads of unknown tags + while len(self.__endTagCache) > len(self.endTagHandler) * 1.1: + # this makes the eviction policy random on Py < 3.7 and FIFO >= 3.7 + self.__endTagCache.pop(next(iter(self.__endTagCache))) + return func(token) class InitialPhase(Phase): + __slots__ = tuple() + def processSpaceCharacters(self, token): pass @@ -613,6 +626,8 @@ def getPhases(debug): return True class BeforeHtmlPhase(Phase): + __slots__ = tuple() + # helper methods def insertHtmlElement(self): self.tree.insertRoot(impliedTagToken("html", "StartTag")) @@ -648,19 +663,7 @@ def getPhases(debug): return token class BeforeHeadPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("head", self.startTagHead) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("head", "body", "html", "br"), self.endTagImplyHead) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): self.startTagHead(impliedTagToken("head", "StartTag")) @@ -693,28 +696,19 @@ def getPhases(debug): self.parser.parseError("end-tag-after-implied-root", {"name": token["name"]}) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), endTagImplyHead) + ]) + endTagHandler.default = endTagOther + class InHeadPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("title", self.startTagTitle), - (("noframes", "style"), self.startTagNoFramesStyle), - ("noscript", self.startTagNoscript), - ("script", self.startTagScript), - (("base", "basefont", "bgsound", "command", "link"), - self.startTagBaseLinkCommand), - ("meta", self.startTagMeta), - ("head", self.startTagHead) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("head", self.endTagHead), - (("br", "html", "body"), self.endTagHtmlBodyBr) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # the real thing def processEOF(self): @@ -796,22 +790,27 @@ def getPhases(debug): def anythingElse(self): self.endTagHead(impliedTagToken("head")) - class InHeadNoscriptPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("title", startTagTitle), + (("noframes", "style"), startTagNoFramesStyle), + ("noscript", startTagNoscript), + ("script", startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + startTagBaseLinkCommand), + ("meta", startTagMeta), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("head", endTagHead), + (("br", "html", "body"), endTagHtmlBodyBr) + ]) + endTagHandler.default = endTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("basefont", "bgsound", "link", "meta", "noframes", "style"), self.startTagBaseLinkCommand), - (("head", "noscript"), self.startTagHeadNoscript), - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("noscript", self.endTagNoscript), - ("br", self.endTagBr), - ]) - self.endTagHandler.default = self.endTagOther + class InHeadNoscriptPhase(Phase): + __slots__ = tuple() def processEOF(self): self.parser.parseError("eof-in-head-noscript") @@ -860,23 +859,21 @@ def getPhases(debug): # Caller must raise parse error first! self.endTagNoscript(impliedTagToken("noscript")) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), startTagBaseLinkCommand), + (("head", "noscript"), startTagHeadNoscript), + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("noscript", endTagNoscript), + ("br", endTagBr), + ]) + endTagHandler.default = endTagOther + class AfterHeadPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("body", self.startTagBody), - ("frameset", self.startTagFrameset), - (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", - "style", "title"), - self.startTagFromHead), - ("head", self.startTagHead) - ]) - self.startTagHandler.default = self.startTagOther - self.endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), - self.endTagHtmlBodyBr)]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): self.anythingElse() @@ -927,80 +924,30 @@ def getPhases(debug): self.parser.phase = self.parser.phases["inBody"] self.parser.framesetOK = True + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("body", startTagBody), + ("frameset", startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + startTagFromHead), + ("head", startTagHead) + ]) + startTagHandler.default = startTagOther + endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + endTagHtmlBodyBr)]) + endTagHandler.default = endTagOther + class InBodyPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody # the really-really-really-very crazy mode - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + __slots__ = ("processSpaceCharacters",) + def __init__(self, *args, **kwargs): + super(InBodyPhase, self).__init__(*args, **kwargs) # Set this to the default handler self.processSpaceCharacters = self.processSpaceCharactersNonPre - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("base", "basefont", "bgsound", "command", "link", "meta", - "script", "style", "title"), - self.startTagProcessInHead), - ("body", self.startTagBody), - ("frameset", self.startTagFrameset), - (("address", "article", "aside", "blockquote", "center", "details", - "dir", "div", "dl", "fieldset", "figcaption", "figure", - "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", - "section", "summary", "ul"), - self.startTagCloseP), - (headingElements, self.startTagHeading), - (("pre", "listing"), self.startTagPreListing), - ("form", self.startTagForm), - (("li", "dd", "dt"), self.startTagListItem), - ("plaintext", self.startTagPlaintext), - ("a", self.startTagA), - (("b", "big", "code", "em", "font", "i", "s", "small", "strike", - "strong", "tt", "u"), self.startTagFormatting), - ("nobr", self.startTagNobr), - ("button", self.startTagButton), - (("applet", "marquee", "object"), self.startTagAppletMarqueeObject), - ("xmp", self.startTagXmp), - ("table", self.startTagTable), - (("area", "br", "embed", "img", "keygen", "wbr"), - self.startTagVoidFormatting), - (("param", "source", "track"), self.startTagParamSource), - ("input", self.startTagInput), - ("hr", self.startTagHr), - ("image", self.startTagImage), - ("isindex", self.startTagIsIndex), - ("textarea", self.startTagTextarea), - ("iframe", self.startTagIFrame), - ("noscript", self.startTagNoscript), - (("noembed", "noframes"), self.startTagRawtext), - ("select", self.startTagSelect), - (("rp", "rt"), self.startTagRpRt), - (("option", "optgroup"), self.startTagOpt), - (("math"), self.startTagMath), - (("svg"), self.startTagSvg), - (("caption", "col", "colgroup", "frame", "head", - "tbody", "td", "tfoot", "th", "thead", - "tr"), self.startTagMisplaced) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("body", self.endTagBody), - ("html", self.endTagHtml), - (("address", "article", "aside", "blockquote", "button", "center", - "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", - "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", - "section", "summary", "ul"), self.endTagBlock), - ("form", self.endTagForm), - ("p", self.endTagP), - (("dd", "dt", "li"), self.endTagListItem), - (headingElements, self.endTagHeading), - (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", - "strike", "strong", "tt", "u"), self.endTagFormatting), - (("applet", "marquee", "object"), self.endTagAppletMarqueeObject), - ("br", self.endTagBr), - ]) - self.endTagHandler.default = self.endTagOther - def isMatchingFormattingElement(self, node1, node2): return (node1.name == node2.name and node1.namespace == node2.namespace and @@ -1650,14 +1597,73 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) break + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("base", "basefont", "bgsound", "command", "link", "meta", + "script", "style", "title"), + startTagProcessInHead), + ("body", startTagBody), + ("frameset", startTagFrameset), + (("address", "article", "aside", "blockquote", "center", "details", + "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", + "section", "summary", "ul"), + startTagCloseP), + (headingElements, startTagHeading), + (("pre", "listing"), startTagPreListing), + ("form", startTagForm), + (("li", "dd", "dt"), startTagListItem), + ("plaintext", startTagPlaintext), + ("a", startTagA), + (("b", "big", "code", "em", "font", "i", "s", "small", "strike", + "strong", "tt", "u"), startTagFormatting), + ("nobr", startTagNobr), + ("button", startTagButton), + (("applet", "marquee", "object"), startTagAppletMarqueeObject), + ("xmp", startTagXmp), + ("table", startTagTable), + (("area", "br", "embed", "img", "keygen", "wbr"), + startTagVoidFormatting), + (("param", "source", "track"), startTagParamSource), + ("input", startTagInput), + ("hr", startTagHr), + ("image", startTagImage), + ("isindex", startTagIsIndex), + ("textarea", startTagTextarea), + ("iframe", startTagIFrame), + ("noscript", startTagNoscript), + (("noembed", "noframes"), startTagRawtext), + ("select", startTagSelect), + (("rp", "rt"), startTagRpRt), + (("option", "optgroup"), startTagOpt), + (("math"), startTagMath), + (("svg"), startTagSvg), + (("caption", "col", "colgroup", "frame", "head", + "tbody", "td", "tfoot", "th", "thead", + "tr"), startTagMisplaced) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("body", endTagBody), + ("html", endTagHtml), + (("address", "article", "aside", "blockquote", "button", "center", + "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", + "section", "summary", "ul"), endTagBlock), + ("form", endTagForm), + ("p", endTagP), + (("dd", "dt", "li"), endTagListItem), + (headingElements, endTagHeading), + (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", + "strike", "strong", "tt", "u"), endTagFormatting), + (("applet", "marquee", "object"), endTagAppletMarqueeObject), + ("br", endTagBr), + ]) + endTagHandler.default = endTagOther + class TextPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([]) - self.startTagHandler.default = self.startTagOther - self.endTagHandler = _utils.MethodDispatcher([ - ("script", self.endTagScript)]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processCharacters(self, token): self.tree.insertText(token["data"]) @@ -1683,30 +1689,15 @@ def getPhases(debug): self.tree.openElements.pop() self.parser.phase = self.parser.originalPhase + startTagHandler = _utils.MethodDispatcher([]) + startTagHandler.default = startTagOther + endTagHandler = _utils.MethodDispatcher([ + ("script", endTagScript)]) + endTagHandler.default = endTagOther + class InTablePhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-table - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("caption", self.startTagCaption), - ("colgroup", self.startTagColgroup), - ("col", self.startTagCol), - (("tbody", "tfoot", "thead"), self.startTagRowGroup), - (("td", "th", "tr"), self.startTagImplyTbody), - ("table", self.startTagTable), - (("style", "script"), self.startTagStyleScript), - ("input", self.startTagInput), - ("form", self.startTagForm) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("table", self.endTagTable), - (("body", "caption", "col", "colgroup", "html", "tbody", "td", - "tfoot", "th", "thead", "tr"), self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper methods def clearStackToTableContext(self): @@ -1828,9 +1819,32 @@ def getPhases(debug): self.parser.phases["inBody"].processEndTag(token) self.tree.insertFromTable = False + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("caption", startTagCaption), + ("colgroup", startTagColgroup), + ("col", startTagCol), + (("tbody", "tfoot", "thead"), startTagRowGroup), + (("td", "th", "tr"), startTagImplyTbody), + ("table", startTagTable), + (("style", "script"), startTagStyleScript), + ("input", startTagInput), + ("form", startTagForm) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("table", endTagTable), + (("body", "caption", "col", "colgroup", "html", "tbody", "td", + "tfoot", "th", "thead", "tr"), endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InTableTextPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + __slots__ = ("originalPhase", "characterTokens") + + def __init__(self, *args, **kwargs): + super(InTableTextPhase, self).__init__(*args, **kwargs) self.originalPhase = None self.characterTokens = [] @@ -1875,23 +1889,7 @@ def getPhases(debug): class InCaptionPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-caption - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", - "thead", "tr"), self.startTagTableElement) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("caption", self.endTagCaption), - ("table", self.endTagTable), - (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", - "thead", "tr"), self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def ignoreEndTagCaption(self): return not self.tree.elementInScope("caption", variant="table") @@ -1944,23 +1942,24 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inBody"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), startTagTableElement) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("caption", endTagCaption), + ("table", endTagTable), + (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", + "thead", "tr"), endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InColumnGroupPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-column - - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("col", self.startTagCol) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("colgroup", self.endTagColgroup), - ("col", self.endTagCol) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def ignoreEndTagColgroup(self): return self.tree.openElements[-1].name == "html" @@ -2010,26 +2009,21 @@ def getPhases(debug): if not ignoreEndTag: return token + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("col", startTagCol) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("colgroup", endTagColgroup), + ("col", endTagCol) + ]) + endTagHandler.default = endTagOther + class InTableBodyPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-table0 - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("tr", self.startTagTr), - (("td", "th"), self.startTagTableCell), - (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), - self.startTagTableOther) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), - ("table", self.endTagTable), - (("body", "caption", "col", "colgroup", "html", "td", "th", - "tr"), self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper methods def clearStackToTableBodyContext(self): @@ -2108,26 +2102,26 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inTable"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("tr", startTagTr), + (("td", "th"), startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), + startTagTableOther) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("tbody", "tfoot", "thead"), endTagTableRowGroup), + ("table", endTagTable), + (("body", "caption", "col", "colgroup", "html", "td", "th", + "tr"), endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InRowPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-row - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("td", "th"), self.startTagTableCell), - (("caption", "col", "colgroup", "tbody", "tfoot", "thead", - "tr"), self.startTagTableOther) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("tr", self.endTagTr), - ("table", self.endTagTable), - (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), - (("body", "caption", "col", "colgroup", "html", "td", "th"), - self.endTagIgnore) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper methods (XXX unify this with other table helper methods) def clearStackToTableRowContext(self): @@ -2197,23 +2191,26 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inTable"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("td", "th"), startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead", + "tr"), startTagTableOther) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("tr", endTagTr), + ("table", endTagTable), + (("tbody", "tfoot", "thead"), endTagTableRowGroup), + (("body", "caption", "col", "colgroup", "html", "td", "th"), + endTagIgnore) + ]) + endTagHandler.default = endTagOther + class InCellPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#in-cell - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", - "thead", "tr"), self.startTagTableOther) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - (("td", "th"), self.endTagTableCell), - (("body", "caption", "col", "colgroup", "html"), self.endTagIgnore), - (("table", "tbody", "tfoot", "thead", "tr"), self.endTagImply) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # helper def closeCell(self): @@ -2273,26 +2270,22 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inBody"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), startTagTableOther) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("td", "th"), endTagTableCell), + (("body", "caption", "col", "colgroup", "html"), endTagIgnore), + (("table", "tbody", "tfoot", "thead", "tr"), endTagImply) + ]) + endTagHandler.default = endTagOther + class InSelectPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("option", self.startTagOption), - ("optgroup", self.startTagOptgroup), - ("select", self.startTagSelect), - (("input", "keygen", "textarea"), self.startTagInput), - ("script", self.startTagScript) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([ - ("option", self.endTagOption), - ("optgroup", self.endTagOptgroup), - ("select", self.endTagSelect) - ]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() # http://www.whatwg.org/specs/web-apps/current-work/#in-select def processEOF(self): @@ -2373,21 +2366,25 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag-in-select", {"name": token["name"]}) - class InSelectInTablePhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), - self.startTagTable) - ]) - self.startTagHandler.default = self.startTagOther + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("option", startTagOption), + ("optgroup", startTagOptgroup), + ("select", startTagSelect), + (("input", "keygen", "textarea"), startTagInput), + ("script", startTagScript) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + ("option", endTagOption), + ("optgroup", endTagOptgroup), + ("select", endTagSelect) + ]) + endTagHandler.default = endTagOther - self.endTagHandler = _utils.MethodDispatcher([ - (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), - self.endTagTable) - ]) - self.endTagHandler.default = self.endTagOther + class InSelectInTablePhase(Phase): + __slots__ = tuple() def processEOF(self): self.parser.phases["inSelect"].processEOF() @@ -2412,7 +2409,21 @@ def getPhases(debug): def endTagOther(self, token): return self.parser.phases["inSelect"].processEndTag(token) + startTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + startTagTable) + ]) + startTagHandler.default = startTagOther + + endTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + endTagTable) + ]) + endTagHandler.default = endTagOther + class InForeignContentPhase(Phase): + __slots__ = tuple() + breakoutElements = frozenset(["b", "big", "blockquote", "body", "br", "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", @@ -2422,9 +2433,6 @@ def getPhases(debug): "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", "var"]) - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - def adjustSVGTagNames(self, token): replacements = {"altglyph": "altGlyph", "altglyphdef": "altGlyphDef", @@ -2478,7 +2486,7 @@ def getPhases(debug): currentNode = self.tree.openElements[-1] if (token["name"] in self.breakoutElements or (token["name"] == "font" and - set(token["data"].keys()) & set(["color", "face", "size"]))): + set(token["data"].keys()) & {"color", "face", "size"})): self.parser.parseError("unexpected-html-element-in-foreign-content", {"name": token["name"]}) while (self.tree.openElements[-1].namespace != @@ -2528,16 +2536,7 @@ def getPhases(debug): return new_token class AfterBodyPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) - - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml) - ]) - self.startTagHandler.default = self.startTagOther - - self.endTagHandler = _utils.MethodDispatcher([("html", self.endTagHtml)]) - self.endTagHandler.default = self.endTagOther + __slots__ = tuple() def processEOF(self): # Stop parsing @@ -2574,23 +2573,17 @@ def getPhases(debug): self.parser.phase = self.parser.phases["inBody"] return token - class InFramesetPhase(Phase): - # http://www.whatwg.org/specs/web-apps/current-work/#in-frameset - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml) + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("frameset", self.startTagFrameset), - ("frame", self.startTagFrame), - ("noframes", self.startTagNoframes) - ]) - self.startTagHandler.default = self.startTagOther + endTagHandler = _utils.MethodDispatcher([("html", endTagHtml)]) + endTagHandler.default = endTagOther - self.endTagHandler = _utils.MethodDispatcher([ - ("frameset", self.endTagFrameset) - ]) - self.endTagHandler.default = self.endTagOther + class InFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-frameset + __slots__ = tuple() def processEOF(self): if self.tree.openElements[-1].name != "html": @@ -2631,21 +2624,22 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag-in-frameset", {"name": token["name"]}) - class AfterFramesetPhase(Phase): - # http://www.whatwg.org/specs/web-apps/current-work/#after3 - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("frameset", startTagFrameset), + ("frame", startTagFrame), + ("noframes", startTagNoframes) + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("noframes", self.startTagNoframes) - ]) - self.startTagHandler.default = self.startTagOther + endTagHandler = _utils.MethodDispatcher([ + ("frameset", endTagFrameset) + ]) + endTagHandler.default = endTagOther - self.endTagHandler = _utils.MethodDispatcher([ - ("html", self.endTagHtml) - ]) - self.endTagHandler.default = self.endTagOther + class AfterFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#after3 + __slots__ = tuple() def processEOF(self): # Stop parsing @@ -2668,14 +2662,19 @@ def getPhases(debug): self.parser.parseError("unexpected-end-tag-after-frameset", {"name": token["name"]}) - class AfterAfterBodyPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", Phase.startTagHtml), + ("noframes", startTagNoframes) + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml) - ]) - self.startTagHandler.default = self.startTagOther + endTagHandler = _utils.MethodDispatcher([ + ("html", endTagHtml) + ]) + endTagHandler.default = endTagOther + + class AfterAfterBodyPhase(Phase): + __slots__ = tuple() def processEOF(self): pass @@ -2706,15 +2705,13 @@ def getPhases(debug): self.parser.phase = self.parser.phases["inBody"] return token - class AfterAfterFramesetPhase(Phase): - def __init__(self, parser, tree): - Phase.__init__(self, parser, tree) + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml) + ]) + startTagHandler.default = startTagOther - self.startTagHandler = _utils.MethodDispatcher([ - ("html", self.startTagHtml), - ("noframes", self.startTagNoFrames) - ]) - self.startTagHandler.default = self.startTagOther + class AfterAfterFramesetPhase(Phase): + __slots__ = tuple() def processEOF(self): pass @@ -2741,6 +2738,13 @@ def getPhases(debug): def processEndTag(self, token): self.parser.parseError("expected-eof-but-got-end-tag", {"name": token["name"]}) + + startTagHandler = _utils.MethodDispatcher([ + ("html", startTagHtml), + ("noframes", startTagNoFrames) + ]) + startTagHandler.default = startTagOther + # pylint:enable=unused-argument return { @@ -2774,8 +2778,8 @@ def getPhases(debug): def adjust_attributes(token, replacements): needs_adjustment = viewkeys(token['data']) & viewkeys(replacements) if needs_adjustment: - token['data'] = OrderedDict((replacements.get(k, k), v) - for k, v in token['data'].items()) + token['data'] = type(token['data'])((replacements.get(k, k), v) + for k, v in token['data'].items()) def impliedTagToken(name, type="EndTag", attributes=None, diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py b/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py index 53f4d44c39742a80ab170507e22347cc59219c72..d5669d8c149923d2eb8ac5c5289ecb7d4ed3e532 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/serializer.py @@ -274,7 +274,7 @@ class HTMLSerializer(object): if token["systemId"]: if token["systemId"].find('"') >= 0: if token["systemId"].find("'") >= 0: - self.serializeError("System identifer contains both single and double quote characters") + self.serializeError("System identifier contains both single and double quote characters") quote_char = "'" else: quote_char = '"' diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py index 73973db51b87cedc5a76db5850700c929c9b254f..965fce29d3b9e01e9e9374a3d6318badeca7e1e1 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/base.py @@ -10,9 +10,9 @@ Marker = None listElementsMap = { None: (frozenset(scopingElements), False), - "button": (frozenset(scopingElements | set([(namespaces["html"], "button")])), False), - "list": (frozenset(scopingElements | set([(namespaces["html"], "ol"), - (namespaces["html"], "ul")])), False), + "button": (frozenset(scopingElements | {(namespaces["html"], "button")}), False), + "list": (frozenset(scopingElements | {(namespaces["html"], "ol"), + (namespaces["html"], "ul")}), False), "table": (frozenset([(namespaces["html"], "html"), (namespaces["html"], "table")]), False), "select": (frozenset([(namespaces["html"], "optgroup"), @@ -28,7 +28,7 @@ class Node(object): :arg name: The tag name associated with the node """ - # The tag name assocaited with the node + # The tag name associated with the node self.name = name # The parent of the current node (or None for the document node) self.parent = None diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py index 0dedf441643902d5258b4aef64f001f9282e7220..ea92dc301fe3fcf2ec9839c39c7844ae9f5df614 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree.py @@ -5,6 +5,8 @@ from pip._vendor.six import text_type import re +from copy import copy + from . import base from .. import _ihatexml from .. import constants @@ -61,16 +63,17 @@ def getETreeBuilder(ElementTreeImplementation, fullTree=False): return self._element.attrib def _setAttributes(self, attributes): - # Delete existing attributes first - # XXX - there may be a better way to do this... - for key in list(self._element.attrib.keys()): - del self._element.attrib[key] - for key, value in attributes.items(): - if isinstance(key, tuple): - name = "{%s}%s" % (key[2], key[1]) - else: - name = key - self._element.set(name, value) + el_attrib = self._element.attrib + el_attrib.clear() + if attributes: + # calling .items _always_ allocates, and the above truthy check is cheaper than the + # allocation on average + for key, value in attributes.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], key[1]) + else: + name = key + el_attrib[name] = value attributes = property(_getAttributes, _setAttributes) @@ -129,8 +132,8 @@ def getETreeBuilder(ElementTreeImplementation, fullTree=False): def cloneNode(self): element = type(self)(self.name, self.namespace) - for name, value in self.attributes.items(): - element.attributes[name] = value + if self._element.attrib: + element._element.attrib = copy(self._element.attrib) return element def reparentChildren(self, newParent): diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py index ca12a99cccf98a1be4bc463bce29c402ff634e62..f037759f42e74a88bb685679d2c3f574d186521e 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py @@ -16,6 +16,11 @@ import warnings import re import sys +try: + from collections.abc import MutableMapping +except ImportError: + from collections import MutableMapping + from . import base from ..constants import DataLossWarning from .. import constants @@ -23,6 +28,7 @@ from . import etree as etree_builders from .. import _ihatexml import lxml.etree as etree +from pip._vendor.six import PY3, binary_type fullTree = True @@ -44,7 +50,11 @@ class Document(object): self._childNodes = [] def appendChild(self, element): - self._elementTree.getroot().addnext(element._element) + last = self._elementTree.getroot() + for last in self._elementTree.getroot().itersiblings(): + pass + + last.addnext(element._element) def _getChildNodes(self): return self._childNodes @@ -185,26 +195,37 @@ class TreeBuilder(base.TreeBuilder): infosetFilter = self.infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) self.namespaceHTMLElements = namespaceHTMLElements - class Attributes(dict): - def __init__(self, element, value=None): - if value is None: - value = {} + class Attributes(MutableMapping): + def __init__(self, element): self._element = element - dict.__init__(self, value) # pylint:disable=non-parent-init-called - for key, value in self.items(): - if isinstance(key, tuple): - name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) - else: - name = infosetFilter.coerceAttribute(key) - self._element._element.attrib[name] = value - def __setitem__(self, key, value): - dict.__setitem__(self, key, value) + def _coerceKey(self, key): if isinstance(key, tuple): name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) else: name = infosetFilter.coerceAttribute(key) - self._element._element.attrib[name] = value + return name + + def __getitem__(self, key): + value = self._element._element.attrib[self._coerceKey(key)] + if not PY3 and isinstance(value, binary_type): + value = value.decode("ascii") + return value + + def __setitem__(self, key, value): + self._element._element.attrib[self._coerceKey(key)] = value + + def __delitem__(self, key): + del self._element._element.attrib[self._coerceKey(key)] + + def __iter__(self): + return iter(self._element._element.attrib) + + def __len__(self): + return len(self._element._element.attrib) + + def clear(self): + return self._element._element.attrib.clear() class Element(builder.Element): def __init__(self, name, namespace): @@ -225,8 +246,10 @@ class TreeBuilder(base.TreeBuilder): def _getAttributes(self): return self._attributes - def _setAttributes(self, attributes): - self._attributes = Attributes(self, attributes) + def _setAttributes(self, value): + attributes = self.attributes + attributes.clear() + attributes.update(value) attributes = property(_getAttributes, _setAttributes) @@ -234,8 +257,11 @@ class TreeBuilder(base.TreeBuilder): data = infosetFilter.coerceCharacters(data) builder.Element.insertText(self, data, insertBefore) - def appendChild(self, child): - builder.Element.appendChild(self, child) + def cloneNode(self): + element = type(self)(self.name, self.namespace) + if self._element.attrib: + element._element.attrib.update(self._element.attrib) + return element class Comment(builder.Comment): def __init__(self, data): diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py index 9bec2076f3f60e644cdd3265d8e113ca17149dfd..b2d3aac3137f5d374ec35dd4bbfdb5e732fc51f0 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py @@ -2,10 +2,10 @@ tree, generating tokens identical to those produced by the tokenizer module. -To create a tree walker for a new type of tree, you need to do +To create a tree walker for a new type of tree, you need to implement a tree walker object (called TreeWalker by convention) that -implements a 'serialize' method taking a tree as sole argument and -returning an iterator generating tokens. +implements a 'serialize' method which takes a tree as sole argument and +returns an iterator which generates tokens. """ from __future__ import absolute_import, division, unicode_literals diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py index 95fc0c170301532cf2b499cd1697fe1179ae80c5..837b27ec486924eb9ccef53c6a5d578bd787aefd 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree.py @@ -127,4 +127,5 @@ def getETreeBuilder(ElementTreeImplementation): return locals() + getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py index e81ddf33b2eff3b278effd4602ef1511311ee4f6..c56af390fe250c1048036375fff340db5d2807a8 100644 --- a/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py +++ b/venv/Lib/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py @@ -1,6 +1,8 @@ from __future__ import absolute_import, division, unicode_literals from pip._vendor.six import text_type +from collections import OrderedDict + from lxml import etree from ..treebuilders.etree import tag_regexp @@ -163,7 +165,7 @@ class TreeWalker(base.NonRecursiveTreeWalker): else: namespace = None tag = ensure_str(node.tag) - attrs = {} + attrs = OrderedDict() for name, value in list(node.attrib.items()): name = ensure_str(name) value = ensure_str(value) diff --git a/venv/Lib/site-packages/pip/_vendor/idna/__init__.py b/venv/Lib/site-packages/pip/_vendor/idna/__init__.py index 847bf9354781fddc44ed2f47055a67c025972fd4..a40eeafcc914108ca79c5d83d6e81da1b29c6e80 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/__init__.py @@ -1,2 +1,44 @@ from .package_data import __version__ -from .core import * +from .core import ( + IDNABidiError, + IDNAError, + InvalidCodepoint, + InvalidCodepointContext, + alabel, + check_bidi, + check_hyphen_ok, + check_initial_combiner, + check_label, + check_nfc, + decode, + encode, + ulabel, + uts46_remap, + valid_contextj, + valid_contexto, + valid_label_length, + valid_string_length, +) +from .intranges import intranges_contain + +__all__ = [ + "IDNABidiError", + "IDNAError", + "InvalidCodepoint", + "InvalidCodepointContext", + "alabel", + "check_bidi", + "check_hyphen_ok", + "check_initial_combiner", + "check_label", + "check_nfc", + "decode", + "encode", + "intranges_contain", + "ulabel", + "uts46_remap", + "valid_contextj", + "valid_contexto", + "valid_label_length", + "valid_string_length", +] diff --git a/venv/Lib/site-packages/pip/_vendor/idna/codec.py b/venv/Lib/site-packages/pip/_vendor/idna/codec.py index 98c65ead146e42b359a11703d719bafe916b1ad7..080f22a3b5923f9d32b955a0c432b35a7e11c2b2 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/codec.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/codec.py @@ -1,41 +1,43 @@ from .core import encode, decode, alabel, ulabel, IDNAError import codecs import re +from typing import Tuple, Optional -_unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') +_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') class Codec(codecs.Codec): def encode(self, data, errors='strict'): - + # type: (str, str) -> Tuple[bytes, int] if errors != 'strict': - raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) if not data: - return "", 0 + return b"", 0 return encode(data), len(data) def decode(self, data, errors='strict'): - + # type: (bytes, str) -> Tuple[str, int] if errors != 'strict': - raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) if not data: - return u"", 0 + return '', 0 return decode(data), len(data) class IncrementalEncoder(codecs.BufferedIncrementalEncoder): - def _buffer_encode(self, data, errors, final): + def _buffer_encode(self, data, errors, final): # type: ignore + # type: (str, str, bool) -> Tuple[str, int] if errors != 'strict': - raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) if not data: - return ("", 0) + return "", 0 labels = _unicode_dots_re.split(data) - trailing_dot = u'' + trailing_dot = '' if labels: if not labels[-1]: trailing_dot = '.' @@ -55,37 +57,30 @@ class IncrementalEncoder(codecs.BufferedIncrementalEncoder): size += len(label) # Join with U+002E - result = ".".join(result) + trailing_dot + result_str = '.'.join(result) + trailing_dot # type: ignore size += len(trailing_dot) - return (result, size) + return result_str, size class IncrementalDecoder(codecs.BufferedIncrementalDecoder): - def _buffer_decode(self, data, errors, final): + def _buffer_decode(self, data, errors, final): # type: ignore + # type: (str, str, bool) -> Tuple[str, int] if errors != 'strict': - raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) if not data: - return (u"", 0) - - # IDNA allows decoding to operate on Unicode strings, too. - if isinstance(data, unicode): - labels = _unicode_dots_re.split(data) - else: - # Must be ASCII string - data = str(data) - unicode(data, "ascii") - labels = data.split(".") - - trailing_dot = u'' + return ('', 0) + + labels = _unicode_dots_re.split(data) + trailing_dot = '' if labels: if not labels[-1]: - trailing_dot = u'.' + trailing_dot = '.' del labels[-1] elif not final: # Keep potentially unfinished label until the next call del labels[-1] if labels: - trailing_dot = u'.' + trailing_dot = '.' result = [] size = 0 @@ -95,22 +90,26 @@ class IncrementalDecoder(codecs.BufferedIncrementalDecoder): size += 1 size += len(label) - result = u".".join(result) + trailing_dot + result_str = '.'.join(result) + trailing_dot size += len(trailing_dot) - return (result, size) + return (result_str, size) class StreamWriter(Codec, codecs.StreamWriter): pass + class StreamReader(Codec, codecs.StreamReader): pass + def getregentry(): + # type: () -> codecs.CodecInfo + # Compatibility as a search_function for codecs.register() return codecs.CodecInfo( name='idna', - encode=Codec().encode, - decode=Codec().decode, + encode=Codec().encode, # type: ignore + decode=Codec().decode, # type: ignore incrementalencoder=IncrementalEncoder, incrementaldecoder=IncrementalDecoder, streamwriter=StreamWriter, diff --git a/venv/Lib/site-packages/pip/_vendor/idna/compat.py b/venv/Lib/site-packages/pip/_vendor/idna/compat.py index 4d47f336dbc55ca7141150c14ec1a7924dd0e854..dc896c766c1edf89b0126010ff9fa77eb14a3e73 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/compat.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/compat.py @@ -1,12 +1,16 @@ from .core import * from .codec import * +from typing import Any, Union def ToASCII(label): + # type: (str) -> bytes return encode(label) def ToUnicode(label): + # type: (Union[bytes, bytearray]) -> str return decode(label) def nameprep(s): - raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") + # type: (Any) -> None + raise NotImplementedError('IDNA 2008 does not utilise nameprep protocol') diff --git a/venv/Lib/site-packages/pip/_vendor/idna/core.py b/venv/Lib/site-packages/pip/_vendor/idna/core.py index 9c3bba2ad7d133b1e57bcd7d638ba70427a8018b..d6051297d5533a28ad80ded45a96e94462006144 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/core.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/core.py @@ -2,16 +2,12 @@ from . import idnadata import bisect import unicodedata import re -import sys +from typing import Union, Optional from .intranges import intranges_contain _virama_combining_class = 9 _alabel_prefix = b'xn--' -_unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') - -if sys.version_info[0] >= 3: - unicode = str - unichr = chr +_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') class IDNAError(UnicodeError): """ Base exception for all IDNA-encoding related problems """ @@ -34,45 +30,49 @@ class InvalidCodepointContext(IDNAError): def _combining_class(cp): - v = unicodedata.combining(unichr(cp)) + # type: (int) -> int + v = unicodedata.combining(chr(cp)) if v == 0: - if not unicodedata.name(unichr(cp)): - raise ValueError("Unknown character in unicodedata") + if not unicodedata.name(chr(cp)): + raise ValueError('Unknown character in unicodedata') return v def _is_script(cp, script): + # type: (str, str) -> bool return intranges_contain(ord(cp), idnadata.scripts[script]) def _punycode(s): + # type: (str) -> bytes return s.encode('punycode') def _unot(s): - return 'U+{0:04X}'.format(s) + # type: (int) -> str + return 'U+{:04X}'.format(s) def valid_label_length(label): - + # type: (Union[bytes, str]) -> bool if len(label) > 63: return False return True def valid_string_length(label, trailing_dot): - + # type: (Union[bytes, str], bool) -> bool if len(label) > (254 if trailing_dot else 253): return False return True def check_bidi(label, check_ltr=False): - + # type: (str, bool) -> bool # Bidi rules should only be applied if string contains RTL characters bidi_label = False for (idx, cp) in enumerate(label, 1): direction = unicodedata.bidirectional(cp) if direction == '': # String likely comes from a newer version of Unicode - raise IDNABidiError('Unknown directionality in label {0} at position {1}'.format(repr(label), idx)) + raise IDNABidiError('Unknown directionality in label {} at position {}'.format(repr(label), idx)) if direction in ['R', 'AL', 'AN']: bidi_label = True if not bidi_label and not check_ltr: @@ -85,17 +85,17 @@ def check_bidi(label, check_ltr=False): elif direction == 'L': rtl = False else: - raise IDNABidiError('First codepoint in label {0} must be directionality L, R or AL'.format(repr(label))) + raise IDNABidiError('First codepoint in label {} must be directionality L, R or AL'.format(repr(label))) valid_ending = False - number_type = False + number_type = None # type: Optional[str] for (idx, cp) in enumerate(label, 1): direction = unicodedata.bidirectional(cp) if rtl: # Bidi rule 2 if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: - raise IDNABidiError('Invalid direction for codepoint at position {0} in a right-to-left label'.format(idx)) + raise IDNABidiError('Invalid direction for codepoint at position {} in a right-to-left label'.format(idx)) # Bidi rule 3 if direction in ['R', 'AL', 'EN', 'AN']: valid_ending = True @@ -111,7 +111,7 @@ def check_bidi(label, check_ltr=False): else: # Bidi rule 5 if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: - raise IDNABidiError('Invalid direction for codepoint at position {0} in a left-to-right label'.format(idx)) + raise IDNABidiError('Invalid direction for codepoint at position {} in a left-to-right label'.format(idx)) # Bidi rule 6 if direction in ['L', 'EN']: valid_ending = True @@ -125,14 +125,14 @@ def check_bidi(label, check_ltr=False): def check_initial_combiner(label): - + # type: (str) -> bool if unicodedata.category(label[0])[0] == 'M': raise IDNAError('Label begins with an illegal combining character') return True def check_hyphen_ok(label): - + # type: (str) -> bool if label[2:4] == '--': raise IDNAError('Label has disallowed hyphens in 3rd and 4th position') if label[0] == '-' or label[-1] == '-': @@ -141,13 +141,13 @@ def check_hyphen_ok(label): def check_nfc(label): - + # type: (str) -> None if unicodedata.normalize('NFC', label) != label: raise IDNAError('Label must be in Normalization Form C') def valid_contextj(label, pos): - + # type: (str, int) -> bool cp_value = ord(label[pos]) if cp_value == 0x200c: @@ -191,7 +191,7 @@ def valid_contextj(label, pos): def valid_contexto(label, pos, exception=False): - + # type: (str, int, bool) -> bool cp_value = ord(label[pos]) if cp_value == 0x00b7: @@ -212,7 +212,7 @@ def valid_contexto(label, pos, exception=False): elif cp_value == 0x30fb: for cp in label: - if cp == u'\u30fb': + if cp == '\u30fb': continue if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'): return True @@ -230,9 +230,11 @@ def valid_contexto(label, pos, exception=False): return False return True + return False -def check_label(label): +def check_label(label): + # type: (Union[str, bytes, bytearray]) -> None if isinstance(label, (bytes, bytearray)): label = label.decode('utf-8') if len(label) == 0: @@ -249,100 +251,109 @@ def check_label(label): elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']): try: if not valid_contextj(label, pos): - raise InvalidCodepointContext('Joiner {0} not allowed at position {1} in {2}'.format( + raise InvalidCodepointContext('Joiner {} not allowed at position {} in {}'.format( _unot(cp_value), pos+1, repr(label))) except ValueError: - raise IDNAError('Unknown codepoint adjacent to joiner {0} at position {1} in {2}'.format( + raise IDNAError('Unknown codepoint adjacent to joiner {} at position {} in {}'.format( _unot(cp_value), pos+1, repr(label))) elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']): if not valid_contexto(label, pos): - raise InvalidCodepointContext('Codepoint {0} not allowed at position {1} in {2}'.format(_unot(cp_value), pos+1, repr(label))) + raise InvalidCodepointContext('Codepoint {} not allowed at position {} in {}'.format(_unot(cp_value), pos+1, repr(label))) else: - raise InvalidCodepoint('Codepoint {0} at position {1} of {2} not allowed'.format(_unot(cp_value), pos+1, repr(label))) + raise InvalidCodepoint('Codepoint {} at position {} of {} not allowed'.format(_unot(cp_value), pos+1, repr(label))) check_bidi(label) def alabel(label): - + # type: (str) -> bytes try: - label = label.encode('ascii') - ulabel(label) - if not valid_label_length(label): + label_bytes = label.encode('ascii') + ulabel(label_bytes) + if not valid_label_length(label_bytes): raise IDNAError('Label too long') - return label + return label_bytes except UnicodeEncodeError: pass if not label: raise IDNAError('No Input') - label = unicode(label) + label = str(label) check_label(label) - label = _punycode(label) - label = _alabel_prefix + label + label_bytes = _punycode(label) + label_bytes = _alabel_prefix + label_bytes - if not valid_label_length(label): + if not valid_label_length(label_bytes): raise IDNAError('Label too long') - return label + return label_bytes def ulabel(label): - + # type: (Union[str, bytes, bytearray]) -> str if not isinstance(label, (bytes, bytearray)): try: - label = label.encode('ascii') + label_bytes = label.encode('ascii') except UnicodeEncodeError: check_label(label) return label - - label = label.lower() - if label.startswith(_alabel_prefix): - label = label[len(_alabel_prefix):] - if label.decode('ascii')[-1] == '-': + else: + label_bytes = label + + label_bytes = label_bytes.lower() + if label_bytes.startswith(_alabel_prefix): + label_bytes = label_bytes[len(_alabel_prefix):] + if not label_bytes: + raise IDNAError('Malformed A-label, no Punycode eligible content found') + if label_bytes.decode('ascii')[-1] == '-': raise IDNAError('A-label must not end with a hyphen') else: - check_label(label) - return label.decode('ascii') + check_label(label_bytes) + return label_bytes.decode('ascii') - label = label.decode('punycode') + label = label_bytes.decode('punycode') check_label(label) return label def uts46_remap(domain, std3_rules=True, transitional=False): + # type: (str, bool, bool) -> str """Re-map the characters in the string according to UTS46 processing.""" from .uts46data import uts46data - output = u"" - try: - for pos, char in enumerate(domain): - code_point = ord(char) + output = '' + + for pos, char in enumerate(domain): + code_point = ord(char) + try: uts46row = uts46data[code_point if code_point < 256 else - bisect.bisect_left(uts46data, (code_point, "Z")) - 1] + bisect.bisect_left(uts46data, (code_point, 'Z')) - 1] status = uts46row[1] - replacement = uts46row[2] if len(uts46row) == 3 else None - if (status == "V" or - (status == "D" and not transitional) or - (status == "3" and not std3_rules and replacement is None)): + replacement = None # type: Optional[str] + if len(uts46row) == 3: + replacement = uts46row[2] # type: ignore + if (status == 'V' or + (status == 'D' and not transitional) or + (status == '3' and not std3_rules and replacement is None)): output += char - elif replacement is not None and (status == "M" or - (status == "3" and not std3_rules) or - (status == "D" and transitional)): + elif replacement is not None and (status == 'M' or + (status == '3' and not std3_rules) or + (status == 'D' and transitional)): output += replacement - elif status != "I": + elif status != 'I': raise IndexError() - return unicodedata.normalize("NFC", output) - except IndexError: - raise InvalidCodepoint( - "Codepoint {0} not allowed at position {1} in {2}".format( - _unot(code_point), pos + 1, repr(domain))) + except IndexError: + raise InvalidCodepoint( + 'Codepoint {} not allowed at position {} in {}'.format( + _unot(code_point), pos + 1, repr(domain))) + return unicodedata.normalize('NFC', output) -def encode(s, strict=False, uts46=False, std3_rules=False, transitional=False): +def encode(s, strict=False, uts46=False, std3_rules=False, transitional=False): + # type: (Union[str, bytes, bytearray], bool, bool, bool, bool) -> bytes if isinstance(s, (bytes, bytearray)): - s = s.decode("ascii") + s = s.decode('ascii') if uts46: s = uts46_remap(s, std3_rules, transitional) trailing_dot = False @@ -371,9 +382,9 @@ def encode(s, strict=False, uts46=False, std3_rules=False, transitional=False): def decode(s, strict=False, uts46=False, std3_rules=False): - + # type: (Union[str, bytes, bytearray], bool, bool, bool) -> str if isinstance(s, (bytes, bytearray)): - s = s.decode("ascii") + s = s.decode('ascii') if uts46: s = uts46_remap(s, std3_rules, False) trailing_dot = False @@ -381,7 +392,7 @@ def decode(s, strict=False, uts46=False, std3_rules=False): if not strict: labels = _unicode_dots_re.split(s) else: - labels = s.split(u'.') + labels = s.split('.') if not labels or labels == ['']: raise IDNAError('Empty domain') if not labels[-1]: @@ -394,5 +405,5 @@ def decode(s, strict=False, uts46=False, std3_rules=False): else: raise IDNAError('Empty label') if trailing_dot: - result.append(u'') - return u'.'.join(result) + result.append('') + return '.'.join(result) diff --git a/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py b/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py index 2b81c522cf580683ea47e6b33d97db35f3324b78..b86a3e06e429aa1bdd67713a3d4998410375dd49 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/idnadata.py @@ -1,6 +1,6 @@ # This file is automatically generated by tools/idna-data -__version__ = "12.1.0" +__version__ = '13.0.0' scripts = { 'Greek': ( 0x37000000374, @@ -48,16 +48,18 @@ scripts = { 0x300700003008, 0x30210000302a, 0x30380000303c, - 0x340000004db6, - 0x4e0000009ff0, + 0x340000004dc0, + 0x4e0000009ffd, 0xf9000000fa6e, 0xfa700000fada, - 0x200000002a6d7, + 0x16ff000016ff2, + 0x200000002a6de, 0x2a7000002b735, 0x2b7400002b81e, 0x2b8200002cea2, 0x2ceb00002ebe1, 0x2f8000002fa1e, + 0x300000003134b, ), 'Hebrew': ( 0x591000005c8, @@ -389,9 +391,9 @@ joining_types = { 0x853: 68, 0x854: 82, 0x855: 68, - 0x856: 85, - 0x857: 85, - 0x858: 85, + 0x856: 82, + 0x857: 82, + 0x858: 82, 0x860: 68, 0x861: 85, 0x862: 68, @@ -432,6 +434,16 @@ joining_types = { 0x8bb: 68, 0x8bc: 68, 0x8bd: 68, + 0x8be: 68, + 0x8bf: 68, + 0x8c0: 68, + 0x8c1: 68, + 0x8c2: 68, + 0x8c3: 68, + 0x8c4: 68, + 0x8c5: 68, + 0x8c6: 68, + 0x8c7: 68, 0x8e2: 85, 0x1806: 85, 0x1807: 68, @@ -756,6 +768,34 @@ joining_types = { 0x10f52: 68, 0x10f53: 68, 0x10f54: 82, + 0x10fb0: 68, + 0x10fb1: 85, + 0x10fb2: 68, + 0x10fb3: 68, + 0x10fb4: 82, + 0x10fb5: 82, + 0x10fb6: 82, + 0x10fb7: 85, + 0x10fb8: 68, + 0x10fb9: 82, + 0x10fba: 82, + 0x10fbb: 68, + 0x10fbc: 68, + 0x10fbd: 82, + 0x10fbe: 68, + 0x10fbf: 68, + 0x10fc0: 85, + 0x10fc1: 68, + 0x10fc2: 82, + 0x10fc3: 82, + 0x10fc4: 68, + 0x10fc5: 85, + 0x10fc6: 85, + 0x10fc7: 85, + 0x10fc8: 85, + 0x10fc9: 82, + 0x10fca: 68, + 0x10fcb: 76, 0x110bd: 85, 0x110cd: 85, 0x1e900: 68, @@ -1129,7 +1169,7 @@ codepoint_classes = { 0x8400000085c, 0x8600000086b, 0x8a0000008b5, - 0x8b6000008be, + 0x8b6000008c8, 0x8d3000008e2, 0x8e300000958, 0x96000000964, @@ -1188,7 +1228,7 @@ codepoint_classes = { 0xb3c00000b45, 0xb4700000b49, 0xb4b00000b4e, - 0xb5600000b58, + 0xb5500000b58, 0xb5f00000b64, 0xb6600000b70, 0xb7100000b72, @@ -1233,8 +1273,7 @@ codepoint_classes = { 0xce000000ce4, 0xce600000cf0, 0xcf100000cf3, - 0xd0000000d04, - 0xd0500000d0d, + 0xd0000000d0d, 0xd0e00000d11, 0xd1200000d45, 0xd4600000d49, @@ -1243,7 +1282,7 @@ codepoint_classes = { 0xd5f00000d64, 0xd6600000d70, 0xd7a00000d80, - 0xd8200000d84, + 0xd8100000d84, 0xd8500000d97, 0xd9a00000db2, 0xdb300000dbc, @@ -1358,6 +1397,7 @@ codepoint_classes = { 0x1a9000001a9a, 0x1aa700001aa8, 0x1ab000001abe, + 0x1abf00001ac1, 0x1b0000001b4c, 0x1b5000001b5a, 0x1b6b00001b74, @@ -1609,10 +1649,10 @@ codepoint_classes = { 0x30a1000030fb, 0x30fc000030ff, 0x310500003130, - 0x31a0000031bb, + 0x31a0000031c0, 0x31f000003200, - 0x340000004db6, - 0x4e0000009ff0, + 0x340000004dc0, + 0x4e0000009ffd, 0xa0000000a48d, 0xa4d00000a4fe, 0xa5000000a60d, @@ -1727,8 +1767,11 @@ codepoint_classes = { 0xa7bd0000a7be, 0xa7bf0000a7c0, 0xa7c30000a7c4, - 0xa7f70000a7f8, + 0xa7c80000a7c9, + 0xa7ca0000a7cb, + 0xa7f60000a7f8, 0xa7fa0000a828, + 0xa82c0000a82d, 0xa8400000a874, 0xa8800000a8c6, 0xa8d00000a8da, @@ -1753,7 +1796,7 @@ codepoint_classes = { 0xab200000ab27, 0xab280000ab2f, 0xab300000ab5b, - 0xab600000ab68, + 0xab600000ab6a, 0xabc00000abeb, 0xabec0000abee, 0xabf00000abfa, @@ -1827,9 +1870,13 @@ codepoint_classes = { 0x10cc000010cf3, 0x10d0000010d28, 0x10d3000010d3a, + 0x10e8000010eaa, + 0x10eab00010ead, + 0x10eb000010eb2, 0x10f0000010f1d, 0x10f2700010f28, 0x10f3000010f51, + 0x10fb000010fc5, 0x10fe000010ff7, 0x1100000011047, 0x1106600011070, @@ -1838,12 +1885,12 @@ codepoint_classes = { 0x110f0000110fa, 0x1110000011135, 0x1113600011140, - 0x1114400011147, + 0x1114400011148, 0x1115000011174, 0x1117600011177, 0x11180000111c5, 0x111c9000111cd, - 0x111d0000111db, + 0x111ce000111db, 0x111dc000111dd, 0x1120000011212, 0x1121300011238, @@ -1872,7 +1919,7 @@ codepoint_classes = { 0x1137000011375, 0x114000001144b, 0x114500001145a, - 0x1145e00011460, + 0x1145e00011462, 0x11480000114c6, 0x114c7000114c8, 0x114d0000114da, @@ -1889,7 +1936,14 @@ codepoint_classes = { 0x117300001173a, 0x118000001183b, 0x118c0000118ea, - 0x118ff00011900, + 0x118ff00011907, + 0x119090001190a, + 0x1190c00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193b00011944, + 0x119500001195a, 0x119a0000119a8, 0x119aa000119d8, 0x119da000119e2, @@ -1920,6 +1974,7 @@ codepoint_classes = { 0x11d9300011d99, 0x11da000011daa, 0x11ee000011ef7, + 0x11fb000011fb1, 0x120000001239a, 0x1248000012544, 0x130000001342f, @@ -1939,9 +1994,11 @@ codepoint_classes = { 0x16f4f00016f88, 0x16f8f00016fa0, 0x16fe000016fe2, - 0x16fe300016fe4, + 0x16fe300016fe5, + 0x16ff000016ff2, 0x17000000187f8, - 0x1880000018af3, + 0x1880000018cd6, + 0x18d0000018d09, 0x1b0000001b11f, 0x1b1500001b153, 0x1b1640001b168, @@ -1971,11 +2028,13 @@ codepoint_classes = { 0x1e8d00001e8d7, 0x1e9220001e94c, 0x1e9500001e95a, - 0x200000002a6d7, + 0x1fbf00001fbfa, + 0x200000002a6de, 0x2a7000002b735, 0x2b7400002b81e, 0x2b8200002cea2, 0x2ceb00002ebe1, + 0x300000003134b, ), 'CONTEXTJ': ( 0x200c0000200e, diff --git a/venv/Lib/site-packages/pip/_vendor/idna/intranges.py b/venv/Lib/site-packages/pip/_vendor/idna/intranges.py index fa8a735662d0afc1950ba75bc57e99e8480dac88..ee364a904879a3cf8719e35cc6ff8b90d5fdb81b 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/intranges.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/intranges.py @@ -6,8 +6,10 @@ in the original list?" in time O(log(# runs)). """ import bisect +from typing import List, Tuple def intranges_from_list(list_): + # type: (List[int]) -> Tuple[int, ...] """Represent a list of integers as a sequence of ranges: ((start_0, end_0), (start_1, end_1), ...), such that the original integers are exactly those x such that start_i <= x < end_i for some i. @@ -29,13 +31,16 @@ def intranges_from_list(list_): return tuple(ranges) def _encode_range(start, end): + # type: (int, int) -> int return (start << 32) | end def _decode_range(r): + # type: (int) -> Tuple[int, int] return (r >> 32), (r & ((1 << 32) - 1)) def intranges_contain(int_, ranges): + # type: (int, Tuple[int, ...]) -> bool """Determine if `int_` falls into one of the ranges in `ranges`.""" tuple_ = _encode_range(int_, 0) pos = bisect.bisect_left(ranges, tuple_) diff --git a/venv/Lib/site-packages/pip/_vendor/idna/package_data.py b/venv/Lib/site-packages/pip/_vendor/idna/package_data.py index b5d8216558a16748867cacffc2c930fca263f57c..e096d1d52dd1a608f8ae3c814d877f01dbf5fda8 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/package_data.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/package_data.py @@ -1,2 +1,2 @@ -__version__ = '2.9' +__version__ = '3.2' diff --git a/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py b/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py index 2711136d7d283ea19fcad53b7a1da3b2b2a1b796..f382ce389e802c5d6af3100ef29e882547699d65 100644 --- a/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py +++ b/venv/Lib/site-packages/pip/_vendor/idna/uts46data.py @@ -1,11 +1,13 @@ # This file is automatically generated by tools/idna-data -# vim: set fileencoding=utf-8 : + +from typing import List, Tuple, Union """IDNA Mapping Table from UTS46.""" -__version__ = "12.1.0" +__version__ = '13.0.0' def _seg_0(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0x0, '3'), (0x1, '3'), @@ -72,32 +74,32 @@ def _seg_0(): (0x3E, '3'), (0x3F, '3'), (0x40, '3'), - (0x41, 'M', u'a'), - (0x42, 'M', u'b'), - (0x43, 'M', u'c'), - (0x44, 'M', u'd'), - (0x45, 'M', u'e'), - (0x46, 'M', u'f'), - (0x47, 'M', u'g'), - (0x48, 'M', u'h'), - (0x49, 'M', u'i'), - (0x4A, 'M', u'j'), - (0x4B, 'M', u'k'), - (0x4C, 'M', u'l'), - (0x4D, 'M', u'm'), - (0x4E, 'M', u'n'), - (0x4F, 'M', u'o'), - (0x50, 'M', u'p'), - (0x51, 'M', u'q'), - (0x52, 'M', u'r'), - (0x53, 'M', u's'), - (0x54, 'M', u't'), - (0x55, 'M', u'u'), - (0x56, 'M', u'v'), - (0x57, 'M', u'w'), - (0x58, 'M', u'x'), - (0x59, 'M', u'y'), - (0x5A, 'M', u'z'), + (0x41, 'M', 'a'), + (0x42, 'M', 'b'), + (0x43, 'M', 'c'), + (0x44, 'M', 'd'), + (0x45, 'M', 'e'), + (0x46, 'M', 'f'), + (0x47, 'M', 'g'), + (0x48, 'M', 'h'), + (0x49, 'M', 'i'), + (0x4A, 'M', 'j'), + (0x4B, 'M', 'k'), + (0x4C, 'M', 'l'), + (0x4D, 'M', 'm'), + (0x4E, 'M', 'n'), + (0x4F, 'M', 'o'), + (0x50, 'M', 'p'), + (0x51, 'M', 'q'), + (0x52, 'M', 'r'), + (0x53, 'M', 's'), + (0x54, 'M', 't'), + (0x55, 'M', 'u'), + (0x56, 'M', 'v'), + (0x57, 'M', 'w'), + (0x58, 'M', 'x'), + (0x59, 'M', 'y'), + (0x5A, 'M', 'z'), (0x5B, '3'), (0x5C, '3'), (0x5D, '3'), @@ -110,6 +112,7 @@ def _seg_0(): ] def _seg_1(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0x64, 'V'), (0x65, 'V'), @@ -171,7 +174,7 @@ def _seg_1(): (0x9D, 'X'), (0x9E, 'X'), (0x9F, 'X'), - (0xA0, '3', u' '), + (0xA0, '3', ' '), (0xA1, 'V'), (0xA2, 'V'), (0xA3, 'V'), @@ -179,66 +182,67 @@ def _seg_1(): (0xA5, 'V'), (0xA6, 'V'), (0xA7, 'V'), - (0xA8, '3', u' ̈'), + (0xA8, '3', ' ̈'), (0xA9, 'V'), - (0xAA, 'M', u'a'), + (0xAA, 'M', 'a'), (0xAB, 'V'), (0xAC, 'V'), (0xAD, 'I'), (0xAE, 'V'), - (0xAF, '3', u' ̄'), + (0xAF, '3', ' ̄'), (0xB0, 'V'), (0xB1, 'V'), - (0xB2, 'M', u'2'), - (0xB3, 'M', u'3'), - (0xB4, '3', u' ́'), - (0xB5, 'M', u'μ'), + (0xB2, 'M', '2'), + (0xB3, 'M', '3'), + (0xB4, '3', ' ́'), + (0xB5, 'M', 'μ'), (0xB6, 'V'), (0xB7, 'V'), - (0xB8, '3', u' ̧'), - (0xB9, 'M', u'1'), - (0xBA, 'M', u'o'), + (0xB8, '3', ' ̧'), + (0xB9, 'M', '1'), + (0xBA, 'M', 'o'), (0xBB, 'V'), - (0xBC, 'M', u'1⁄4'), - (0xBD, 'M', u'1⁄2'), - (0xBE, 'M', u'3⁄4'), + (0xBC, 'M', '1⁄4'), + (0xBD, 'M', '1⁄2'), + (0xBE, 'M', '3⁄4'), (0xBF, 'V'), - (0xC0, 'M', u'à'), - (0xC1, 'M', u'á'), - (0xC2, 'M', u'â'), - (0xC3, 'M', u'ã'), - (0xC4, 'M', u'ä'), - (0xC5, 'M', u'å'), - (0xC6, 'M', u'æ'), - (0xC7, 'M', u'ç'), + (0xC0, 'M', 'à'), + (0xC1, 'M', 'á'), + (0xC2, 'M', 'â'), + (0xC3, 'M', 'ã'), + (0xC4, 'M', 'ä'), + (0xC5, 'M', 'å'), + (0xC6, 'M', 'æ'), + (0xC7, 'M', 'ç'), ] def _seg_2(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xC8, 'M', u'è'), - (0xC9, 'M', u'é'), - (0xCA, 'M', u'ê'), - (0xCB, 'M', u'ë'), - (0xCC, 'M', u'ì'), - (0xCD, 'M', u'í'), - (0xCE, 'M', u'î'), - (0xCF, 'M', u'ï'), - (0xD0, 'M', u'ð'), - (0xD1, 'M', u'ñ'), - (0xD2, 'M', u'ò'), - (0xD3, 'M', u'ó'), - (0xD4, 'M', u'ô'), - (0xD5, 'M', u'õ'), - (0xD6, 'M', u'ö'), + (0xC8, 'M', 'è'), + (0xC9, 'M', 'é'), + (0xCA, 'M', 'ê'), + (0xCB, 'M', 'ë'), + (0xCC, 'M', 'ì'), + (0xCD, 'M', 'í'), + (0xCE, 'M', 'î'), + (0xCF, 'M', 'ï'), + (0xD0, 'M', 'ð'), + (0xD1, 'M', 'ñ'), + (0xD2, 'M', 'ò'), + (0xD3, 'M', 'ó'), + (0xD4, 'M', 'ô'), + (0xD5, 'M', 'õ'), + (0xD6, 'M', 'ö'), (0xD7, 'V'), - (0xD8, 'M', u'ø'), - (0xD9, 'M', u'ù'), - (0xDA, 'M', u'ú'), - (0xDB, 'M', u'û'), - (0xDC, 'M', u'ü'), - (0xDD, 'M', u'ý'), - (0xDE, 'M', u'þ'), - (0xDF, 'D', u'ss'), + (0xD8, 'M', 'ø'), + (0xD9, 'M', 'ù'), + (0xDA, 'M', 'ú'), + (0xDB, 'M', 'û'), + (0xDC, 'M', 'ü'), + (0xDD, 'M', 'ý'), + (0xDE, 'M', 'þ'), + (0xDF, 'D', 'ss'), (0xE0, 'V'), (0xE1, 'V'), (0xE2, 'V'), @@ -271,765 +275,772 @@ def _seg_2(): (0xFD, 'V'), (0xFE, 'V'), (0xFF, 'V'), - (0x100, 'M', u'ā'), + (0x100, 'M', 'ā'), (0x101, 'V'), - (0x102, 'M', u'ă'), + (0x102, 'M', 'ă'), (0x103, 'V'), - (0x104, 'M', u'ą'), + (0x104, 'M', 'ą'), (0x105, 'V'), - (0x106, 'M', u'ć'), + (0x106, 'M', 'ć'), (0x107, 'V'), - (0x108, 'M', u'ĉ'), + (0x108, 'M', 'ĉ'), (0x109, 'V'), - (0x10A, 'M', u'ċ'), + (0x10A, 'M', 'ċ'), (0x10B, 'V'), - (0x10C, 'M', u'č'), + (0x10C, 'M', 'č'), (0x10D, 'V'), - (0x10E, 'M', u'ď'), + (0x10E, 'M', 'ď'), (0x10F, 'V'), - (0x110, 'M', u'đ'), + (0x110, 'M', 'đ'), (0x111, 'V'), - (0x112, 'M', u'ē'), + (0x112, 'M', 'ē'), (0x113, 'V'), - (0x114, 'M', u'ĕ'), + (0x114, 'M', 'ĕ'), (0x115, 'V'), - (0x116, 'M', u'ė'), + (0x116, 'M', 'ė'), (0x117, 'V'), - (0x118, 'M', u'ę'), + (0x118, 'M', 'ę'), (0x119, 'V'), - (0x11A, 'M', u'ě'), + (0x11A, 'M', 'ě'), (0x11B, 'V'), - (0x11C, 'M', u'ĝ'), + (0x11C, 'M', 'ĝ'), (0x11D, 'V'), - (0x11E, 'M', u'ğ'), + (0x11E, 'M', 'ğ'), (0x11F, 'V'), - (0x120, 'M', u'ġ'), + (0x120, 'M', 'ġ'), (0x121, 'V'), - (0x122, 'M', u'ģ'), + (0x122, 'M', 'ģ'), (0x123, 'V'), - (0x124, 'M', u'ĥ'), + (0x124, 'M', 'ĥ'), (0x125, 'V'), - (0x126, 'M', u'ħ'), + (0x126, 'M', 'ħ'), (0x127, 'V'), - (0x128, 'M', u'ĩ'), + (0x128, 'M', 'ĩ'), (0x129, 'V'), - (0x12A, 'M', u'ī'), + (0x12A, 'M', 'ī'), (0x12B, 'V'), ] def _seg_3(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x12C, 'M', u'ĭ'), + (0x12C, 'M', 'ĭ'), (0x12D, 'V'), - (0x12E, 'M', u'į'), + (0x12E, 'M', 'į'), (0x12F, 'V'), - (0x130, 'M', u'i̇'), + (0x130, 'M', 'i̇'), (0x131, 'V'), - (0x132, 'M', u'ij'), - (0x134, 'M', u'ĵ'), + (0x132, 'M', 'ij'), + (0x134, 'M', 'ĵ'), (0x135, 'V'), - (0x136, 'M', u'ķ'), + (0x136, 'M', 'ķ'), (0x137, 'V'), - (0x139, 'M', u'ĺ'), + (0x139, 'M', 'ĺ'), (0x13A, 'V'), - (0x13B, 'M', u'ļ'), + (0x13B, 'M', 'ļ'), (0x13C, 'V'), - (0x13D, 'M', u'ľ'), + (0x13D, 'M', 'ľ'), (0x13E, 'V'), - (0x13F, 'M', u'l·'), - (0x141, 'M', u'ł'), + (0x13F, 'M', 'l·'), + (0x141, 'M', 'ł'), (0x142, 'V'), - (0x143, 'M', u'ń'), + (0x143, 'M', 'ń'), (0x144, 'V'), - (0x145, 'M', u'ņ'), + (0x145, 'M', 'ņ'), (0x146, 'V'), - (0x147, 'M', u'ň'), + (0x147, 'M', 'ň'), (0x148, 'V'), - (0x149, 'M', u'ʼn'), - (0x14A, 'M', u'ŋ'), + (0x149, 'M', 'ʼn'), + (0x14A, 'M', 'ŋ'), (0x14B, 'V'), - (0x14C, 'M', u'ō'), + (0x14C, 'M', 'ō'), (0x14D, 'V'), - (0x14E, 'M', u'ŏ'), + (0x14E, 'M', 'ŏ'), (0x14F, 'V'), - (0x150, 'M', u'ő'), + (0x150, 'M', 'ő'), (0x151, 'V'), - (0x152, 'M', u'œ'), + (0x152, 'M', 'œ'), (0x153, 'V'), - (0x154, 'M', u'ŕ'), + (0x154, 'M', 'ŕ'), (0x155, 'V'), - (0x156, 'M', u'ŗ'), + (0x156, 'M', 'ŗ'), (0x157, 'V'), - (0x158, 'M', u'ř'), + (0x158, 'M', 'ř'), (0x159, 'V'), - (0x15A, 'M', u'ś'), + (0x15A, 'M', 'ś'), (0x15B, 'V'), - (0x15C, 'M', u'ŝ'), + (0x15C, 'M', 'ŝ'), (0x15D, 'V'), - (0x15E, 'M', u'ş'), + (0x15E, 'M', 'ş'), (0x15F, 'V'), - (0x160, 'M', u'š'), + (0x160, 'M', 'š'), (0x161, 'V'), - (0x162, 'M', u'ţ'), + (0x162, 'M', 'ţ'), (0x163, 'V'), - (0x164, 'M', u'ť'), + (0x164, 'M', 'ť'), (0x165, 'V'), - (0x166, 'M', u'ŧ'), + (0x166, 'M', 'ŧ'), (0x167, 'V'), - (0x168, 'M', u'ũ'), + (0x168, 'M', 'ũ'), (0x169, 'V'), - (0x16A, 'M', u'ū'), + (0x16A, 'M', 'ū'), (0x16B, 'V'), - (0x16C, 'M', u'ŭ'), + (0x16C, 'M', 'ŭ'), (0x16D, 'V'), - (0x16E, 'M', u'ů'), + (0x16E, 'M', 'ů'), (0x16F, 'V'), - (0x170, 'M', u'ű'), + (0x170, 'M', 'ű'), (0x171, 'V'), - (0x172, 'M', u'ų'), + (0x172, 'M', 'ų'), (0x173, 'V'), - (0x174, 'M', u'ŵ'), + (0x174, 'M', 'ŵ'), (0x175, 'V'), - (0x176, 'M', u'ŷ'), + (0x176, 'M', 'ŷ'), (0x177, 'V'), - (0x178, 'M', u'ÿ'), - (0x179, 'M', u'ź'), + (0x178, 'M', 'ÿ'), + (0x179, 'M', 'ź'), (0x17A, 'V'), - (0x17B, 'M', u'ż'), + (0x17B, 'M', 'ż'), (0x17C, 'V'), - (0x17D, 'M', u'ž'), + (0x17D, 'M', 'ž'), (0x17E, 'V'), - (0x17F, 'M', u's'), + (0x17F, 'M', 's'), (0x180, 'V'), - (0x181, 'M', u'ɓ'), - (0x182, 'M', u'ƃ'), + (0x181, 'M', 'ɓ'), + (0x182, 'M', 'ƃ'), (0x183, 'V'), - (0x184, 'M', u'ƅ'), + (0x184, 'M', 'ƅ'), (0x185, 'V'), - (0x186, 'M', u'ɔ'), - (0x187, 'M', u'ƈ'), + (0x186, 'M', 'ɔ'), + (0x187, 'M', 'ƈ'), (0x188, 'V'), - (0x189, 'M', u'ɖ'), - (0x18A, 'M', u'ɗ'), - (0x18B, 'M', u'ƌ'), + (0x189, 'M', 'ɖ'), + (0x18A, 'M', 'ɗ'), + (0x18B, 'M', 'ƌ'), (0x18C, 'V'), - (0x18E, 'M', u'ǝ'), - (0x18F, 'M', u'ə'), - (0x190, 'M', u'ɛ'), - (0x191, 'M', u'ƒ'), + (0x18E, 'M', 'ǝ'), + (0x18F, 'M', 'ə'), + (0x190, 'M', 'ɛ'), + (0x191, 'M', 'ƒ'), (0x192, 'V'), - (0x193, 'M', u'ɠ'), + (0x193, 'M', 'ɠ'), ] def _seg_4(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x194, 'M', u'ɣ'), + (0x194, 'M', 'ɣ'), (0x195, 'V'), - (0x196, 'M', u'ɩ'), - (0x197, 'M', u'ɨ'), - (0x198, 'M', u'ƙ'), + (0x196, 'M', 'ɩ'), + (0x197, 'M', 'ɨ'), + (0x198, 'M', 'ƙ'), (0x199, 'V'), - (0x19C, 'M', u'ɯ'), - (0x19D, 'M', u'ɲ'), + (0x19C, 'M', 'ɯ'), + (0x19D, 'M', 'ɲ'), (0x19E, 'V'), - (0x19F, 'M', u'ɵ'), - (0x1A0, 'M', u'ơ'), + (0x19F, 'M', 'ɵ'), + (0x1A0, 'M', 'ơ'), (0x1A1, 'V'), - (0x1A2, 'M', u'ƣ'), + (0x1A2, 'M', 'ƣ'), (0x1A3, 'V'), - (0x1A4, 'M', u'ƥ'), + (0x1A4, 'M', 'ƥ'), (0x1A5, 'V'), - (0x1A6, 'M', u'ʀ'), - (0x1A7, 'M', u'ƨ'), + (0x1A6, 'M', 'ʀ'), + (0x1A7, 'M', 'ƨ'), (0x1A8, 'V'), - (0x1A9, 'M', u'ʃ'), + (0x1A9, 'M', 'ʃ'), (0x1AA, 'V'), - (0x1AC, 'M', u'ƭ'), + (0x1AC, 'M', 'ƭ'), (0x1AD, 'V'), - (0x1AE, 'M', u'ʈ'), - (0x1AF, 'M', u'ư'), + (0x1AE, 'M', 'ʈ'), + (0x1AF, 'M', 'ư'), (0x1B0, 'V'), - (0x1B1, 'M', u'ʊ'), - (0x1B2, 'M', u'ʋ'), - (0x1B3, 'M', u'ƴ'), + (0x1B1, 'M', 'ʊ'), + (0x1B2, 'M', 'ʋ'), + (0x1B3, 'M', 'ƴ'), (0x1B4, 'V'), - (0x1B5, 'M', u'ƶ'), + (0x1B5, 'M', 'ƶ'), (0x1B6, 'V'), - (0x1B7, 'M', u'ʒ'), - (0x1B8, 'M', u'ƹ'), + (0x1B7, 'M', 'ʒ'), + (0x1B8, 'M', 'ƹ'), (0x1B9, 'V'), - (0x1BC, 'M', u'ƽ'), + (0x1BC, 'M', 'ƽ'), (0x1BD, 'V'), - (0x1C4, 'M', u'dž'), - (0x1C7, 'M', u'lj'), - (0x1CA, 'M', u'nj'), - (0x1CD, 'M', u'ǎ'), + (0x1C4, 'M', 'dž'), + (0x1C7, 'M', 'lj'), + (0x1CA, 'M', 'nj'), + (0x1CD, 'M', 'ǎ'), (0x1CE, 'V'), - (0x1CF, 'M', u'ǐ'), + (0x1CF, 'M', 'ǐ'), (0x1D0, 'V'), - (0x1D1, 'M', u'ǒ'), + (0x1D1, 'M', 'ǒ'), (0x1D2, 'V'), - (0x1D3, 'M', u'ǔ'), + (0x1D3, 'M', 'ǔ'), (0x1D4, 'V'), - (0x1D5, 'M', u'ǖ'), + (0x1D5, 'M', 'ǖ'), (0x1D6, 'V'), - (0x1D7, 'M', u'ǘ'), + (0x1D7, 'M', 'ǘ'), (0x1D8, 'V'), - (0x1D9, 'M', u'ǚ'), + (0x1D9, 'M', 'ǚ'), (0x1DA, 'V'), - (0x1DB, 'M', u'ǜ'), + (0x1DB, 'M', 'ǜ'), (0x1DC, 'V'), - (0x1DE, 'M', u'ǟ'), + (0x1DE, 'M', 'ǟ'), (0x1DF, 'V'), - (0x1E0, 'M', u'ǡ'), + (0x1E0, 'M', 'ǡ'), (0x1E1, 'V'), - (0x1E2, 'M', u'ǣ'), + (0x1E2, 'M', 'ǣ'), (0x1E3, 'V'), - (0x1E4, 'M', u'ǥ'), + (0x1E4, 'M', 'ǥ'), (0x1E5, 'V'), - (0x1E6, 'M', u'ǧ'), + (0x1E6, 'M', 'ǧ'), (0x1E7, 'V'), - (0x1E8, 'M', u'ǩ'), + (0x1E8, 'M', 'ǩ'), (0x1E9, 'V'), - (0x1EA, 'M', u'ǫ'), + (0x1EA, 'M', 'ǫ'), (0x1EB, 'V'), - (0x1EC, 'M', u'ǭ'), + (0x1EC, 'M', 'ǭ'), (0x1ED, 'V'), - (0x1EE, 'M', u'ǯ'), + (0x1EE, 'M', 'ǯ'), (0x1EF, 'V'), - (0x1F1, 'M', u'dz'), - (0x1F4, 'M', u'ǵ'), + (0x1F1, 'M', 'dz'), + (0x1F4, 'M', 'ǵ'), (0x1F5, 'V'), - (0x1F6, 'M', u'ƕ'), - (0x1F7, 'M', u'ƿ'), - (0x1F8, 'M', u'ǹ'), + (0x1F6, 'M', 'ƕ'), + (0x1F7, 'M', 'ƿ'), + (0x1F8, 'M', 'ǹ'), (0x1F9, 'V'), - (0x1FA, 'M', u'ǻ'), + (0x1FA, 'M', 'ǻ'), (0x1FB, 'V'), - (0x1FC, 'M', u'ǽ'), + (0x1FC, 'M', 'ǽ'), (0x1FD, 'V'), - (0x1FE, 'M', u'ǿ'), + (0x1FE, 'M', 'ǿ'), (0x1FF, 'V'), - (0x200, 'M', u'ȁ'), + (0x200, 'M', 'ȁ'), (0x201, 'V'), - (0x202, 'M', u'ȃ'), + (0x202, 'M', 'ȃ'), (0x203, 'V'), - (0x204, 'M', u'ȅ'), + (0x204, 'M', 'ȅ'), (0x205, 'V'), - (0x206, 'M', u'ȇ'), + (0x206, 'M', 'ȇ'), (0x207, 'V'), - (0x208, 'M', u'ȉ'), + (0x208, 'M', 'ȉ'), (0x209, 'V'), - (0x20A, 'M', u'ȋ'), + (0x20A, 'M', 'ȋ'), (0x20B, 'V'), - (0x20C, 'M', u'ȍ'), + (0x20C, 'M', 'ȍ'), ] def _seg_5(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0x20D, 'V'), - (0x20E, 'M', u'ȏ'), + (0x20E, 'M', 'ȏ'), (0x20F, 'V'), - (0x210, 'M', u'ȑ'), + (0x210, 'M', 'ȑ'), (0x211, 'V'), - (0x212, 'M', u'ȓ'), + (0x212, 'M', 'ȓ'), (0x213, 'V'), - (0x214, 'M', u'ȕ'), + (0x214, 'M', 'ȕ'), (0x215, 'V'), - (0x216, 'M', u'ȗ'), + (0x216, 'M', 'ȗ'), (0x217, 'V'), - (0x218, 'M', u'ș'), + (0x218, 'M', 'ș'), (0x219, 'V'), - (0x21A, 'M', u'ț'), + (0x21A, 'M', 'ț'), (0x21B, 'V'), - (0x21C, 'M', u'ȝ'), + (0x21C, 'M', 'ȝ'), (0x21D, 'V'), - (0x21E, 'M', u'ȟ'), + (0x21E, 'M', 'ȟ'), (0x21F, 'V'), - (0x220, 'M', u'ƞ'), + (0x220, 'M', 'ƞ'), (0x221, 'V'), - (0x222, 'M', u'ȣ'), + (0x222, 'M', 'ȣ'), (0x223, 'V'), - (0x224, 'M', u'ȥ'), + (0x224, 'M', 'ȥ'), (0x225, 'V'), - (0x226, 'M', u'ȧ'), + (0x226, 'M', 'ȧ'), (0x227, 'V'), - (0x228, 'M', u'ȩ'), + (0x228, 'M', 'ȩ'), (0x229, 'V'), - (0x22A, 'M', u'ȫ'), + (0x22A, 'M', 'ȫ'), (0x22B, 'V'), - (0x22C, 'M', u'ȭ'), + (0x22C, 'M', 'ȭ'), (0x22D, 'V'), - (0x22E, 'M', u'ȯ'), + (0x22E, 'M', 'ȯ'), (0x22F, 'V'), - (0x230, 'M', u'ȱ'), + (0x230, 'M', 'ȱ'), (0x231, 'V'), - (0x232, 'M', u'ȳ'), + (0x232, 'M', 'ȳ'), (0x233, 'V'), - (0x23A, 'M', u'ⱥ'), - (0x23B, 'M', u'ȼ'), + (0x23A, 'M', 'ⱥ'), + (0x23B, 'M', 'ȼ'), (0x23C, 'V'), - (0x23D, 'M', u'ƚ'), - (0x23E, 'M', u'ⱦ'), + (0x23D, 'M', 'ƚ'), + (0x23E, 'M', 'ⱦ'), (0x23F, 'V'), - (0x241, 'M', u'ɂ'), + (0x241, 'M', 'ɂ'), (0x242, 'V'), - (0x243, 'M', u'ƀ'), - (0x244, 'M', u'ʉ'), - (0x245, 'M', u'ʌ'), - (0x246, 'M', u'ɇ'), + (0x243, 'M', 'ƀ'), + (0x244, 'M', 'ʉ'), + (0x245, 'M', 'ʌ'), + (0x246, 'M', 'ɇ'), (0x247, 'V'), - (0x248, 'M', u'ɉ'), + (0x248, 'M', 'ɉ'), (0x249, 'V'), - (0x24A, 'M', u'ɋ'), + (0x24A, 'M', 'ɋ'), (0x24B, 'V'), - (0x24C, 'M', u'ɍ'), + (0x24C, 'M', 'ɍ'), (0x24D, 'V'), - (0x24E, 'M', u'ɏ'), + (0x24E, 'M', 'ɏ'), (0x24F, 'V'), - (0x2B0, 'M', u'h'), - (0x2B1, 'M', u'ɦ'), - (0x2B2, 'M', u'j'), - (0x2B3, 'M', u'r'), - (0x2B4, 'M', u'ɹ'), - (0x2B5, 'M', u'ɻ'), - (0x2B6, 'M', u'ʁ'), - (0x2B7, 'M', u'w'), - (0x2B8, 'M', u'y'), + (0x2B0, 'M', 'h'), + (0x2B1, 'M', 'ɦ'), + (0x2B2, 'M', 'j'), + (0x2B3, 'M', 'r'), + (0x2B4, 'M', 'ɹ'), + (0x2B5, 'M', 'ɻ'), + (0x2B6, 'M', 'ʁ'), + (0x2B7, 'M', 'w'), + (0x2B8, 'M', 'y'), (0x2B9, 'V'), - (0x2D8, '3', u' ̆'), - (0x2D9, '3', u' ̇'), - (0x2DA, '3', u' ̊'), - (0x2DB, '3', u' ̨'), - (0x2DC, '3', u' ̃'), - (0x2DD, '3', u' ̋'), + (0x2D8, '3', ' ̆'), + (0x2D9, '3', ' ̇'), + (0x2DA, '3', ' ̊'), + (0x2DB, '3', ' ̨'), + (0x2DC, '3', ' ̃'), + (0x2DD, '3', ' ̋'), (0x2DE, 'V'), - (0x2E0, 'M', u'ɣ'), - (0x2E1, 'M', u'l'), - (0x2E2, 'M', u's'), - (0x2E3, 'M', u'x'), - (0x2E4, 'M', u'ʕ'), + (0x2E0, 'M', 'ɣ'), + (0x2E1, 'M', 'l'), + (0x2E2, 'M', 's'), + (0x2E3, 'M', 'x'), + (0x2E4, 'M', 'ʕ'), (0x2E5, 'V'), - (0x340, 'M', u'̀'), - (0x341, 'M', u'́'), + (0x340, 'M', '̀'), + (0x341, 'M', '́'), (0x342, 'V'), - (0x343, 'M', u'̓'), - (0x344, 'M', u'̈́'), - (0x345, 'M', u'ι'), + (0x343, 'M', '̓'), + (0x344, 'M', '̈́'), + (0x345, 'M', 'ι'), (0x346, 'V'), (0x34F, 'I'), (0x350, 'V'), - (0x370, 'M', u'ͱ'), + (0x370, 'M', 'ͱ'), (0x371, 'V'), - (0x372, 'M', u'ͳ'), + (0x372, 'M', 'ͳ'), (0x373, 'V'), - (0x374, 'M', u'ʹ'), + (0x374, 'M', 'ʹ'), (0x375, 'V'), - (0x376, 'M', u'ͷ'), + (0x376, 'M', 'ͷ'), (0x377, 'V'), ] def _seg_6(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0x378, 'X'), - (0x37A, '3', u' ι'), + (0x37A, '3', ' ι'), (0x37B, 'V'), - (0x37E, '3', u';'), - (0x37F, 'M', u'ϳ'), + (0x37E, '3', ';'), + (0x37F, 'M', 'ϳ'), (0x380, 'X'), - (0x384, '3', u' ́'), - (0x385, '3', u' ̈́'), - (0x386, 'M', u'ά'), - (0x387, 'M', u'·'), - (0x388, 'M', u'έ'), - (0x389, 'M', u'ή'), - (0x38A, 'M', u'ί'), + (0x384, '3', ' ́'), + (0x385, '3', ' ̈́'), + (0x386, 'M', 'ά'), + (0x387, 'M', '·'), + (0x388, 'M', 'έ'), + (0x389, 'M', 'ή'), + (0x38A, 'M', 'ί'), (0x38B, 'X'), - (0x38C, 'M', u'ό'), + (0x38C, 'M', 'ό'), (0x38D, 'X'), - (0x38E, 'M', u'ύ'), - (0x38F, 'M', u'ώ'), + (0x38E, 'M', 'ύ'), + (0x38F, 'M', 'ώ'), (0x390, 'V'), - (0x391, 'M', u'α'), - (0x392, 'M', u'β'), - (0x393, 'M', u'γ'), - (0x394, 'M', u'δ'), - (0x395, 'M', u'ε'), - (0x396, 'M', u'ζ'), - (0x397, 'M', u'η'), - (0x398, 'M', u'θ'), - (0x399, 'M', u'ι'), - (0x39A, 'M', u'κ'), - (0x39B, 'M', u'λ'), - (0x39C, 'M', u'μ'), - (0x39D, 'M', u'ν'), - (0x39E, 'M', u'ξ'), - (0x39F, 'M', u'ο'), - (0x3A0, 'M', u'π'), - (0x3A1, 'M', u'ρ'), + (0x391, 'M', 'α'), + (0x392, 'M', 'β'), + (0x393, 'M', 'γ'), + (0x394, 'M', 'δ'), + (0x395, 'M', 'ε'), + (0x396, 'M', 'ζ'), + (0x397, 'M', 'η'), + (0x398, 'M', 'θ'), + (0x399, 'M', 'ι'), + (0x39A, 'M', 'κ'), + (0x39B, 'M', 'λ'), + (0x39C, 'M', 'μ'), + (0x39D, 'M', 'ν'), + (0x39E, 'M', 'ξ'), + (0x39F, 'M', 'ο'), + (0x3A0, 'M', 'π'), + (0x3A1, 'M', 'ρ'), (0x3A2, 'X'), - (0x3A3, 'M', u'σ'), - (0x3A4, 'M', u'τ'), - (0x3A5, 'M', u'υ'), - (0x3A6, 'M', u'φ'), - (0x3A7, 'M', u'χ'), - (0x3A8, 'M', u'ψ'), - (0x3A9, 'M', u'ω'), - (0x3AA, 'M', u'ϊ'), - (0x3AB, 'M', u'ϋ'), + (0x3A3, 'M', 'σ'), + (0x3A4, 'M', 'τ'), + (0x3A5, 'M', 'υ'), + (0x3A6, 'M', 'φ'), + (0x3A7, 'M', 'χ'), + (0x3A8, 'M', 'ψ'), + (0x3A9, 'M', 'ω'), + (0x3AA, 'M', 'ϊ'), + (0x3AB, 'M', 'ϋ'), (0x3AC, 'V'), - (0x3C2, 'D', u'σ'), + (0x3C2, 'D', 'σ'), (0x3C3, 'V'), - (0x3CF, 'M', u'ϗ'), - (0x3D0, 'M', u'β'), - (0x3D1, 'M', u'θ'), - (0x3D2, 'M', u'υ'), - (0x3D3, 'M', u'ύ'), - (0x3D4, 'M', u'ϋ'), - (0x3D5, 'M', u'φ'), - (0x3D6, 'M', u'π'), + (0x3CF, 'M', 'ϗ'), + (0x3D0, 'M', 'β'), + (0x3D1, 'M', 'θ'), + (0x3D2, 'M', 'υ'), + (0x3D3, 'M', 'ύ'), + (0x3D4, 'M', 'ϋ'), + (0x3D5, 'M', 'φ'), + (0x3D6, 'M', 'π'), (0x3D7, 'V'), - (0x3D8, 'M', u'ϙ'), + (0x3D8, 'M', 'ϙ'), (0x3D9, 'V'), - (0x3DA, 'M', u'ϛ'), + (0x3DA, 'M', 'ϛ'), (0x3DB, 'V'), - (0x3DC, 'M', u'ϝ'), + (0x3DC, 'M', 'ϝ'), (0x3DD, 'V'), - (0x3DE, 'M', u'ϟ'), + (0x3DE, 'M', 'ϟ'), (0x3DF, 'V'), - (0x3E0, 'M', u'ϡ'), + (0x3E0, 'M', 'ϡ'), (0x3E1, 'V'), - (0x3E2, 'M', u'ϣ'), + (0x3E2, 'M', 'ϣ'), (0x3E3, 'V'), - (0x3E4, 'M', u'ϥ'), + (0x3E4, 'M', 'ϥ'), (0x3E5, 'V'), - (0x3E6, 'M', u'ϧ'), + (0x3E6, 'M', 'ϧ'), (0x3E7, 'V'), - (0x3E8, 'M', u'ϩ'), + (0x3E8, 'M', 'ϩ'), (0x3E9, 'V'), - (0x3EA, 'M', u'ϫ'), + (0x3EA, 'M', 'ϫ'), (0x3EB, 'V'), - (0x3EC, 'M', u'ϭ'), + (0x3EC, 'M', 'ϭ'), (0x3ED, 'V'), - (0x3EE, 'M', u'ϯ'), + (0x3EE, 'M', 'ϯ'), (0x3EF, 'V'), - (0x3F0, 'M', u'κ'), - (0x3F1, 'M', u'ρ'), - (0x3F2, 'M', u'σ'), + (0x3F0, 'M', 'κ'), + (0x3F1, 'M', 'ρ'), + (0x3F2, 'M', 'σ'), (0x3F3, 'V'), - (0x3F4, 'M', u'θ'), - (0x3F5, 'M', u'ε'), + (0x3F4, 'M', 'θ'), + (0x3F5, 'M', 'ε'), (0x3F6, 'V'), - (0x3F7, 'M', u'ϸ'), + (0x3F7, 'M', 'ϸ'), (0x3F8, 'V'), - (0x3F9, 'M', u'σ'), - (0x3FA, 'M', u'ϻ'), + (0x3F9, 'M', 'σ'), + (0x3FA, 'M', 'ϻ'), (0x3FB, 'V'), - (0x3FD, 'M', u'ͻ'), - (0x3FE, 'M', u'ͼ'), - (0x3FF, 'M', u'ͽ'), - (0x400, 'M', u'ѐ'), - (0x401, 'M', u'ё'), - (0x402, 'M', u'ђ'), + (0x3FD, 'M', 'ͻ'), + (0x3FE, 'M', 'ͼ'), + (0x3FF, 'M', 'ͽ'), + (0x400, 'M', 'ѐ'), + (0x401, 'M', 'ё'), + (0x402, 'M', 'ђ'), ] def _seg_7(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x403, 'M', u'ѓ'), - (0x404, 'M', u'є'), - (0x405, 'M', u'ѕ'), - (0x406, 'M', u'і'), - (0x407, 'M', u'ї'), - (0x408, 'M', u'ј'), - (0x409, 'M', u'љ'), - (0x40A, 'M', u'њ'), - (0x40B, 'M', u'ћ'), - (0x40C, 'M', u'ќ'), - (0x40D, 'M', u'ѝ'), - (0x40E, 'M', u'ў'), - (0x40F, 'M', u'џ'), - (0x410, 'M', u'а'), - (0x411, 'M', u'б'), - (0x412, 'M', u'в'), - (0x413, 'M', u'г'), - (0x414, 'M', u'д'), - (0x415, 'M', u'е'), - (0x416, 'M', u'ж'), - (0x417, 'M', u'з'), - (0x418, 'M', u'и'), - (0x419, 'M', u'й'), - (0x41A, 'M', u'к'), - (0x41B, 'M', u'л'), - (0x41C, 'M', u'м'), - (0x41D, 'M', u'н'), - (0x41E, 'M', u'о'), - (0x41F, 'M', u'п'), - (0x420, 'M', u'р'), - (0x421, 'M', u'с'), - (0x422, 'M', u'т'), - (0x423, 'M', u'у'), - (0x424, 'M', u'ф'), - (0x425, 'M', u'х'), - (0x426, 'M', u'ц'), - (0x427, 'M', u'ч'), - (0x428, 'M', u'ш'), - (0x429, 'M', u'щ'), - (0x42A, 'M', u'ъ'), - (0x42B, 'M', u'ы'), - (0x42C, 'M', u'ь'), - (0x42D, 'M', u'э'), - (0x42E, 'M', u'ю'), - (0x42F, 'M', u'я'), + (0x403, 'M', 'ѓ'), + (0x404, 'M', 'є'), + (0x405, 'M', 'ѕ'), + (0x406, 'M', 'і'), + (0x407, 'M', 'ї'), + (0x408, 'M', 'ј'), + (0x409, 'M', 'љ'), + (0x40A, 'M', 'њ'), + (0x40B, 'M', 'ћ'), + (0x40C, 'M', 'ќ'), + (0x40D, 'M', 'ѝ'), + (0x40E, 'M', 'ў'), + (0x40F, 'M', 'џ'), + (0x410, 'M', 'а'), + (0x411, 'M', 'б'), + (0x412, 'M', 'в'), + (0x413, 'M', 'г'), + (0x414, 'M', 'д'), + (0x415, 'M', 'е'), + (0x416, 'M', 'ж'), + (0x417, 'M', 'з'), + (0x418, 'M', 'и'), + (0x419, 'M', 'й'), + (0x41A, 'M', 'к'), + (0x41B, 'M', 'л'), + (0x41C, 'M', 'м'), + (0x41D, 'M', 'н'), + (0x41E, 'M', 'о'), + (0x41F, 'M', 'п'), + (0x420, 'M', 'р'), + (0x421, 'M', 'с'), + (0x422, 'M', 'т'), + (0x423, 'M', 'у'), + (0x424, 'M', 'ф'), + (0x425, 'M', 'х'), + (0x426, 'M', 'ц'), + (0x427, 'M', 'ч'), + (0x428, 'M', 'ш'), + (0x429, 'M', 'щ'), + (0x42A, 'M', 'ъ'), + (0x42B, 'M', 'ы'), + (0x42C, 'M', 'ь'), + (0x42D, 'M', 'э'), + (0x42E, 'M', 'ю'), + (0x42F, 'M', 'я'), (0x430, 'V'), - (0x460, 'M', u'ѡ'), + (0x460, 'M', 'ѡ'), (0x461, 'V'), - (0x462, 'M', u'ѣ'), + (0x462, 'M', 'ѣ'), (0x463, 'V'), - (0x464, 'M', u'ѥ'), + (0x464, 'M', 'ѥ'), (0x465, 'V'), - (0x466, 'M', u'ѧ'), + (0x466, 'M', 'ѧ'), (0x467, 'V'), - (0x468, 'M', u'ѩ'), + (0x468, 'M', 'ѩ'), (0x469, 'V'), - (0x46A, 'M', u'ѫ'), + (0x46A, 'M', 'ѫ'), (0x46B, 'V'), - (0x46C, 'M', u'ѭ'), + (0x46C, 'M', 'ѭ'), (0x46D, 'V'), - (0x46E, 'M', u'ѯ'), + (0x46E, 'M', 'ѯ'), (0x46F, 'V'), - (0x470, 'M', u'ѱ'), + (0x470, 'M', 'ѱ'), (0x471, 'V'), - (0x472, 'M', u'ѳ'), + (0x472, 'M', 'ѳ'), (0x473, 'V'), - (0x474, 'M', u'ѵ'), + (0x474, 'M', 'ѵ'), (0x475, 'V'), - (0x476, 'M', u'ѷ'), + (0x476, 'M', 'ѷ'), (0x477, 'V'), - (0x478, 'M', u'ѹ'), + (0x478, 'M', 'ѹ'), (0x479, 'V'), - (0x47A, 'M', u'ѻ'), + (0x47A, 'M', 'ѻ'), (0x47B, 'V'), - (0x47C, 'M', u'ѽ'), + (0x47C, 'M', 'ѽ'), (0x47D, 'V'), - (0x47E, 'M', u'ѿ'), + (0x47E, 'M', 'ѿ'), (0x47F, 'V'), - (0x480, 'M', u'ҁ'), + (0x480, 'M', 'ҁ'), (0x481, 'V'), - (0x48A, 'M', u'ҋ'), + (0x48A, 'M', 'ҋ'), (0x48B, 'V'), - (0x48C, 'M', u'ҍ'), + (0x48C, 'M', 'ҍ'), (0x48D, 'V'), - (0x48E, 'M', u'ҏ'), + (0x48E, 'M', 'ҏ'), (0x48F, 'V'), - (0x490, 'M', u'ґ'), + (0x490, 'M', 'ґ'), (0x491, 'V'), - (0x492, 'M', u'ғ'), + (0x492, 'M', 'ғ'), (0x493, 'V'), - (0x494, 'M', u'ҕ'), + (0x494, 'M', 'ҕ'), (0x495, 'V'), - (0x496, 'M', u'җ'), + (0x496, 'M', 'җ'), (0x497, 'V'), - (0x498, 'M', u'ҙ'), + (0x498, 'M', 'ҙ'), (0x499, 'V'), - (0x49A, 'M', u'қ'), + (0x49A, 'M', 'қ'), (0x49B, 'V'), - (0x49C, 'M', u'ҝ'), + (0x49C, 'M', 'ҝ'), (0x49D, 'V'), ] def _seg_8(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x49E, 'M', u'ҟ'), + (0x49E, 'M', 'ҟ'), (0x49F, 'V'), - (0x4A0, 'M', u'ҡ'), + (0x4A0, 'M', 'ҡ'), (0x4A1, 'V'), - (0x4A2, 'M', u'ң'), + (0x4A2, 'M', 'ң'), (0x4A3, 'V'), - (0x4A4, 'M', u'ҥ'), + (0x4A4, 'M', 'ҥ'), (0x4A5, 'V'), - (0x4A6, 'M', u'ҧ'), + (0x4A6, 'M', 'ҧ'), (0x4A7, 'V'), - (0x4A8, 'M', u'ҩ'), + (0x4A8, 'M', 'ҩ'), (0x4A9, 'V'), - (0x4AA, 'M', u'ҫ'), + (0x4AA, 'M', 'ҫ'), (0x4AB, 'V'), - (0x4AC, 'M', u'ҭ'), + (0x4AC, 'M', 'ҭ'), (0x4AD, 'V'), - (0x4AE, 'M', u'ү'), + (0x4AE, 'M', 'ү'), (0x4AF, 'V'), - (0x4B0, 'M', u'ұ'), + (0x4B0, 'M', 'ұ'), (0x4B1, 'V'), - (0x4B2, 'M', u'ҳ'), + (0x4B2, 'M', 'ҳ'), (0x4B3, 'V'), - (0x4B4, 'M', u'ҵ'), + (0x4B4, 'M', 'ҵ'), (0x4B5, 'V'), - (0x4B6, 'M', u'ҷ'), + (0x4B6, 'M', 'ҷ'), (0x4B7, 'V'), - (0x4B8, 'M', u'ҹ'), + (0x4B8, 'M', 'ҹ'), (0x4B9, 'V'), - (0x4BA, 'M', u'һ'), + (0x4BA, 'M', 'һ'), (0x4BB, 'V'), - (0x4BC, 'M', u'ҽ'), + (0x4BC, 'M', 'ҽ'), (0x4BD, 'V'), - (0x4BE, 'M', u'ҿ'), + (0x4BE, 'M', 'ҿ'), (0x4BF, 'V'), (0x4C0, 'X'), - (0x4C1, 'M', u'ӂ'), + (0x4C1, 'M', 'ӂ'), (0x4C2, 'V'), - (0x4C3, 'M', u'ӄ'), + (0x4C3, 'M', 'ӄ'), (0x4C4, 'V'), - (0x4C5, 'M', u'ӆ'), + (0x4C5, 'M', 'ӆ'), (0x4C6, 'V'), - (0x4C7, 'M', u'ӈ'), + (0x4C7, 'M', 'ӈ'), (0x4C8, 'V'), - (0x4C9, 'M', u'ӊ'), + (0x4C9, 'M', 'ӊ'), (0x4CA, 'V'), - (0x4CB, 'M', u'ӌ'), + (0x4CB, 'M', 'ӌ'), (0x4CC, 'V'), - (0x4CD, 'M', u'ӎ'), + (0x4CD, 'M', 'ӎ'), (0x4CE, 'V'), - (0x4D0, 'M', u'ӑ'), + (0x4D0, 'M', 'ӑ'), (0x4D1, 'V'), - (0x4D2, 'M', u'ӓ'), + (0x4D2, 'M', 'ӓ'), (0x4D3, 'V'), - (0x4D4, 'M', u'ӕ'), + (0x4D4, 'M', 'ӕ'), (0x4D5, 'V'), - (0x4D6, 'M', u'ӗ'), + (0x4D6, 'M', 'ӗ'), (0x4D7, 'V'), - (0x4D8, 'M', u'ә'), + (0x4D8, 'M', 'ә'), (0x4D9, 'V'), - (0x4DA, 'M', u'ӛ'), + (0x4DA, 'M', 'ӛ'), (0x4DB, 'V'), - (0x4DC, 'M', u'ӝ'), + (0x4DC, 'M', 'ӝ'), (0x4DD, 'V'), - (0x4DE, 'M', u'ӟ'), + (0x4DE, 'M', 'ӟ'), (0x4DF, 'V'), - (0x4E0, 'M', u'ӡ'), + (0x4E0, 'M', 'ӡ'), (0x4E1, 'V'), - (0x4E2, 'M', u'ӣ'), + (0x4E2, 'M', 'ӣ'), (0x4E3, 'V'), - (0x4E4, 'M', u'ӥ'), + (0x4E4, 'M', 'ӥ'), (0x4E5, 'V'), - (0x4E6, 'M', u'ӧ'), + (0x4E6, 'M', 'ӧ'), (0x4E7, 'V'), - (0x4E8, 'M', u'ө'), + (0x4E8, 'M', 'ө'), (0x4E9, 'V'), - (0x4EA, 'M', u'ӫ'), + (0x4EA, 'M', 'ӫ'), (0x4EB, 'V'), - (0x4EC, 'M', u'ӭ'), + (0x4EC, 'M', 'ӭ'), (0x4ED, 'V'), - (0x4EE, 'M', u'ӯ'), + (0x4EE, 'M', 'ӯ'), (0x4EF, 'V'), - (0x4F0, 'M', u'ӱ'), + (0x4F0, 'M', 'ӱ'), (0x4F1, 'V'), - (0x4F2, 'M', u'ӳ'), + (0x4F2, 'M', 'ӳ'), (0x4F3, 'V'), - (0x4F4, 'M', u'ӵ'), + (0x4F4, 'M', 'ӵ'), (0x4F5, 'V'), - (0x4F6, 'M', u'ӷ'), + (0x4F6, 'M', 'ӷ'), (0x4F7, 'V'), - (0x4F8, 'M', u'ӹ'), + (0x4F8, 'M', 'ӹ'), (0x4F9, 'V'), - (0x4FA, 'M', u'ӻ'), + (0x4FA, 'M', 'ӻ'), (0x4FB, 'V'), - (0x4FC, 'M', u'ӽ'), + (0x4FC, 'M', 'ӽ'), (0x4FD, 'V'), - (0x4FE, 'M', u'ӿ'), + (0x4FE, 'M', 'ӿ'), (0x4FF, 'V'), - (0x500, 'M', u'ԁ'), + (0x500, 'M', 'ԁ'), (0x501, 'V'), - (0x502, 'M', u'ԃ'), + (0x502, 'M', 'ԃ'), ] def _seg_9(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0x503, 'V'), - (0x504, 'M', u'ԅ'), + (0x504, 'M', 'ԅ'), (0x505, 'V'), - (0x506, 'M', u'ԇ'), + (0x506, 'M', 'ԇ'), (0x507, 'V'), - (0x508, 'M', u'ԉ'), + (0x508, 'M', 'ԉ'), (0x509, 'V'), - (0x50A, 'M', u'ԋ'), + (0x50A, 'M', 'ԋ'), (0x50B, 'V'), - (0x50C, 'M', u'ԍ'), + (0x50C, 'M', 'ԍ'), (0x50D, 'V'), - (0x50E, 'M', u'ԏ'), + (0x50E, 'M', 'ԏ'), (0x50F, 'V'), - (0x510, 'M', u'ԑ'), + (0x510, 'M', 'ԑ'), (0x511, 'V'), - (0x512, 'M', u'ԓ'), + (0x512, 'M', 'ԓ'), (0x513, 'V'), - (0x514, 'M', u'ԕ'), + (0x514, 'M', 'ԕ'), (0x515, 'V'), - (0x516, 'M', u'ԗ'), + (0x516, 'M', 'ԗ'), (0x517, 'V'), - (0x518, 'M', u'ԙ'), + (0x518, 'M', 'ԙ'), (0x519, 'V'), - (0x51A, 'M', u'ԛ'), + (0x51A, 'M', 'ԛ'), (0x51B, 'V'), - (0x51C, 'M', u'ԝ'), + (0x51C, 'M', 'ԝ'), (0x51D, 'V'), - (0x51E, 'M', u'ԟ'), + (0x51E, 'M', 'ԟ'), (0x51F, 'V'), - (0x520, 'M', u'ԡ'), + (0x520, 'M', 'ԡ'), (0x521, 'V'), - (0x522, 'M', u'ԣ'), + (0x522, 'M', 'ԣ'), (0x523, 'V'), - (0x524, 'M', u'ԥ'), + (0x524, 'M', 'ԥ'), (0x525, 'V'), - (0x526, 'M', u'ԧ'), + (0x526, 'M', 'ԧ'), (0x527, 'V'), - (0x528, 'M', u'ԩ'), + (0x528, 'M', 'ԩ'), (0x529, 'V'), - (0x52A, 'M', u'ԫ'), + (0x52A, 'M', 'ԫ'), (0x52B, 'V'), - (0x52C, 'M', u'ԭ'), + (0x52C, 'M', 'ԭ'), (0x52D, 'V'), - (0x52E, 'M', u'ԯ'), + (0x52E, 'M', 'ԯ'), (0x52F, 'V'), (0x530, 'X'), - (0x531, 'M', u'ա'), - (0x532, 'M', u'բ'), - (0x533, 'M', u'գ'), - (0x534, 'M', u'դ'), - (0x535, 'M', u'ե'), - (0x536, 'M', u'զ'), - (0x537, 'M', u'է'), - (0x538, 'M', u'ը'), - (0x539, 'M', u'թ'), - (0x53A, 'M', u'ժ'), - (0x53B, 'M', u'ի'), - (0x53C, 'M', u'լ'), - (0x53D, 'M', u'խ'), - (0x53E, 'M', u'ծ'), - (0x53F, 'M', u'կ'), - (0x540, 'M', u'հ'), - (0x541, 'M', u'ձ'), - (0x542, 'M', u'ղ'), - (0x543, 'M', u'ճ'), - (0x544, 'M', u'մ'), - (0x545, 'M', u'յ'), - (0x546, 'M', u'ն'), - (0x547, 'M', u'շ'), - (0x548, 'M', u'ո'), - (0x549, 'M', u'չ'), - (0x54A, 'M', u'պ'), - (0x54B, 'M', u'ջ'), - (0x54C, 'M', u'ռ'), - (0x54D, 'M', u'ս'), - (0x54E, 'M', u'վ'), - (0x54F, 'M', u'տ'), - (0x550, 'M', u'ր'), - (0x551, 'M', u'ց'), - (0x552, 'M', u'ւ'), - (0x553, 'M', u'փ'), - (0x554, 'M', u'ք'), - (0x555, 'M', u'օ'), - (0x556, 'M', u'ֆ'), + (0x531, 'M', 'ա'), + (0x532, 'M', 'բ'), + (0x533, 'M', 'գ'), + (0x534, 'M', 'դ'), + (0x535, 'M', 'ե'), + (0x536, 'M', 'զ'), + (0x537, 'M', 'է'), + (0x538, 'M', 'ը'), + (0x539, 'M', 'թ'), + (0x53A, 'M', 'ժ'), + (0x53B, 'M', 'ի'), + (0x53C, 'M', 'լ'), + (0x53D, 'M', 'խ'), + (0x53E, 'M', 'ծ'), + (0x53F, 'M', 'կ'), + (0x540, 'M', 'հ'), + (0x541, 'M', 'ձ'), + (0x542, 'M', 'ղ'), + (0x543, 'M', 'ճ'), + (0x544, 'M', 'մ'), + (0x545, 'M', 'յ'), + (0x546, 'M', 'ն'), + (0x547, 'M', 'շ'), + (0x548, 'M', 'ո'), + (0x549, 'M', 'չ'), + (0x54A, 'M', 'պ'), + (0x54B, 'M', 'ջ'), + (0x54C, 'M', 'ռ'), + (0x54D, 'M', 'ս'), + (0x54E, 'M', 'վ'), + (0x54F, 'M', 'տ'), + (0x550, 'M', 'ր'), + (0x551, 'M', 'ց'), + (0x552, 'M', 'ւ'), + (0x553, 'M', 'փ'), + (0x554, 'M', 'ք'), + (0x555, 'M', 'օ'), + (0x556, 'M', 'ֆ'), (0x557, 'X'), (0x559, 'V'), - (0x587, 'M', u'եւ'), + (0x587, 'M', 'եւ'), (0x588, 'V'), (0x58B, 'X'), (0x58D, 'V'), @@ -1046,11 +1057,12 @@ def _seg_9(): ] def _seg_10(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x675, 'M', u'اٴ'), - (0x676, 'M', u'وٴ'), - (0x677, 'M', u'ۇٴ'), - (0x678, 'M', u'يٴ'), + (0x675, 'M', 'اٴ'), + (0x676, 'M', 'وٴ'), + (0x677, 'M', 'ۇٴ'), + (0x678, 'M', 'يٴ'), (0x679, 'V'), (0x6DD, 'X'), (0x6DE, 'V'), @@ -1074,18 +1086,18 @@ def _seg_10(): (0x8A0, 'V'), (0x8B5, 'X'), (0x8B6, 'V'), - (0x8BE, 'X'), + (0x8C8, 'X'), (0x8D3, 'V'), (0x8E2, 'X'), (0x8E3, 'V'), - (0x958, 'M', u'क़'), - (0x959, 'M', u'ख़'), - (0x95A, 'M', u'ग़'), - (0x95B, 'M', u'ज़'), - (0x95C, 'M', u'ड़'), - (0x95D, 'M', u'ढ़'), - (0x95E, 'M', u'फ़'), - (0x95F, 'M', u'य़'), + (0x958, 'M', 'क़'), + (0x959, 'M', 'ख़'), + (0x95A, 'M', 'ग़'), + (0x95B, 'M', 'ज़'), + (0x95C, 'M', 'ड़'), + (0x95D, 'M', 'ढ़'), + (0x95E, 'M', 'फ़'), + (0x95F, 'M', 'य़'), (0x960, 'V'), (0x984, 'X'), (0x985, 'V'), @@ -1108,10 +1120,10 @@ def _seg_10(): (0x9CF, 'X'), (0x9D7, 'V'), (0x9D8, 'X'), - (0x9DC, 'M', u'ড়'), - (0x9DD, 'M', u'ঢ়'), + (0x9DC, 'M', 'ড়'), + (0x9DD, 'M', 'ঢ়'), (0x9DE, 'X'), - (0x9DF, 'M', u'য়'), + (0x9DF, 'M', 'য়'), (0x9E0, 'V'), (0x9E4, 'X'), (0x9E6, 'V'), @@ -1127,10 +1139,10 @@ def _seg_10(): (0xA2A, 'V'), (0xA31, 'X'), (0xA32, 'V'), - (0xA33, 'M', u'ਲ਼'), + (0xA33, 'M', 'ਲ਼'), (0xA34, 'X'), (0xA35, 'V'), - (0xA36, 'M', u'ਸ਼'), + (0xA36, 'M', 'ਸ਼'), (0xA37, 'X'), (0xA38, 'V'), (0xA3A, 'X'), @@ -1144,16 +1156,17 @@ def _seg_10(): (0xA4E, 'X'), (0xA51, 'V'), (0xA52, 'X'), - (0xA59, 'M', u'ਖ਼'), - (0xA5A, 'M', u'ਗ਼'), - (0xA5B, 'M', u'ਜ਼'), + (0xA59, 'M', 'ਖ਼'), + (0xA5A, 'M', 'ਗ਼'), + (0xA5B, 'M', 'ਜ਼'), ] def _seg_11(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0xA5C, 'V'), (0xA5D, 'X'), - (0xA5E, 'M', u'ਫ਼'), + (0xA5E, 'M', 'ਫ਼'), (0xA5F, 'X'), (0xA66, 'V'), (0xA77, 'X'), @@ -1205,10 +1218,10 @@ def _seg_11(): (0xB49, 'X'), (0xB4B, 'V'), (0xB4E, 'X'), - (0xB56, 'V'), + (0xB55, 'V'), (0xB58, 'X'), - (0xB5C, 'M', u'ଡ଼'), - (0xB5D, 'M', u'ଢ଼'), + (0xB5C, 'M', 'ଡ଼'), + (0xB5D, 'M', 'ଢ଼'), (0xB5E, 'X'), (0xB5F, 'V'), (0xB64, 'X'), @@ -1254,6 +1267,7 @@ def _seg_11(): ] def _seg_12(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ (0xC29, 'X'), (0xC2A, 'V'), @@ -1299,8 +1313,6 @@ def _seg_12(): (0xCF1, 'V'), (0xCF3, 'X'), (0xD00, 'V'), - (0xD04, 'X'), - (0xD05, 'V'), (0xD0D, 'X'), (0xD0E, 'V'), (0xD11, 'X'), @@ -1314,7 +1326,7 @@ def _seg_12(): (0xD64, 'X'), (0xD66, 'V'), (0xD80, 'X'), - (0xD82, 'V'), + (0xD81, 'V'), (0xD84, 'X'), (0xD85, 'V'), (0xD97, 'X'), @@ -1339,7 +1351,7 @@ def _seg_12(): (0xDF2, 'V'), (0xDF5, 'X'), (0xE01, 'V'), - (0xE33, 'M', u'ํา'), + (0xE33, 'M', 'ํา'), (0xE34, 'V'), (0xE3B, 'X'), (0xE3F, 'V'), @@ -1355,12 +1367,13 @@ def _seg_12(): (0xEA5, 'V'), (0xEA6, 'X'), (0xEA7, 'V'), + (0xEB3, 'M', 'ໍາ'), + (0xEB4, 'V'), ] def _seg_13(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xEB3, 'M', u'ໍາ'), - (0xEB4, 'V'), (0xEBE, 'X'), (0xEC0, 'V'), (0xEC5, 'X'), @@ -1370,52 +1383,52 @@ def _seg_13(): (0xECE, 'X'), (0xED0, 'V'), (0xEDA, 'X'), - (0xEDC, 'M', u'ຫນ'), - (0xEDD, 'M', u'ຫມ'), + (0xEDC, 'M', 'ຫນ'), + (0xEDD, 'M', 'ຫມ'), (0xEDE, 'V'), (0xEE0, 'X'), (0xF00, 'V'), - (0xF0C, 'M', u'་'), + (0xF0C, 'M', '་'), (0xF0D, 'V'), - (0xF43, 'M', u'གྷ'), + (0xF43, 'M', 'གྷ'), (0xF44, 'V'), (0xF48, 'X'), (0xF49, 'V'), - (0xF4D, 'M', u'ཌྷ'), + (0xF4D, 'M', 'ཌྷ'), (0xF4E, 'V'), - (0xF52, 'M', u'དྷ'), + (0xF52, 'M', 'དྷ'), (0xF53, 'V'), - (0xF57, 'M', u'བྷ'), + (0xF57, 'M', 'བྷ'), (0xF58, 'V'), - (0xF5C, 'M', u'ཛྷ'), + (0xF5C, 'M', 'ཛྷ'), (0xF5D, 'V'), - (0xF69, 'M', u'ཀྵ'), + (0xF69, 'M', 'ཀྵ'), (0xF6A, 'V'), (0xF6D, 'X'), (0xF71, 'V'), - (0xF73, 'M', u'ཱི'), + (0xF73, 'M', 'ཱི'), (0xF74, 'V'), - (0xF75, 'M', u'ཱུ'), - (0xF76, 'M', u'ྲྀ'), - (0xF77, 'M', u'ྲཱྀ'), - (0xF78, 'M', u'ླྀ'), - (0xF79, 'M', u'ླཱྀ'), + (0xF75, 'M', 'ཱུ'), + (0xF76, 'M', 'ྲྀ'), + (0xF77, 'M', 'ྲཱྀ'), + (0xF78, 'M', 'ླྀ'), + (0xF79, 'M', 'ླཱྀ'), (0xF7A, 'V'), - (0xF81, 'M', u'ཱྀ'), + (0xF81, 'M', 'ཱྀ'), (0xF82, 'V'), - (0xF93, 'M', u'ྒྷ'), + (0xF93, 'M', 'ྒྷ'), (0xF94, 'V'), (0xF98, 'X'), (0xF99, 'V'), - (0xF9D, 'M', u'ྜྷ'), + (0xF9D, 'M', 'ྜྷ'), (0xF9E, 'V'), - (0xFA2, 'M', u'ྡྷ'), + (0xFA2, 'M', 'ྡྷ'), (0xFA3, 'V'), - (0xFA7, 'M', u'ྦྷ'), + (0xFA7, 'M', 'ྦྷ'), (0xFA8, 'V'), - (0xFAC, 'M', u'ྫྷ'), + (0xFAC, 'M', 'ྫྷ'), (0xFAD, 'V'), - (0xFB9, 'M', u'ྐྵ'), + (0xFB9, 'M', 'ྐྵ'), (0xFBA, 'V'), (0xFBD, 'X'), (0xFBE, 'V'), @@ -1424,12 +1437,12 @@ def _seg_13(): (0xFDB, 'X'), (0x1000, 'V'), (0x10A0, 'X'), - (0x10C7, 'M', u'ⴧ'), + (0x10C7, 'M', 'ⴧ'), (0x10C8, 'X'), - (0x10CD, 'M', u'ⴭ'), + (0x10CD, 'M', 'ⴭ'), (0x10CE, 'X'), (0x10D0, 'V'), - (0x10FC, 'M', u'ნ'), + (0x10FC, 'M', 'ნ'), (0x10FD, 'V'), (0x115F, 'X'), (0x1161, 'V'), @@ -1459,12 +1472,13 @@ def _seg_13(): (0x12C8, 'V'), (0x12D7, 'X'), (0x12D8, 'V'), + (0x1311, 'X'), + (0x1312, 'V'), ] def _seg_14(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1311, 'X'), - (0x1312, 'V'), (0x1316, 'X'), (0x1318, 'V'), (0x135B, 'X'), @@ -1474,12 +1488,12 @@ def _seg_14(): (0x139A, 'X'), (0x13A0, 'V'), (0x13F6, 'X'), - (0x13F8, 'M', u'Ᏸ'), - (0x13F9, 'M', u'Ᏹ'), - (0x13FA, 'M', u'Ᏺ'), - (0x13FB, 'M', u'Ᏻ'), - (0x13FC, 'M', u'Ᏼ'), - (0x13FD, 'M', u'Ᏽ'), + (0x13F8, 'M', 'Ᏸ'), + (0x13F9, 'M', 'Ᏹ'), + (0x13FA, 'M', 'Ᏺ'), + (0x13FB, 'M', 'Ᏻ'), + (0x13FC, 'M', 'Ᏼ'), + (0x13FD, 'M', 'Ᏽ'), (0x13FE, 'X'), (0x1400, 'V'), (0x1680, 'X'), @@ -1553,7 +1567,7 @@ def _seg_14(): (0x1AA0, 'V'), (0x1AAE, 'X'), (0x1AB0, 'V'), - (0x1ABF, 'X'), + (0x1AC1, 'X'), (0x1B00, 'V'), (0x1B4C, 'X'), (0x1B50, 'V'), @@ -1563,1198 +1577,1210 @@ def _seg_14(): (0x1BFC, 'V'), (0x1C38, 'X'), (0x1C3B, 'V'), + (0x1C4A, 'X'), + (0x1C4D, 'V'), ] def _seg_15(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1C4A, 'X'), - (0x1C4D, 'V'), - (0x1C80, 'M', u'в'), - (0x1C81, 'M', u'д'), - (0x1C82, 'M', u'о'), - (0x1C83, 'M', u'с'), - (0x1C84, 'M', u'т'), - (0x1C86, 'M', u'ъ'), - (0x1C87, 'M', u'ѣ'), - (0x1C88, 'M', u'ꙋ'), + (0x1C80, 'M', 'в'), + (0x1C81, 'M', 'д'), + (0x1C82, 'M', 'о'), + (0x1C83, 'M', 'с'), + (0x1C84, 'M', 'т'), + (0x1C86, 'M', 'ъ'), + (0x1C87, 'M', 'ѣ'), + (0x1C88, 'M', 'ꙋ'), (0x1C89, 'X'), - (0x1C90, 'M', u'ა'), - (0x1C91, 'M', u'ბ'), - (0x1C92, 'M', u'გ'), - (0x1C93, 'M', u'დ'), - (0x1C94, 'M', u'ე'), - (0x1C95, 'M', u'ვ'), - (0x1C96, 'M', u'ზ'), - (0x1C97, 'M', u'თ'), - (0x1C98, 'M', u'ი'), - (0x1C99, 'M', u'კ'), - (0x1C9A, 'M', u'ლ'), - (0x1C9B, 'M', u'მ'), - (0x1C9C, 'M', u'ნ'), - (0x1C9D, 'M', u'ო'), - (0x1C9E, 'M', u'პ'), - (0x1C9F, 'M', u'ჟ'), - (0x1CA0, 'M', u'რ'), - (0x1CA1, 'M', u'ს'), - (0x1CA2, 'M', u'ტ'), - (0x1CA3, 'M', u'უ'), - (0x1CA4, 'M', u'ფ'), - (0x1CA5, 'M', u'ქ'), - (0x1CA6, 'M', u'ღ'), - (0x1CA7, 'M', u'ყ'), - (0x1CA8, 'M', u'შ'), - (0x1CA9, 'M', u'ჩ'), - (0x1CAA, 'M', u'ც'), - (0x1CAB, 'M', u'ძ'), - (0x1CAC, 'M', u'წ'), - (0x1CAD, 'M', u'ჭ'), - (0x1CAE, 'M', u'ხ'), - (0x1CAF, 'M', u'ჯ'), - (0x1CB0, 'M', u'ჰ'), - (0x1CB1, 'M', u'ჱ'), - (0x1CB2, 'M', u'ჲ'), - (0x1CB3, 'M', u'ჳ'), - (0x1CB4, 'M', u'ჴ'), - (0x1CB5, 'M', u'ჵ'), - (0x1CB6, 'M', u'ჶ'), - (0x1CB7, 'M', u'ჷ'), - (0x1CB8, 'M', u'ჸ'), - (0x1CB9, 'M', u'ჹ'), - (0x1CBA, 'M', u'ჺ'), + (0x1C90, 'M', 'ა'), + (0x1C91, 'M', 'ბ'), + (0x1C92, 'M', 'გ'), + (0x1C93, 'M', 'დ'), + (0x1C94, 'M', 'ე'), + (0x1C95, 'M', 'ვ'), + (0x1C96, 'M', 'ზ'), + (0x1C97, 'M', 'თ'), + (0x1C98, 'M', 'ი'), + (0x1C99, 'M', 'კ'), + (0x1C9A, 'M', 'ლ'), + (0x1C9B, 'M', 'მ'), + (0x1C9C, 'M', 'ნ'), + (0x1C9D, 'M', 'ო'), + (0x1C9E, 'M', 'პ'), + (0x1C9F, 'M', 'ჟ'), + (0x1CA0, 'M', 'რ'), + (0x1CA1, 'M', 'ს'), + (0x1CA2, 'M', 'ტ'), + (0x1CA3, 'M', 'უ'), + (0x1CA4, 'M', 'ფ'), + (0x1CA5, 'M', 'ქ'), + (0x1CA6, 'M', 'ღ'), + (0x1CA7, 'M', 'ყ'), + (0x1CA8, 'M', 'შ'), + (0x1CA9, 'M', 'ჩ'), + (0x1CAA, 'M', 'ც'), + (0x1CAB, 'M', 'ძ'), + (0x1CAC, 'M', 'წ'), + (0x1CAD, 'M', 'ჭ'), + (0x1CAE, 'M', 'ხ'), + (0x1CAF, 'M', 'ჯ'), + (0x1CB0, 'M', 'ჰ'), + (0x1CB1, 'M', 'ჱ'), + (0x1CB2, 'M', 'ჲ'), + (0x1CB3, 'M', 'ჳ'), + (0x1CB4, 'M', 'ჴ'), + (0x1CB5, 'M', 'ჵ'), + (0x1CB6, 'M', 'ჶ'), + (0x1CB7, 'M', 'ჷ'), + (0x1CB8, 'M', 'ჸ'), + (0x1CB9, 'M', 'ჹ'), + (0x1CBA, 'M', 'ჺ'), (0x1CBB, 'X'), - (0x1CBD, 'M', u'ჽ'), - (0x1CBE, 'M', u'ჾ'), - (0x1CBF, 'M', u'ჿ'), + (0x1CBD, 'M', 'ჽ'), + (0x1CBE, 'M', 'ჾ'), + (0x1CBF, 'M', 'ჿ'), (0x1CC0, 'V'), (0x1CC8, 'X'), (0x1CD0, 'V'), (0x1CFB, 'X'), (0x1D00, 'V'), - (0x1D2C, 'M', u'a'), - (0x1D2D, 'M', u'æ'), - (0x1D2E, 'M', u'b'), + (0x1D2C, 'M', 'a'), + (0x1D2D, 'M', 'æ'), + (0x1D2E, 'M', 'b'), (0x1D2F, 'V'), - (0x1D30, 'M', u'd'), - (0x1D31, 'M', u'e'), - (0x1D32, 'M', u'ǝ'), - (0x1D33, 'M', u'g'), - (0x1D34, 'M', u'h'), - (0x1D35, 'M', u'i'), - (0x1D36, 'M', u'j'), - (0x1D37, 'M', u'k'), - (0x1D38, 'M', u'l'), - (0x1D39, 'M', u'm'), - (0x1D3A, 'M', u'n'), + (0x1D30, 'M', 'd'), + (0x1D31, 'M', 'e'), + (0x1D32, 'M', 'ǝ'), + (0x1D33, 'M', 'g'), + (0x1D34, 'M', 'h'), + (0x1D35, 'M', 'i'), + (0x1D36, 'M', 'j'), + (0x1D37, 'M', 'k'), + (0x1D38, 'M', 'l'), + (0x1D39, 'M', 'm'), + (0x1D3A, 'M', 'n'), (0x1D3B, 'V'), - (0x1D3C, 'M', u'o'), - (0x1D3D, 'M', u'ȣ'), - (0x1D3E, 'M', u'p'), - (0x1D3F, 'M', u'r'), - (0x1D40, 'M', u't'), - (0x1D41, 'M', u'u'), - (0x1D42, 'M', u'w'), - (0x1D43, 'M', u'a'), - (0x1D44, 'M', u'ɐ'), - (0x1D45, 'M', u'ɑ'), - (0x1D46, 'M', u'ᴂ'), - (0x1D47, 'M', u'b'), - (0x1D48, 'M', u'd'), - (0x1D49, 'M', u'e'), - (0x1D4A, 'M', u'ə'), - (0x1D4B, 'M', u'ɛ'), - (0x1D4C, 'M', u'ɜ'), - (0x1D4D, 'M', u'g'), + (0x1D3C, 'M', 'o'), + (0x1D3D, 'M', 'ȣ'), + (0x1D3E, 'M', 'p'), + (0x1D3F, 'M', 'r'), + (0x1D40, 'M', 't'), + (0x1D41, 'M', 'u'), + (0x1D42, 'M', 'w'), + (0x1D43, 'M', 'a'), + (0x1D44, 'M', 'ɐ'), + (0x1D45, 'M', 'ɑ'), + (0x1D46, 'M', 'ᴂ'), + (0x1D47, 'M', 'b'), + (0x1D48, 'M', 'd'), + (0x1D49, 'M', 'e'), + (0x1D4A, 'M', 'ə'), + (0x1D4B, 'M', 'ɛ'), + (0x1D4C, 'M', 'ɜ'), + (0x1D4D, 'M', 'g'), (0x1D4E, 'V'), - (0x1D4F, 'M', u'k'), - (0x1D50, 'M', u'm'), + (0x1D4F, 'M', 'k'), + (0x1D50, 'M', 'm'), + (0x1D51, 'M', 'ŋ'), + (0x1D52, 'M', 'o'), ] def _seg_16(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D51, 'M', u'ŋ'), - (0x1D52, 'M', u'o'), - (0x1D53, 'M', u'ɔ'), - (0x1D54, 'M', u'ᴖ'), - (0x1D55, 'M', u'ᴗ'), - (0x1D56, 'M', u'p'), - (0x1D57, 'M', u't'), - (0x1D58, 'M', u'u'), - (0x1D59, 'M', u'ᴝ'), - (0x1D5A, 'M', u'ɯ'), - (0x1D5B, 'M', u'v'), - (0x1D5C, 'M', u'ᴥ'), - (0x1D5D, 'M', u'β'), - (0x1D5E, 'M', u'γ'), - (0x1D5F, 'M', u'δ'), - (0x1D60, 'M', u'φ'), - (0x1D61, 'M', u'χ'), - (0x1D62, 'M', u'i'), - (0x1D63, 'M', u'r'), - (0x1D64, 'M', u'u'), - (0x1D65, 'M', u'v'), - (0x1D66, 'M', u'β'), - (0x1D67, 'M', u'γ'), - (0x1D68, 'M', u'ρ'), - (0x1D69, 'M', u'φ'), - (0x1D6A, 'M', u'χ'), + (0x1D53, 'M', 'ɔ'), + (0x1D54, 'M', 'ᴖ'), + (0x1D55, 'M', 'ᴗ'), + (0x1D56, 'M', 'p'), + (0x1D57, 'M', 't'), + (0x1D58, 'M', 'u'), + (0x1D59, 'M', 'ᴝ'), + (0x1D5A, 'M', 'ɯ'), + (0x1D5B, 'M', 'v'), + (0x1D5C, 'M', 'ᴥ'), + (0x1D5D, 'M', 'β'), + (0x1D5E, 'M', 'γ'), + (0x1D5F, 'M', 'δ'), + (0x1D60, 'M', 'φ'), + (0x1D61, 'M', 'χ'), + (0x1D62, 'M', 'i'), + (0x1D63, 'M', 'r'), + (0x1D64, 'M', 'u'), + (0x1D65, 'M', 'v'), + (0x1D66, 'M', 'β'), + (0x1D67, 'M', 'γ'), + (0x1D68, 'M', 'ρ'), + (0x1D69, 'M', 'φ'), + (0x1D6A, 'M', 'χ'), (0x1D6B, 'V'), - (0x1D78, 'M', u'н'), + (0x1D78, 'M', 'н'), (0x1D79, 'V'), - (0x1D9B, 'M', u'ɒ'), - (0x1D9C, 'M', u'c'), - (0x1D9D, 'M', u'ɕ'), - (0x1D9E, 'M', u'ð'), - (0x1D9F, 'M', u'ɜ'), - (0x1DA0, 'M', u'f'), - (0x1DA1, 'M', u'ɟ'), - (0x1DA2, 'M', u'ɡ'), - (0x1DA3, 'M', u'ɥ'), - (0x1DA4, 'M', u'ɨ'), - (0x1DA5, 'M', u'ɩ'), - (0x1DA6, 'M', u'ɪ'), - (0x1DA7, 'M', u'ᵻ'), - (0x1DA8, 'M', u'ʝ'), - (0x1DA9, 'M', u'ɭ'), - (0x1DAA, 'M', u'ᶅ'), - (0x1DAB, 'M', u'ʟ'), - (0x1DAC, 'M', u'ɱ'), - (0x1DAD, 'M', u'ɰ'), - (0x1DAE, 'M', u'ɲ'), - (0x1DAF, 'M', u'ɳ'), - (0x1DB0, 'M', u'ɴ'), - (0x1DB1, 'M', u'ɵ'), - (0x1DB2, 'M', u'ɸ'), - (0x1DB3, 'M', u'ʂ'), - (0x1DB4, 'M', u'ʃ'), - (0x1DB5, 'M', u'ƫ'), - (0x1DB6, 'M', u'ʉ'), - (0x1DB7, 'M', u'ʊ'), - (0x1DB8, 'M', u'ᴜ'), - (0x1DB9, 'M', u'ʋ'), - (0x1DBA, 'M', u'ʌ'), - (0x1DBB, 'M', u'z'), - (0x1DBC, 'M', u'ʐ'), - (0x1DBD, 'M', u'ʑ'), - (0x1DBE, 'M', u'ʒ'), - (0x1DBF, 'M', u'θ'), + (0x1D9B, 'M', 'ɒ'), + (0x1D9C, 'M', 'c'), + (0x1D9D, 'M', 'ɕ'), + (0x1D9E, 'M', 'ð'), + (0x1D9F, 'M', 'ɜ'), + (0x1DA0, 'M', 'f'), + (0x1DA1, 'M', 'ɟ'), + (0x1DA2, 'M', 'ɡ'), + (0x1DA3, 'M', 'ɥ'), + (0x1DA4, 'M', 'ɨ'), + (0x1DA5, 'M', 'ɩ'), + (0x1DA6, 'M', 'ɪ'), + (0x1DA7, 'M', 'ᵻ'), + (0x1DA8, 'M', 'ʝ'), + (0x1DA9, 'M', 'ɭ'), + (0x1DAA, 'M', 'ᶅ'), + (0x1DAB, 'M', 'ʟ'), + (0x1DAC, 'M', 'ɱ'), + (0x1DAD, 'M', 'ɰ'), + (0x1DAE, 'M', 'ɲ'), + (0x1DAF, 'M', 'ɳ'), + (0x1DB0, 'M', 'ɴ'), + (0x1DB1, 'M', 'ɵ'), + (0x1DB2, 'M', 'ɸ'), + (0x1DB3, 'M', 'ʂ'), + (0x1DB4, 'M', 'ʃ'), + (0x1DB5, 'M', 'ƫ'), + (0x1DB6, 'M', 'ʉ'), + (0x1DB7, 'M', 'ʊ'), + (0x1DB8, 'M', 'ᴜ'), + (0x1DB9, 'M', 'ʋ'), + (0x1DBA, 'M', 'ʌ'), + (0x1DBB, 'M', 'z'), + (0x1DBC, 'M', 'ʐ'), + (0x1DBD, 'M', 'ʑ'), + (0x1DBE, 'M', 'ʒ'), + (0x1DBF, 'M', 'θ'), (0x1DC0, 'V'), (0x1DFA, 'X'), (0x1DFB, 'V'), - (0x1E00, 'M', u'ḁ'), + (0x1E00, 'M', 'ḁ'), (0x1E01, 'V'), - (0x1E02, 'M', u'ḃ'), + (0x1E02, 'M', 'ḃ'), (0x1E03, 'V'), - (0x1E04, 'M', u'ḅ'), + (0x1E04, 'M', 'ḅ'), (0x1E05, 'V'), - (0x1E06, 'M', u'ḇ'), + (0x1E06, 'M', 'ḇ'), (0x1E07, 'V'), - (0x1E08, 'M', u'ḉ'), + (0x1E08, 'M', 'ḉ'), (0x1E09, 'V'), - (0x1E0A, 'M', u'ḋ'), + (0x1E0A, 'M', 'ḋ'), (0x1E0B, 'V'), - (0x1E0C, 'M', u'ḍ'), + (0x1E0C, 'M', 'ḍ'), (0x1E0D, 'V'), - (0x1E0E, 'M', u'ḏ'), + (0x1E0E, 'M', 'ḏ'), (0x1E0F, 'V'), - (0x1E10, 'M', u'ḑ'), + (0x1E10, 'M', 'ḑ'), (0x1E11, 'V'), - (0x1E12, 'M', u'ḓ'), + (0x1E12, 'M', 'ḓ'), (0x1E13, 'V'), - (0x1E14, 'M', u'ḕ'), + (0x1E14, 'M', 'ḕ'), (0x1E15, 'V'), - (0x1E16, 'M', u'ḗ'), + (0x1E16, 'M', 'ḗ'), (0x1E17, 'V'), - (0x1E18, 'M', u'ḙ'), + (0x1E18, 'M', 'ḙ'), (0x1E19, 'V'), - (0x1E1A, 'M', u'ḛ'), + (0x1E1A, 'M', 'ḛ'), (0x1E1B, 'V'), - (0x1E1C, 'M', u'ḝ'), + (0x1E1C, 'M', 'ḝ'), (0x1E1D, 'V'), - (0x1E1E, 'M', u'ḟ'), + (0x1E1E, 'M', 'ḟ'), + (0x1E1F, 'V'), + (0x1E20, 'M', 'ḡ'), ] def _seg_17(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1E1F, 'V'), - (0x1E20, 'M', u'ḡ'), (0x1E21, 'V'), - (0x1E22, 'M', u'ḣ'), + (0x1E22, 'M', 'ḣ'), (0x1E23, 'V'), - (0x1E24, 'M', u'ḥ'), + (0x1E24, 'M', 'ḥ'), (0x1E25, 'V'), - (0x1E26, 'M', u'ḧ'), + (0x1E26, 'M', 'ḧ'), (0x1E27, 'V'), - (0x1E28, 'M', u'ḩ'), + (0x1E28, 'M', 'ḩ'), (0x1E29, 'V'), - (0x1E2A, 'M', u'ḫ'), + (0x1E2A, 'M', 'ḫ'), (0x1E2B, 'V'), - (0x1E2C, 'M', u'ḭ'), + (0x1E2C, 'M', 'ḭ'), (0x1E2D, 'V'), - (0x1E2E, 'M', u'ḯ'), + (0x1E2E, 'M', 'ḯ'), (0x1E2F, 'V'), - (0x1E30, 'M', u'ḱ'), + (0x1E30, 'M', 'ḱ'), (0x1E31, 'V'), - (0x1E32, 'M', u'ḳ'), + (0x1E32, 'M', 'ḳ'), (0x1E33, 'V'), - (0x1E34, 'M', u'ḵ'), + (0x1E34, 'M', 'ḵ'), (0x1E35, 'V'), - (0x1E36, 'M', u'ḷ'), + (0x1E36, 'M', 'ḷ'), (0x1E37, 'V'), - (0x1E38, 'M', u'ḹ'), + (0x1E38, 'M', 'ḹ'), (0x1E39, 'V'), - (0x1E3A, 'M', u'ḻ'), + (0x1E3A, 'M', 'ḻ'), (0x1E3B, 'V'), - (0x1E3C, 'M', u'ḽ'), + (0x1E3C, 'M', 'ḽ'), (0x1E3D, 'V'), - (0x1E3E, 'M', u'ḿ'), + (0x1E3E, 'M', 'ḿ'), (0x1E3F, 'V'), - (0x1E40, 'M', u'ṁ'), + (0x1E40, 'M', 'ṁ'), (0x1E41, 'V'), - (0x1E42, 'M', u'ṃ'), + (0x1E42, 'M', 'ṃ'), (0x1E43, 'V'), - (0x1E44, 'M', u'ṅ'), + (0x1E44, 'M', 'ṅ'), (0x1E45, 'V'), - (0x1E46, 'M', u'ṇ'), + (0x1E46, 'M', 'ṇ'), (0x1E47, 'V'), - (0x1E48, 'M', u'ṉ'), + (0x1E48, 'M', 'ṉ'), (0x1E49, 'V'), - (0x1E4A, 'M', u'ṋ'), + (0x1E4A, 'M', 'ṋ'), (0x1E4B, 'V'), - (0x1E4C, 'M', u'ṍ'), + (0x1E4C, 'M', 'ṍ'), (0x1E4D, 'V'), - (0x1E4E, 'M', u'ṏ'), + (0x1E4E, 'M', 'ṏ'), (0x1E4F, 'V'), - (0x1E50, 'M', u'ṑ'), + (0x1E50, 'M', 'ṑ'), (0x1E51, 'V'), - (0x1E52, 'M', u'ṓ'), + (0x1E52, 'M', 'ṓ'), (0x1E53, 'V'), - (0x1E54, 'M', u'ṕ'), + (0x1E54, 'M', 'ṕ'), (0x1E55, 'V'), - (0x1E56, 'M', u'ṗ'), + (0x1E56, 'M', 'ṗ'), (0x1E57, 'V'), - (0x1E58, 'M', u'ṙ'), + (0x1E58, 'M', 'ṙ'), (0x1E59, 'V'), - (0x1E5A, 'M', u'ṛ'), + (0x1E5A, 'M', 'ṛ'), (0x1E5B, 'V'), - (0x1E5C, 'M', u'ṝ'), + (0x1E5C, 'M', 'ṝ'), (0x1E5D, 'V'), - (0x1E5E, 'M', u'ṟ'), + (0x1E5E, 'M', 'ṟ'), (0x1E5F, 'V'), - (0x1E60, 'M', u'ṡ'), + (0x1E60, 'M', 'ṡ'), (0x1E61, 'V'), - (0x1E62, 'M', u'ṣ'), + (0x1E62, 'M', 'ṣ'), (0x1E63, 'V'), - (0x1E64, 'M', u'ṥ'), + (0x1E64, 'M', 'ṥ'), (0x1E65, 'V'), - (0x1E66, 'M', u'ṧ'), + (0x1E66, 'M', 'ṧ'), (0x1E67, 'V'), - (0x1E68, 'M', u'ṩ'), + (0x1E68, 'M', 'ṩ'), (0x1E69, 'V'), - (0x1E6A, 'M', u'ṫ'), + (0x1E6A, 'M', 'ṫ'), (0x1E6B, 'V'), - (0x1E6C, 'M', u'ṭ'), + (0x1E6C, 'M', 'ṭ'), (0x1E6D, 'V'), - (0x1E6E, 'M', u'ṯ'), + (0x1E6E, 'M', 'ṯ'), (0x1E6F, 'V'), - (0x1E70, 'M', u'ṱ'), + (0x1E70, 'M', 'ṱ'), (0x1E71, 'V'), - (0x1E72, 'M', u'ṳ'), + (0x1E72, 'M', 'ṳ'), (0x1E73, 'V'), - (0x1E74, 'M', u'ṵ'), + (0x1E74, 'M', 'ṵ'), (0x1E75, 'V'), - (0x1E76, 'M', u'ṷ'), + (0x1E76, 'M', 'ṷ'), (0x1E77, 'V'), - (0x1E78, 'M', u'ṹ'), + (0x1E78, 'M', 'ṹ'), (0x1E79, 'V'), - (0x1E7A, 'M', u'ṻ'), + (0x1E7A, 'M', 'ṻ'), (0x1E7B, 'V'), - (0x1E7C, 'M', u'ṽ'), + (0x1E7C, 'M', 'ṽ'), (0x1E7D, 'V'), - (0x1E7E, 'M', u'ṿ'), + (0x1E7E, 'M', 'ṿ'), (0x1E7F, 'V'), - (0x1E80, 'M', u'ẁ'), + (0x1E80, 'M', 'ẁ'), (0x1E81, 'V'), - (0x1E82, 'M', u'ẃ'), + (0x1E82, 'M', 'ẃ'), + (0x1E83, 'V'), + (0x1E84, 'M', 'ẅ'), ] def _seg_18(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1E83, 'V'), - (0x1E84, 'M', u'ẅ'), (0x1E85, 'V'), - (0x1E86, 'M', u'ẇ'), + (0x1E86, 'M', 'ẇ'), (0x1E87, 'V'), - (0x1E88, 'M', u'ẉ'), + (0x1E88, 'M', 'ẉ'), (0x1E89, 'V'), - (0x1E8A, 'M', u'ẋ'), + (0x1E8A, 'M', 'ẋ'), (0x1E8B, 'V'), - (0x1E8C, 'M', u'ẍ'), + (0x1E8C, 'M', 'ẍ'), (0x1E8D, 'V'), - (0x1E8E, 'M', u'ẏ'), + (0x1E8E, 'M', 'ẏ'), (0x1E8F, 'V'), - (0x1E90, 'M', u'ẑ'), + (0x1E90, 'M', 'ẑ'), (0x1E91, 'V'), - (0x1E92, 'M', u'ẓ'), + (0x1E92, 'M', 'ẓ'), (0x1E93, 'V'), - (0x1E94, 'M', u'ẕ'), + (0x1E94, 'M', 'ẕ'), (0x1E95, 'V'), - (0x1E9A, 'M', u'aʾ'), - (0x1E9B, 'M', u'ṡ'), + (0x1E9A, 'M', 'aʾ'), + (0x1E9B, 'M', 'ṡ'), (0x1E9C, 'V'), - (0x1E9E, 'M', u'ss'), + (0x1E9E, 'M', 'ss'), (0x1E9F, 'V'), - (0x1EA0, 'M', u'ạ'), + (0x1EA0, 'M', 'ạ'), (0x1EA1, 'V'), - (0x1EA2, 'M', u'ả'), + (0x1EA2, 'M', 'ả'), (0x1EA3, 'V'), - (0x1EA4, 'M', u'ấ'), + (0x1EA4, 'M', 'ấ'), (0x1EA5, 'V'), - (0x1EA6, 'M', u'ầ'), + (0x1EA6, 'M', 'ầ'), (0x1EA7, 'V'), - (0x1EA8, 'M', u'ẩ'), + (0x1EA8, 'M', 'ẩ'), (0x1EA9, 'V'), - (0x1EAA, 'M', u'ẫ'), + (0x1EAA, 'M', 'ẫ'), (0x1EAB, 'V'), - (0x1EAC, 'M', u'ậ'), + (0x1EAC, 'M', 'ậ'), (0x1EAD, 'V'), - (0x1EAE, 'M', u'ắ'), + (0x1EAE, 'M', 'ắ'), (0x1EAF, 'V'), - (0x1EB0, 'M', u'ằ'), + (0x1EB0, 'M', 'ằ'), (0x1EB1, 'V'), - (0x1EB2, 'M', u'ẳ'), + (0x1EB2, 'M', 'ẳ'), (0x1EB3, 'V'), - (0x1EB4, 'M', u'ẵ'), + (0x1EB4, 'M', 'ẵ'), (0x1EB5, 'V'), - (0x1EB6, 'M', u'ặ'), + (0x1EB6, 'M', 'ặ'), (0x1EB7, 'V'), - (0x1EB8, 'M', u'ẹ'), + (0x1EB8, 'M', 'ẹ'), (0x1EB9, 'V'), - (0x1EBA, 'M', u'ẻ'), + (0x1EBA, 'M', 'ẻ'), (0x1EBB, 'V'), - (0x1EBC, 'M', u'ẽ'), + (0x1EBC, 'M', 'ẽ'), (0x1EBD, 'V'), - (0x1EBE, 'M', u'ế'), + (0x1EBE, 'M', 'ế'), (0x1EBF, 'V'), - (0x1EC0, 'M', u'ề'), + (0x1EC0, 'M', 'ề'), (0x1EC1, 'V'), - (0x1EC2, 'M', u'ể'), + (0x1EC2, 'M', 'ể'), (0x1EC3, 'V'), - (0x1EC4, 'M', u'ễ'), + (0x1EC4, 'M', 'ễ'), (0x1EC5, 'V'), - (0x1EC6, 'M', u'ệ'), + (0x1EC6, 'M', 'ệ'), (0x1EC7, 'V'), - (0x1EC8, 'M', u'ỉ'), + (0x1EC8, 'M', 'ỉ'), (0x1EC9, 'V'), - (0x1ECA, 'M', u'ị'), + (0x1ECA, 'M', 'ị'), (0x1ECB, 'V'), - (0x1ECC, 'M', u'ọ'), + (0x1ECC, 'M', 'ọ'), (0x1ECD, 'V'), - (0x1ECE, 'M', u'ỏ'), + (0x1ECE, 'M', 'ỏ'), (0x1ECF, 'V'), - (0x1ED0, 'M', u'ố'), + (0x1ED0, 'M', 'ố'), (0x1ED1, 'V'), - (0x1ED2, 'M', u'ồ'), + (0x1ED2, 'M', 'ồ'), (0x1ED3, 'V'), - (0x1ED4, 'M', u'ổ'), + (0x1ED4, 'M', 'ổ'), (0x1ED5, 'V'), - (0x1ED6, 'M', u'ỗ'), + (0x1ED6, 'M', 'ỗ'), (0x1ED7, 'V'), - (0x1ED8, 'M', u'ộ'), + (0x1ED8, 'M', 'ộ'), (0x1ED9, 'V'), - (0x1EDA, 'M', u'ớ'), + (0x1EDA, 'M', 'ớ'), (0x1EDB, 'V'), - (0x1EDC, 'M', u'ờ'), + (0x1EDC, 'M', 'ờ'), (0x1EDD, 'V'), - (0x1EDE, 'M', u'ở'), + (0x1EDE, 'M', 'ở'), (0x1EDF, 'V'), - (0x1EE0, 'M', u'ỡ'), + (0x1EE0, 'M', 'ỡ'), (0x1EE1, 'V'), - (0x1EE2, 'M', u'ợ'), + (0x1EE2, 'M', 'ợ'), (0x1EE3, 'V'), - (0x1EE4, 'M', u'ụ'), + (0x1EE4, 'M', 'ụ'), (0x1EE5, 'V'), - (0x1EE6, 'M', u'ủ'), + (0x1EE6, 'M', 'ủ'), (0x1EE7, 'V'), - (0x1EE8, 'M', u'ứ'), + (0x1EE8, 'M', 'ứ'), (0x1EE9, 'V'), - (0x1EEA, 'M', u'ừ'), + (0x1EEA, 'M', 'ừ'), (0x1EEB, 'V'), + (0x1EEC, 'M', 'ử'), + (0x1EED, 'V'), ] def _seg_19(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1EEC, 'M', u'ử'), - (0x1EED, 'V'), - (0x1EEE, 'M', u'ữ'), + (0x1EEE, 'M', 'ữ'), (0x1EEF, 'V'), - (0x1EF0, 'M', u'ự'), + (0x1EF0, 'M', 'ự'), (0x1EF1, 'V'), - (0x1EF2, 'M', u'ỳ'), + (0x1EF2, 'M', 'ỳ'), (0x1EF3, 'V'), - (0x1EF4, 'M', u'ỵ'), + (0x1EF4, 'M', 'ỵ'), (0x1EF5, 'V'), - (0x1EF6, 'M', u'ỷ'), + (0x1EF6, 'M', 'ỷ'), (0x1EF7, 'V'), - (0x1EF8, 'M', u'ỹ'), + (0x1EF8, 'M', 'ỹ'), (0x1EF9, 'V'), - (0x1EFA, 'M', u'ỻ'), + (0x1EFA, 'M', 'ỻ'), (0x1EFB, 'V'), - (0x1EFC, 'M', u'ỽ'), + (0x1EFC, 'M', 'ỽ'), (0x1EFD, 'V'), - (0x1EFE, 'M', u'ỿ'), + (0x1EFE, 'M', 'ỿ'), (0x1EFF, 'V'), - (0x1F08, 'M', u'ἀ'), - (0x1F09, 'M', u'ἁ'), - (0x1F0A, 'M', u'ἂ'), - (0x1F0B, 'M', u'ἃ'), - (0x1F0C, 'M', u'ἄ'), - (0x1F0D, 'M', u'ἅ'), - (0x1F0E, 'M', u'ἆ'), - (0x1F0F, 'M', u'ἇ'), + (0x1F08, 'M', 'ἀ'), + (0x1F09, 'M', 'ἁ'), + (0x1F0A, 'M', 'ἂ'), + (0x1F0B, 'M', 'ἃ'), + (0x1F0C, 'M', 'ἄ'), + (0x1F0D, 'M', 'ἅ'), + (0x1F0E, 'M', 'ἆ'), + (0x1F0F, 'M', 'ἇ'), (0x1F10, 'V'), (0x1F16, 'X'), - (0x1F18, 'M', u'ἐ'), - (0x1F19, 'M', u'ἑ'), - (0x1F1A, 'M', u'ἒ'), - (0x1F1B, 'M', u'ἓ'), - (0x1F1C, 'M', u'ἔ'), - (0x1F1D, 'M', u'ἕ'), + (0x1F18, 'M', 'ἐ'), + (0x1F19, 'M', 'ἑ'), + (0x1F1A, 'M', 'ἒ'), + (0x1F1B, 'M', 'ἓ'), + (0x1F1C, 'M', 'ἔ'), + (0x1F1D, 'M', 'ἕ'), (0x1F1E, 'X'), (0x1F20, 'V'), - (0x1F28, 'M', u'ἠ'), - (0x1F29, 'M', u'ἡ'), - (0x1F2A, 'M', u'ἢ'), - (0x1F2B, 'M', u'ἣ'), - (0x1F2C, 'M', u'ἤ'), - (0x1F2D, 'M', u'ἥ'), - (0x1F2E, 'M', u'ἦ'), - (0x1F2F, 'M', u'ἧ'), + (0x1F28, 'M', 'ἠ'), + (0x1F29, 'M', 'ἡ'), + (0x1F2A, 'M', 'ἢ'), + (0x1F2B, 'M', 'ἣ'), + (0x1F2C, 'M', 'ἤ'), + (0x1F2D, 'M', 'ἥ'), + (0x1F2E, 'M', 'ἦ'), + (0x1F2F, 'M', 'ἧ'), (0x1F30, 'V'), - (0x1F38, 'M', u'ἰ'), - (0x1F39, 'M', u'ἱ'), - (0x1F3A, 'M', u'ἲ'), - (0x1F3B, 'M', u'ἳ'), - (0x1F3C, 'M', u'ἴ'), - (0x1F3D, 'M', u'ἵ'), - (0x1F3E, 'M', u'ἶ'), - (0x1F3F, 'M', u'ἷ'), + (0x1F38, 'M', 'ἰ'), + (0x1F39, 'M', 'ἱ'), + (0x1F3A, 'M', 'ἲ'), + (0x1F3B, 'M', 'ἳ'), + (0x1F3C, 'M', 'ἴ'), + (0x1F3D, 'M', 'ἵ'), + (0x1F3E, 'M', 'ἶ'), + (0x1F3F, 'M', 'ἷ'), (0x1F40, 'V'), (0x1F46, 'X'), - (0x1F48, 'M', u'ὀ'), - (0x1F49, 'M', u'ὁ'), - (0x1F4A, 'M', u'ὂ'), - (0x1F4B, 'M', u'ὃ'), - (0x1F4C, 'M', u'ὄ'), - (0x1F4D, 'M', u'ὅ'), + (0x1F48, 'M', 'ὀ'), + (0x1F49, 'M', 'ὁ'), + (0x1F4A, 'M', 'ὂ'), + (0x1F4B, 'M', 'ὃ'), + (0x1F4C, 'M', 'ὄ'), + (0x1F4D, 'M', 'ὅ'), (0x1F4E, 'X'), (0x1F50, 'V'), (0x1F58, 'X'), - (0x1F59, 'M', u'ὑ'), + (0x1F59, 'M', 'ὑ'), (0x1F5A, 'X'), - (0x1F5B, 'M', u'ὓ'), + (0x1F5B, 'M', 'ὓ'), (0x1F5C, 'X'), - (0x1F5D, 'M', u'ὕ'), + (0x1F5D, 'M', 'ὕ'), (0x1F5E, 'X'), - (0x1F5F, 'M', u'ὗ'), + (0x1F5F, 'M', 'ὗ'), (0x1F60, 'V'), - (0x1F68, 'M', u'ὠ'), - (0x1F69, 'M', u'ὡ'), - (0x1F6A, 'M', u'ὢ'), - (0x1F6B, 'M', u'ὣ'), - (0x1F6C, 'M', u'ὤ'), - (0x1F6D, 'M', u'ὥ'), - (0x1F6E, 'M', u'ὦ'), - (0x1F6F, 'M', u'ὧ'), + (0x1F68, 'M', 'ὠ'), + (0x1F69, 'M', 'ὡ'), + (0x1F6A, 'M', 'ὢ'), + (0x1F6B, 'M', 'ὣ'), + (0x1F6C, 'M', 'ὤ'), + (0x1F6D, 'M', 'ὥ'), + (0x1F6E, 'M', 'ὦ'), + (0x1F6F, 'M', 'ὧ'), (0x1F70, 'V'), - (0x1F71, 'M', u'ά'), + (0x1F71, 'M', 'ά'), (0x1F72, 'V'), - (0x1F73, 'M', u'έ'), + (0x1F73, 'M', 'έ'), (0x1F74, 'V'), - (0x1F75, 'M', u'ή'), + (0x1F75, 'M', 'ή'), (0x1F76, 'V'), - (0x1F77, 'M', u'ί'), + (0x1F77, 'M', 'ί'), (0x1F78, 'V'), - (0x1F79, 'M', u'ό'), + (0x1F79, 'M', 'ό'), (0x1F7A, 'V'), - (0x1F7B, 'M', u'ύ'), + (0x1F7B, 'M', 'ύ'), (0x1F7C, 'V'), - (0x1F7D, 'M', u'ώ'), + (0x1F7D, 'M', 'ώ'), (0x1F7E, 'X'), - (0x1F80, 'M', u'ἀι'), - (0x1F81, 'M', u'ἁι'), - (0x1F82, 'M', u'ἂι'), + (0x1F80, 'M', 'ἀι'), + (0x1F81, 'M', 'ἁι'), + (0x1F82, 'M', 'ἂι'), + (0x1F83, 'M', 'ἃι'), + (0x1F84, 'M', 'ἄι'), ] def _seg_20(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1F83, 'M', u'ἃι'), - (0x1F84, 'M', u'ἄι'), - (0x1F85, 'M', u'ἅι'), - (0x1F86, 'M', u'ἆι'), - (0x1F87, 'M', u'ἇι'), - (0x1F88, 'M', u'ἀι'), - (0x1F89, 'M', u'ἁι'), - (0x1F8A, 'M', u'ἂι'), - (0x1F8B, 'M', u'ἃι'), - (0x1F8C, 'M', u'ἄι'), - (0x1F8D, 'M', u'ἅι'), - (0x1F8E, 'M', u'ἆι'), - (0x1F8F, 'M', u'ἇι'), - (0x1F90, 'M', u'ἠι'), - (0x1F91, 'M', u'ἡι'), - (0x1F92, 'M', u'ἢι'), - (0x1F93, 'M', u'ἣι'), - (0x1F94, 'M', u'ἤι'), - (0x1F95, 'M', u'ἥι'), - (0x1F96, 'M', u'ἦι'), - (0x1F97, 'M', u'ἧι'), - (0x1F98, 'M', u'ἠι'), - (0x1F99, 'M', u'ἡι'), - (0x1F9A, 'M', u'ἢι'), - (0x1F9B, 'M', u'ἣι'), - (0x1F9C, 'M', u'ἤι'), - (0x1F9D, 'M', u'ἥι'), - (0x1F9E, 'M', u'ἦι'), - (0x1F9F, 'M', u'ἧι'), - (0x1FA0, 'M', u'ὠι'), - (0x1FA1, 'M', u'ὡι'), - (0x1FA2, 'M', u'ὢι'), - (0x1FA3, 'M', u'ὣι'), - (0x1FA4, 'M', u'ὤι'), - (0x1FA5, 'M', u'ὥι'), - (0x1FA6, 'M', u'ὦι'), - (0x1FA7, 'M', u'ὧι'), - (0x1FA8, 'M', u'ὠι'), - (0x1FA9, 'M', u'ὡι'), - (0x1FAA, 'M', u'ὢι'), - (0x1FAB, 'M', u'ὣι'), - (0x1FAC, 'M', u'ὤι'), - (0x1FAD, 'M', u'ὥι'), - (0x1FAE, 'M', u'ὦι'), - (0x1FAF, 'M', u'ὧι'), + (0x1F85, 'M', 'ἅι'), + (0x1F86, 'M', 'ἆι'), + (0x1F87, 'M', 'ἇι'), + (0x1F88, 'M', 'ἀι'), + (0x1F89, 'M', 'ἁι'), + (0x1F8A, 'M', 'ἂι'), + (0x1F8B, 'M', 'ἃι'), + (0x1F8C, 'M', 'ἄι'), + (0x1F8D, 'M', 'ἅι'), + (0x1F8E, 'M', 'ἆι'), + (0x1F8F, 'M', 'ἇι'), + (0x1F90, 'M', 'ἠι'), + (0x1F91, 'M', 'ἡι'), + (0x1F92, 'M', 'ἢι'), + (0x1F93, 'M', 'ἣι'), + (0x1F94, 'M', 'ἤι'), + (0x1F95, 'M', 'ἥι'), + (0x1F96, 'M', 'ἦι'), + (0x1F97, 'M', 'ἧι'), + (0x1F98, 'M', 'ἠι'), + (0x1F99, 'M', 'ἡι'), + (0x1F9A, 'M', 'ἢι'), + (0x1F9B, 'M', 'ἣι'), + (0x1F9C, 'M', 'ἤι'), + (0x1F9D, 'M', 'ἥι'), + (0x1F9E, 'M', 'ἦι'), + (0x1F9F, 'M', 'ἧι'), + (0x1FA0, 'M', 'ὠι'), + (0x1FA1, 'M', 'ὡι'), + (0x1FA2, 'M', 'ὢι'), + (0x1FA3, 'M', 'ὣι'), + (0x1FA4, 'M', 'ὤι'), + (0x1FA5, 'M', 'ὥι'), + (0x1FA6, 'M', 'ὦι'), + (0x1FA7, 'M', 'ὧι'), + (0x1FA8, 'M', 'ὠι'), + (0x1FA9, 'M', 'ὡι'), + (0x1FAA, 'M', 'ὢι'), + (0x1FAB, 'M', 'ὣι'), + (0x1FAC, 'M', 'ὤι'), + (0x1FAD, 'M', 'ὥι'), + (0x1FAE, 'M', 'ὦι'), + (0x1FAF, 'M', 'ὧι'), (0x1FB0, 'V'), - (0x1FB2, 'M', u'ὰι'), - (0x1FB3, 'M', u'αι'), - (0x1FB4, 'M', u'άι'), + (0x1FB2, 'M', 'ὰι'), + (0x1FB3, 'M', 'αι'), + (0x1FB4, 'M', 'άι'), (0x1FB5, 'X'), (0x1FB6, 'V'), - (0x1FB7, 'M', u'ᾶι'), - (0x1FB8, 'M', u'ᾰ'), - (0x1FB9, 'M', u'ᾱ'), - (0x1FBA, 'M', u'ὰ'), - (0x1FBB, 'M', u'ά'), - (0x1FBC, 'M', u'αι'), - (0x1FBD, '3', u' ̓'), - (0x1FBE, 'M', u'ι'), - (0x1FBF, '3', u' ̓'), - (0x1FC0, '3', u' ͂'), - (0x1FC1, '3', u' ̈͂'), - (0x1FC2, 'M', u'ὴι'), - (0x1FC3, 'M', u'ηι'), - (0x1FC4, 'M', u'ήι'), + (0x1FB7, 'M', 'ᾶι'), + (0x1FB8, 'M', 'ᾰ'), + (0x1FB9, 'M', 'ᾱ'), + (0x1FBA, 'M', 'ὰ'), + (0x1FBB, 'M', 'ά'), + (0x1FBC, 'M', 'αι'), + (0x1FBD, '3', ' ̓'), + (0x1FBE, 'M', 'ι'), + (0x1FBF, '3', ' ̓'), + (0x1FC0, '3', ' ͂'), + (0x1FC1, '3', ' ̈͂'), + (0x1FC2, 'M', 'ὴι'), + (0x1FC3, 'M', 'ηι'), + (0x1FC4, 'M', 'ήι'), (0x1FC5, 'X'), (0x1FC6, 'V'), - (0x1FC7, 'M', u'ῆι'), - (0x1FC8, 'M', u'ὲ'), - (0x1FC9, 'M', u'έ'), - (0x1FCA, 'M', u'ὴ'), - (0x1FCB, 'M', u'ή'), - (0x1FCC, 'M', u'ηι'), - (0x1FCD, '3', u' ̓̀'), - (0x1FCE, '3', u' ̓́'), - (0x1FCF, '3', u' ̓͂'), + (0x1FC7, 'M', 'ῆι'), + (0x1FC8, 'M', 'ὲ'), + (0x1FC9, 'M', 'έ'), + (0x1FCA, 'M', 'ὴ'), + (0x1FCB, 'M', 'ή'), + (0x1FCC, 'M', 'ηι'), + (0x1FCD, '3', ' ̓̀'), + (0x1FCE, '3', ' ̓́'), + (0x1FCF, '3', ' ̓͂'), (0x1FD0, 'V'), - (0x1FD3, 'M', u'ΐ'), + (0x1FD3, 'M', 'ΐ'), (0x1FD4, 'X'), (0x1FD6, 'V'), - (0x1FD8, 'M', u'ῐ'), - (0x1FD9, 'M', u'ῑ'), - (0x1FDA, 'M', u'ὶ'), - (0x1FDB, 'M', u'ί'), + (0x1FD8, 'M', 'ῐ'), + (0x1FD9, 'M', 'ῑ'), + (0x1FDA, 'M', 'ὶ'), + (0x1FDB, 'M', 'ί'), (0x1FDC, 'X'), - (0x1FDD, '3', u' ̔̀'), - (0x1FDE, '3', u' ̔́'), - (0x1FDF, '3', u' ̔͂'), + (0x1FDD, '3', ' ̔̀'), + (0x1FDE, '3', ' ̔́'), + (0x1FDF, '3', ' ̔͂'), (0x1FE0, 'V'), - (0x1FE3, 'M', u'ΰ'), + (0x1FE3, 'M', 'ΰ'), (0x1FE4, 'V'), - (0x1FE8, 'M', u'ῠ'), - (0x1FE9, 'M', u'ῡ'), - (0x1FEA, 'M', u'ὺ'), - (0x1FEB, 'M', u'ύ'), - (0x1FEC, 'M', u'ῥ'), - (0x1FED, '3', u' ̈̀'), - (0x1FEE, '3', u' ̈́'), - (0x1FEF, '3', u'`'), + (0x1FE8, 'M', 'ῠ'), + (0x1FE9, 'M', 'ῡ'), + (0x1FEA, 'M', 'ὺ'), + (0x1FEB, 'M', 'ύ'), + (0x1FEC, 'M', 'ῥ'), + (0x1FED, '3', ' ̈̀'), + (0x1FEE, '3', ' ̈́'), + (0x1FEF, '3', '`'), (0x1FF0, 'X'), + (0x1FF2, 'M', 'ὼι'), + (0x1FF3, 'M', 'ωι'), ] def _seg_21(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1FF2, 'M', u'ὼι'), - (0x1FF3, 'M', u'ωι'), - (0x1FF4, 'M', u'ώι'), + (0x1FF4, 'M', 'ώι'), (0x1FF5, 'X'), (0x1FF6, 'V'), - (0x1FF7, 'M', u'ῶι'), - (0x1FF8, 'M', u'ὸ'), - (0x1FF9, 'M', u'ό'), - (0x1FFA, 'M', u'ὼ'), - (0x1FFB, 'M', u'ώ'), - (0x1FFC, 'M', u'ωι'), - (0x1FFD, '3', u' ́'), - (0x1FFE, '3', u' ̔'), + (0x1FF7, 'M', 'ῶι'), + (0x1FF8, 'M', 'ὸ'), + (0x1FF9, 'M', 'ό'), + (0x1FFA, 'M', 'ὼ'), + (0x1FFB, 'M', 'ώ'), + (0x1FFC, 'M', 'ωι'), + (0x1FFD, '3', ' ́'), + (0x1FFE, '3', ' ̔'), (0x1FFF, 'X'), - (0x2000, '3', u' '), + (0x2000, '3', ' '), (0x200B, 'I'), - (0x200C, 'D', u''), + (0x200C, 'D', ''), (0x200E, 'X'), (0x2010, 'V'), - (0x2011, 'M', u'‐'), + (0x2011, 'M', '‐'), (0x2012, 'V'), - (0x2017, '3', u' ̳'), + (0x2017, '3', ' ̳'), (0x2018, 'V'), (0x2024, 'X'), (0x2027, 'V'), (0x2028, 'X'), - (0x202F, '3', u' '), + (0x202F, '3', ' '), (0x2030, 'V'), - (0x2033, 'M', u'′′'), - (0x2034, 'M', u'′′′'), + (0x2033, 'M', '′′'), + (0x2034, 'M', '′′′'), (0x2035, 'V'), - (0x2036, 'M', u'‵‵'), - (0x2037, 'M', u'‵‵‵'), + (0x2036, 'M', '‵‵'), + (0x2037, 'M', '‵‵‵'), (0x2038, 'V'), - (0x203C, '3', u'!!'), + (0x203C, '3', '!!'), (0x203D, 'V'), - (0x203E, '3', u' ̅'), + (0x203E, '3', ' ̅'), (0x203F, 'V'), - (0x2047, '3', u'??'), - (0x2048, '3', u'?!'), - (0x2049, '3', u'!?'), + (0x2047, '3', '??'), + (0x2048, '3', '?!'), + (0x2049, '3', '!?'), (0x204A, 'V'), - (0x2057, 'M', u'′′′′'), + (0x2057, 'M', '′′′′'), (0x2058, 'V'), - (0x205F, '3', u' '), + (0x205F, '3', ' '), (0x2060, 'I'), (0x2061, 'X'), (0x2064, 'I'), (0x2065, 'X'), - (0x2070, 'M', u'0'), - (0x2071, 'M', u'i'), + (0x2070, 'M', '0'), + (0x2071, 'M', 'i'), (0x2072, 'X'), - (0x2074, 'M', u'4'), - (0x2075, 'M', u'5'), - (0x2076, 'M', u'6'), - (0x2077, 'M', u'7'), - (0x2078, 'M', u'8'), - (0x2079, 'M', u'9'), - (0x207A, '3', u'+'), - (0x207B, 'M', u'−'), - (0x207C, '3', u'='), - (0x207D, '3', u'('), - (0x207E, '3', u')'), - (0x207F, 'M', u'n'), - (0x2080, 'M', u'0'), - (0x2081, 'M', u'1'), - (0x2082, 'M', u'2'), - (0x2083, 'M', u'3'), - (0x2084, 'M', u'4'), - (0x2085, 'M', u'5'), - (0x2086, 'M', u'6'), - (0x2087, 'M', u'7'), - (0x2088, 'M', u'8'), - (0x2089, 'M', u'9'), - (0x208A, '3', u'+'), - (0x208B, 'M', u'−'), - (0x208C, '3', u'='), - (0x208D, '3', u'('), - (0x208E, '3', u')'), + (0x2074, 'M', '4'), + (0x2075, 'M', '5'), + (0x2076, 'M', '6'), + (0x2077, 'M', '7'), + (0x2078, 'M', '8'), + (0x2079, 'M', '9'), + (0x207A, '3', '+'), + (0x207B, 'M', '−'), + (0x207C, '3', '='), + (0x207D, '3', '('), + (0x207E, '3', ')'), + (0x207F, 'M', 'n'), + (0x2080, 'M', '0'), + (0x2081, 'M', '1'), + (0x2082, 'M', '2'), + (0x2083, 'M', '3'), + (0x2084, 'M', '4'), + (0x2085, 'M', '5'), + (0x2086, 'M', '6'), + (0x2087, 'M', '7'), + (0x2088, 'M', '8'), + (0x2089, 'M', '9'), + (0x208A, '3', '+'), + (0x208B, 'M', '−'), + (0x208C, '3', '='), + (0x208D, '3', '('), + (0x208E, '3', ')'), (0x208F, 'X'), - (0x2090, 'M', u'a'), - (0x2091, 'M', u'e'), - (0x2092, 'M', u'o'), - (0x2093, 'M', u'x'), - (0x2094, 'M', u'ə'), - (0x2095, 'M', u'h'), - (0x2096, 'M', u'k'), - (0x2097, 'M', u'l'), - (0x2098, 'M', u'm'), - (0x2099, 'M', u'n'), - (0x209A, 'M', u'p'), - (0x209B, 'M', u's'), - (0x209C, 'M', u't'), + (0x2090, 'M', 'a'), + (0x2091, 'M', 'e'), + (0x2092, 'M', 'o'), + (0x2093, 'M', 'x'), + (0x2094, 'M', 'ə'), + (0x2095, 'M', 'h'), + (0x2096, 'M', 'k'), + (0x2097, 'M', 'l'), + (0x2098, 'M', 'm'), + (0x2099, 'M', 'n'), + (0x209A, 'M', 'p'), + (0x209B, 'M', 's'), + (0x209C, 'M', 't'), (0x209D, 'X'), (0x20A0, 'V'), - (0x20A8, 'M', u'rs'), + (0x20A8, 'M', 'rs'), (0x20A9, 'V'), (0x20C0, 'X'), (0x20D0, 'V'), (0x20F1, 'X'), + (0x2100, '3', 'a/c'), + (0x2101, '3', 'a/s'), ] def _seg_22(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2100, '3', u'a/c'), - (0x2101, '3', u'a/s'), - (0x2102, 'M', u'c'), - (0x2103, 'M', u'°c'), + (0x2102, 'M', 'c'), + (0x2103, 'M', '°c'), (0x2104, 'V'), - (0x2105, '3', u'c/o'), - (0x2106, '3', u'c/u'), - (0x2107, 'M', u'ɛ'), + (0x2105, '3', 'c/o'), + (0x2106, '3', 'c/u'), + (0x2107, 'M', 'ɛ'), (0x2108, 'V'), - (0x2109, 'M', u'°f'), - (0x210A, 'M', u'g'), - (0x210B, 'M', u'h'), - (0x210F, 'M', u'ħ'), - (0x2110, 'M', u'i'), - (0x2112, 'M', u'l'), + (0x2109, 'M', '°f'), + (0x210A, 'M', 'g'), + (0x210B, 'M', 'h'), + (0x210F, 'M', 'ħ'), + (0x2110, 'M', 'i'), + (0x2112, 'M', 'l'), (0x2114, 'V'), - (0x2115, 'M', u'n'), - (0x2116, 'M', u'no'), + (0x2115, 'M', 'n'), + (0x2116, 'M', 'no'), (0x2117, 'V'), - (0x2119, 'M', u'p'), - (0x211A, 'M', u'q'), - (0x211B, 'M', u'r'), + (0x2119, 'M', 'p'), + (0x211A, 'M', 'q'), + (0x211B, 'M', 'r'), (0x211E, 'V'), - (0x2120, 'M', u'sm'), - (0x2121, 'M', u'tel'), - (0x2122, 'M', u'tm'), + (0x2120, 'M', 'sm'), + (0x2121, 'M', 'tel'), + (0x2122, 'M', 'tm'), (0x2123, 'V'), - (0x2124, 'M', u'z'), + (0x2124, 'M', 'z'), (0x2125, 'V'), - (0x2126, 'M', u'ω'), + (0x2126, 'M', 'ω'), (0x2127, 'V'), - (0x2128, 'M', u'z'), + (0x2128, 'M', 'z'), (0x2129, 'V'), - (0x212A, 'M', u'k'), - (0x212B, 'M', u'å'), - (0x212C, 'M', u'b'), - (0x212D, 'M', u'c'), + (0x212A, 'M', 'k'), + (0x212B, 'M', 'å'), + (0x212C, 'M', 'b'), + (0x212D, 'M', 'c'), (0x212E, 'V'), - (0x212F, 'M', u'e'), - (0x2131, 'M', u'f'), + (0x212F, 'M', 'e'), + (0x2131, 'M', 'f'), (0x2132, 'X'), - (0x2133, 'M', u'm'), - (0x2134, 'M', u'o'), - (0x2135, 'M', u'א'), - (0x2136, 'M', u'ב'), - (0x2137, 'M', u'ג'), - (0x2138, 'M', u'ד'), - (0x2139, 'M', u'i'), + (0x2133, 'M', 'm'), + (0x2134, 'M', 'o'), + (0x2135, 'M', 'א'), + (0x2136, 'M', 'ב'), + (0x2137, 'M', 'ג'), + (0x2138, 'M', 'ד'), + (0x2139, 'M', 'i'), (0x213A, 'V'), - (0x213B, 'M', u'fax'), - (0x213C, 'M', u'π'), - (0x213D, 'M', u'γ'), - (0x213F, 'M', u'π'), - (0x2140, 'M', u'∑'), + (0x213B, 'M', 'fax'), + (0x213C, 'M', 'π'), + (0x213D, 'M', 'γ'), + (0x213F, 'M', 'π'), + (0x2140, 'M', '∑'), (0x2141, 'V'), - (0x2145, 'M', u'd'), - (0x2147, 'M', u'e'), - (0x2148, 'M', u'i'), - (0x2149, 'M', u'j'), + (0x2145, 'M', 'd'), + (0x2147, 'M', 'e'), + (0x2148, 'M', 'i'), + (0x2149, 'M', 'j'), (0x214A, 'V'), - (0x2150, 'M', u'1⁄7'), - (0x2151, 'M', u'1⁄9'), - (0x2152, 'M', u'1⁄10'), - (0x2153, 'M', u'1⁄3'), - (0x2154, 'M', u'2⁄3'), - (0x2155, 'M', u'1⁄5'), - (0x2156, 'M', u'2⁄5'), - (0x2157, 'M', u'3⁄5'), - (0x2158, 'M', u'4⁄5'), - (0x2159, 'M', u'1⁄6'), - (0x215A, 'M', u'5⁄6'), - (0x215B, 'M', u'1⁄8'), - (0x215C, 'M', u'3⁄8'), - (0x215D, 'M', u'5⁄8'), - (0x215E, 'M', u'7⁄8'), - (0x215F, 'M', u'1⁄'), - (0x2160, 'M', u'i'), - (0x2161, 'M', u'ii'), - (0x2162, 'M', u'iii'), - (0x2163, 'M', u'iv'), - (0x2164, 'M', u'v'), - (0x2165, 'M', u'vi'), - (0x2166, 'M', u'vii'), - (0x2167, 'M', u'viii'), - (0x2168, 'M', u'ix'), - (0x2169, 'M', u'x'), - (0x216A, 'M', u'xi'), - (0x216B, 'M', u'xii'), - (0x216C, 'M', u'l'), - (0x216D, 'M', u'c'), - (0x216E, 'M', u'd'), - (0x216F, 'M', u'm'), - (0x2170, 'M', u'i'), - (0x2171, 'M', u'ii'), - (0x2172, 'M', u'iii'), - (0x2173, 'M', u'iv'), - (0x2174, 'M', u'v'), - (0x2175, 'M', u'vi'), - (0x2176, 'M', u'vii'), - (0x2177, 'M', u'viii'), + (0x2150, 'M', '1⁄7'), + (0x2151, 'M', '1⁄9'), + (0x2152, 'M', '1⁄10'), + (0x2153, 'M', '1⁄3'), + (0x2154, 'M', '2⁄3'), + (0x2155, 'M', '1⁄5'), + (0x2156, 'M', '2⁄5'), + (0x2157, 'M', '3⁄5'), + (0x2158, 'M', '4⁄5'), + (0x2159, 'M', '1⁄6'), + (0x215A, 'M', '5⁄6'), + (0x215B, 'M', '1⁄8'), + (0x215C, 'M', '3⁄8'), + (0x215D, 'M', '5⁄8'), + (0x215E, 'M', '7⁄8'), + (0x215F, 'M', '1⁄'), + (0x2160, 'M', 'i'), + (0x2161, 'M', 'ii'), + (0x2162, 'M', 'iii'), + (0x2163, 'M', 'iv'), + (0x2164, 'M', 'v'), + (0x2165, 'M', 'vi'), + (0x2166, 'M', 'vii'), + (0x2167, 'M', 'viii'), + (0x2168, 'M', 'ix'), + (0x2169, 'M', 'x'), + (0x216A, 'M', 'xi'), + (0x216B, 'M', 'xii'), + (0x216C, 'M', 'l'), + (0x216D, 'M', 'c'), + (0x216E, 'M', 'd'), + (0x216F, 'M', 'm'), + (0x2170, 'M', 'i'), + (0x2171, 'M', 'ii'), + (0x2172, 'M', 'iii'), + (0x2173, 'M', 'iv'), + (0x2174, 'M', 'v'), + (0x2175, 'M', 'vi'), + (0x2176, 'M', 'vii'), + (0x2177, 'M', 'viii'), + (0x2178, 'M', 'ix'), + (0x2179, 'M', 'x'), ] def _seg_23(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2178, 'M', u'ix'), - (0x2179, 'M', u'x'), - (0x217A, 'M', u'xi'), - (0x217B, 'M', u'xii'), - (0x217C, 'M', u'l'), - (0x217D, 'M', u'c'), - (0x217E, 'M', u'd'), - (0x217F, 'M', u'm'), + (0x217A, 'M', 'xi'), + (0x217B, 'M', 'xii'), + (0x217C, 'M', 'l'), + (0x217D, 'M', 'c'), + (0x217E, 'M', 'd'), + (0x217F, 'M', 'm'), (0x2180, 'V'), (0x2183, 'X'), (0x2184, 'V'), - (0x2189, 'M', u'0⁄3'), + (0x2189, 'M', '0⁄3'), (0x218A, 'V'), (0x218C, 'X'), (0x2190, 'V'), - (0x222C, 'M', u'∫∫'), - (0x222D, 'M', u'∫∫∫'), + (0x222C, 'M', '∫∫'), + (0x222D, 'M', '∫∫∫'), (0x222E, 'V'), - (0x222F, 'M', u'∮∮'), - (0x2230, 'M', u'∮∮∮'), + (0x222F, 'M', '∮∮'), + (0x2230, 'M', '∮∮∮'), (0x2231, 'V'), (0x2260, '3'), (0x2261, 'V'), (0x226E, '3'), (0x2270, 'V'), - (0x2329, 'M', u'〈'), - (0x232A, 'M', u'〉'), + (0x2329, 'M', '〈'), + (0x232A, 'M', '〉'), (0x232B, 'V'), (0x2427, 'X'), (0x2440, 'V'), (0x244B, 'X'), - (0x2460, 'M', u'1'), - (0x2461, 'M', u'2'), - (0x2462, 'M', u'3'), - (0x2463, 'M', u'4'), - (0x2464, 'M', u'5'), - (0x2465, 'M', u'6'), - (0x2466, 'M', u'7'), - (0x2467, 'M', u'8'), - (0x2468, 'M', u'9'), - (0x2469, 'M', u'10'), - (0x246A, 'M', u'11'), - (0x246B, 'M', u'12'), - (0x246C, 'M', u'13'), - (0x246D, 'M', u'14'), - (0x246E, 'M', u'15'), - (0x246F, 'M', u'16'), - (0x2470, 'M', u'17'), - (0x2471, 'M', u'18'), - (0x2472, 'M', u'19'), - (0x2473, 'M', u'20'), - (0x2474, '3', u'(1)'), - (0x2475, '3', u'(2)'), - (0x2476, '3', u'(3)'), - (0x2477, '3', u'(4)'), - (0x2478, '3', u'(5)'), - (0x2479, '3', u'(6)'), - (0x247A, '3', u'(7)'), - (0x247B, '3', u'(8)'), - (0x247C, '3', u'(9)'), - (0x247D, '3', u'(10)'), - (0x247E, '3', u'(11)'), - (0x247F, '3', u'(12)'), - (0x2480, '3', u'(13)'), - (0x2481, '3', u'(14)'), - (0x2482, '3', u'(15)'), - (0x2483, '3', u'(16)'), - (0x2484, '3', u'(17)'), - (0x2485, '3', u'(18)'), - (0x2486, '3', u'(19)'), - (0x2487, '3', u'(20)'), + (0x2460, 'M', '1'), + (0x2461, 'M', '2'), + (0x2462, 'M', '3'), + (0x2463, 'M', '4'), + (0x2464, 'M', '5'), + (0x2465, 'M', '6'), + (0x2466, 'M', '7'), + (0x2467, 'M', '8'), + (0x2468, 'M', '9'), + (0x2469, 'M', '10'), + (0x246A, 'M', '11'), + (0x246B, 'M', '12'), + (0x246C, 'M', '13'), + (0x246D, 'M', '14'), + (0x246E, 'M', '15'), + (0x246F, 'M', '16'), + (0x2470, 'M', '17'), + (0x2471, 'M', '18'), + (0x2472, 'M', '19'), + (0x2473, 'M', '20'), + (0x2474, '3', '(1)'), + (0x2475, '3', '(2)'), + (0x2476, '3', '(3)'), + (0x2477, '3', '(4)'), + (0x2478, '3', '(5)'), + (0x2479, '3', '(6)'), + (0x247A, '3', '(7)'), + (0x247B, '3', '(8)'), + (0x247C, '3', '(9)'), + (0x247D, '3', '(10)'), + (0x247E, '3', '(11)'), + (0x247F, '3', '(12)'), + (0x2480, '3', '(13)'), + (0x2481, '3', '(14)'), + (0x2482, '3', '(15)'), + (0x2483, '3', '(16)'), + (0x2484, '3', '(17)'), + (0x2485, '3', '(18)'), + (0x2486, '3', '(19)'), + (0x2487, '3', '(20)'), (0x2488, 'X'), - (0x249C, '3', u'(a)'), - (0x249D, '3', u'(b)'), - (0x249E, '3', u'(c)'), - (0x249F, '3', u'(d)'), - (0x24A0, '3', u'(e)'), - (0x24A1, '3', u'(f)'), - (0x24A2, '3', u'(g)'), - (0x24A3, '3', u'(h)'), - (0x24A4, '3', u'(i)'), - (0x24A5, '3', u'(j)'), - (0x24A6, '3', u'(k)'), - (0x24A7, '3', u'(l)'), - (0x24A8, '3', u'(m)'), - (0x24A9, '3', u'(n)'), - (0x24AA, '3', u'(o)'), - (0x24AB, '3', u'(p)'), - (0x24AC, '3', u'(q)'), - (0x24AD, '3', u'(r)'), - (0x24AE, '3', u'(s)'), - (0x24AF, '3', u'(t)'), - (0x24B0, '3', u'(u)'), - (0x24B1, '3', u'(v)'), - (0x24B2, '3', u'(w)'), - (0x24B3, '3', u'(x)'), - (0x24B4, '3', u'(y)'), - (0x24B5, '3', u'(z)'), - (0x24B6, 'M', u'a'), - (0x24B7, 'M', u'b'), + (0x249C, '3', '(a)'), + (0x249D, '3', '(b)'), + (0x249E, '3', '(c)'), + (0x249F, '3', '(d)'), + (0x24A0, '3', '(e)'), + (0x24A1, '3', '(f)'), + (0x24A2, '3', '(g)'), + (0x24A3, '3', '(h)'), + (0x24A4, '3', '(i)'), + (0x24A5, '3', '(j)'), + (0x24A6, '3', '(k)'), + (0x24A7, '3', '(l)'), + (0x24A8, '3', '(m)'), + (0x24A9, '3', '(n)'), + (0x24AA, '3', '(o)'), + (0x24AB, '3', '(p)'), + (0x24AC, '3', '(q)'), + (0x24AD, '3', '(r)'), + (0x24AE, '3', '(s)'), + (0x24AF, '3', '(t)'), + (0x24B0, '3', '(u)'), + (0x24B1, '3', '(v)'), + (0x24B2, '3', '(w)'), + (0x24B3, '3', '(x)'), + (0x24B4, '3', '(y)'), + (0x24B5, '3', '(z)'), + (0x24B6, 'M', 'a'), + (0x24B7, 'M', 'b'), + (0x24B8, 'M', 'c'), + (0x24B9, 'M', 'd'), ] def _seg_24(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x24B8, 'M', u'c'), - (0x24B9, 'M', u'd'), - (0x24BA, 'M', u'e'), - (0x24BB, 'M', u'f'), - (0x24BC, 'M', u'g'), - (0x24BD, 'M', u'h'), - (0x24BE, 'M', u'i'), - (0x24BF, 'M', u'j'), - (0x24C0, 'M', u'k'), - (0x24C1, 'M', u'l'), - (0x24C2, 'M', u'm'), - (0x24C3, 'M', u'n'), - (0x24C4, 'M', u'o'), - (0x24C5, 'M', u'p'), - (0x24C6, 'M', u'q'), - (0x24C7, 'M', u'r'), - (0x24C8, 'M', u's'), - (0x24C9, 'M', u't'), - (0x24CA, 'M', u'u'), - (0x24CB, 'M', u'v'), - (0x24CC, 'M', u'w'), - (0x24CD, 'M', u'x'), - (0x24CE, 'M', u'y'), - (0x24CF, 'M', u'z'), - (0x24D0, 'M', u'a'), - (0x24D1, 'M', u'b'), - (0x24D2, 'M', u'c'), - (0x24D3, 'M', u'd'), - (0x24D4, 'M', u'e'), - (0x24D5, 'M', u'f'), - (0x24D6, 'M', u'g'), - (0x24D7, 'M', u'h'), - (0x24D8, 'M', u'i'), - (0x24D9, 'M', u'j'), - (0x24DA, 'M', u'k'), - (0x24DB, 'M', u'l'), - (0x24DC, 'M', u'm'), - (0x24DD, 'M', u'n'), - (0x24DE, 'M', u'o'), - (0x24DF, 'M', u'p'), - (0x24E0, 'M', u'q'), - (0x24E1, 'M', u'r'), - (0x24E2, 'M', u's'), - (0x24E3, 'M', u't'), - (0x24E4, 'M', u'u'), - (0x24E5, 'M', u'v'), - (0x24E6, 'M', u'w'), - (0x24E7, 'M', u'x'), - (0x24E8, 'M', u'y'), - (0x24E9, 'M', u'z'), - (0x24EA, 'M', u'0'), + (0x24BA, 'M', 'e'), + (0x24BB, 'M', 'f'), + (0x24BC, 'M', 'g'), + (0x24BD, 'M', 'h'), + (0x24BE, 'M', 'i'), + (0x24BF, 'M', 'j'), + (0x24C0, 'M', 'k'), + (0x24C1, 'M', 'l'), + (0x24C2, 'M', 'm'), + (0x24C3, 'M', 'n'), + (0x24C4, 'M', 'o'), + (0x24C5, 'M', 'p'), + (0x24C6, 'M', 'q'), + (0x24C7, 'M', 'r'), + (0x24C8, 'M', 's'), + (0x24C9, 'M', 't'), + (0x24CA, 'M', 'u'), + (0x24CB, 'M', 'v'), + (0x24CC, 'M', 'w'), + (0x24CD, 'M', 'x'), + (0x24CE, 'M', 'y'), + (0x24CF, 'M', 'z'), + (0x24D0, 'M', 'a'), + (0x24D1, 'M', 'b'), + (0x24D2, 'M', 'c'), + (0x24D3, 'M', 'd'), + (0x24D4, 'M', 'e'), + (0x24D5, 'M', 'f'), + (0x24D6, 'M', 'g'), + (0x24D7, 'M', 'h'), + (0x24D8, 'M', 'i'), + (0x24D9, 'M', 'j'), + (0x24DA, 'M', 'k'), + (0x24DB, 'M', 'l'), + (0x24DC, 'M', 'm'), + (0x24DD, 'M', 'n'), + (0x24DE, 'M', 'o'), + (0x24DF, 'M', 'p'), + (0x24E0, 'M', 'q'), + (0x24E1, 'M', 'r'), + (0x24E2, 'M', 's'), + (0x24E3, 'M', 't'), + (0x24E4, 'M', 'u'), + (0x24E5, 'M', 'v'), + (0x24E6, 'M', 'w'), + (0x24E7, 'M', 'x'), + (0x24E8, 'M', 'y'), + (0x24E9, 'M', 'z'), + (0x24EA, 'M', '0'), (0x24EB, 'V'), - (0x2A0C, 'M', u'∫∫∫∫'), + (0x2A0C, 'M', '∫∫∫∫'), (0x2A0D, 'V'), - (0x2A74, '3', u'::='), - (0x2A75, '3', u'=='), - (0x2A76, '3', u'==='), + (0x2A74, '3', '::='), + (0x2A75, '3', '=='), + (0x2A76, '3', '==='), (0x2A77, 'V'), - (0x2ADC, 'M', u'⫝̸'), + (0x2ADC, 'M', '⫝̸'), (0x2ADD, 'V'), (0x2B74, 'X'), (0x2B76, 'V'), (0x2B96, 'X'), - (0x2B98, 'V'), - (0x2C00, 'M', u'ⰰ'), - (0x2C01, 'M', u'ⰱ'), - (0x2C02, 'M', u'ⰲ'), - (0x2C03, 'M', u'ⰳ'), - (0x2C04, 'M', u'ⰴ'), - (0x2C05, 'M', u'ⰵ'), - (0x2C06, 'M', u'ⰶ'), - (0x2C07, 'M', u'ⰷ'), - (0x2C08, 'M', u'ⰸ'), - (0x2C09, 'M', u'ⰹ'), - (0x2C0A, 'M', u'ⰺ'), - (0x2C0B, 'M', u'ⰻ'), - (0x2C0C, 'M', u'ⰼ'), - (0x2C0D, 'M', u'ⰽ'), - (0x2C0E, 'M', u'ⰾ'), - (0x2C0F, 'M', u'ⰿ'), - (0x2C10, 'M', u'ⱀ'), - (0x2C11, 'M', u'ⱁ'), - (0x2C12, 'M', u'ⱂ'), - (0x2C13, 'M', u'ⱃ'), - (0x2C14, 'M', u'ⱄ'), - (0x2C15, 'M', u'ⱅ'), - (0x2C16, 'M', u'ⱆ'), - (0x2C17, 'M', u'ⱇ'), - (0x2C18, 'M', u'ⱈ'), - (0x2C19, 'M', u'ⱉ'), - (0x2C1A, 'M', u'ⱊ'), - (0x2C1B, 'M', u'ⱋ'), - (0x2C1C, 'M', u'ⱌ'), - (0x2C1D, 'M', u'ⱍ'), - (0x2C1E, 'M', u'ⱎ'), - (0x2C1F, 'M', u'ⱏ'), - (0x2C20, 'M', u'ⱐ'), - (0x2C21, 'M', u'ⱑ'), - (0x2C22, 'M', u'ⱒ'), - (0x2C23, 'M', u'ⱓ'), + (0x2B97, 'V'), + (0x2C00, 'M', 'ⰰ'), + (0x2C01, 'M', 'ⰱ'), + (0x2C02, 'M', 'ⰲ'), + (0x2C03, 'M', 'ⰳ'), + (0x2C04, 'M', 'ⰴ'), + (0x2C05, 'M', 'ⰵ'), + (0x2C06, 'M', 'ⰶ'), + (0x2C07, 'M', 'ⰷ'), + (0x2C08, 'M', 'ⰸ'), + (0x2C09, 'M', 'ⰹ'), + (0x2C0A, 'M', 'ⰺ'), + (0x2C0B, 'M', 'ⰻ'), + (0x2C0C, 'M', 'ⰼ'), + (0x2C0D, 'M', 'ⰽ'), + (0x2C0E, 'M', 'ⰾ'), + (0x2C0F, 'M', 'ⰿ'), + (0x2C10, 'M', 'ⱀ'), + (0x2C11, 'M', 'ⱁ'), + (0x2C12, 'M', 'ⱂ'), + (0x2C13, 'M', 'ⱃ'), + (0x2C14, 'M', 'ⱄ'), + (0x2C15, 'M', 'ⱅ'), + (0x2C16, 'M', 'ⱆ'), + (0x2C17, 'M', 'ⱇ'), + (0x2C18, 'M', 'ⱈ'), + (0x2C19, 'M', 'ⱉ'), + (0x2C1A, 'M', 'ⱊ'), + (0x2C1B, 'M', 'ⱋ'), + (0x2C1C, 'M', 'ⱌ'), + (0x2C1D, 'M', 'ⱍ'), + (0x2C1E, 'M', 'ⱎ'), + (0x2C1F, 'M', 'ⱏ'), + (0x2C20, 'M', 'ⱐ'), + (0x2C21, 'M', 'ⱑ'), + (0x2C22, 'M', 'ⱒ'), + (0x2C23, 'M', 'ⱓ'), + (0x2C24, 'M', 'ⱔ'), + (0x2C25, 'M', 'ⱕ'), ] def _seg_25(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2C24, 'M', u'ⱔ'), - (0x2C25, 'M', u'ⱕ'), - (0x2C26, 'M', u'ⱖ'), - (0x2C27, 'M', u'ⱗ'), - (0x2C28, 'M', u'ⱘ'), - (0x2C29, 'M', u'ⱙ'), - (0x2C2A, 'M', u'ⱚ'), - (0x2C2B, 'M', u'ⱛ'), - (0x2C2C, 'M', u'ⱜ'), - (0x2C2D, 'M', u'ⱝ'), - (0x2C2E, 'M', u'ⱞ'), + (0x2C26, 'M', 'ⱖ'), + (0x2C27, 'M', 'ⱗ'), + (0x2C28, 'M', 'ⱘ'), + (0x2C29, 'M', 'ⱙ'), + (0x2C2A, 'M', 'ⱚ'), + (0x2C2B, 'M', 'ⱛ'), + (0x2C2C, 'M', 'ⱜ'), + (0x2C2D, 'M', 'ⱝ'), + (0x2C2E, 'M', 'ⱞ'), (0x2C2F, 'X'), (0x2C30, 'V'), (0x2C5F, 'X'), - (0x2C60, 'M', u'ⱡ'), + (0x2C60, 'M', 'ⱡ'), (0x2C61, 'V'), - (0x2C62, 'M', u'ɫ'), - (0x2C63, 'M', u'ᵽ'), - (0x2C64, 'M', u'ɽ'), + (0x2C62, 'M', 'ɫ'), + (0x2C63, 'M', 'ᵽ'), + (0x2C64, 'M', 'ɽ'), (0x2C65, 'V'), - (0x2C67, 'M', u'ⱨ'), + (0x2C67, 'M', 'ⱨ'), (0x2C68, 'V'), - (0x2C69, 'M', u'ⱪ'), + (0x2C69, 'M', 'ⱪ'), (0x2C6A, 'V'), - (0x2C6B, 'M', u'ⱬ'), + (0x2C6B, 'M', 'ⱬ'), (0x2C6C, 'V'), - (0x2C6D, 'M', u'ɑ'), - (0x2C6E, 'M', u'ɱ'), - (0x2C6F, 'M', u'ɐ'), - (0x2C70, 'M', u'ɒ'), + (0x2C6D, 'M', 'ɑ'), + (0x2C6E, 'M', 'ɱ'), + (0x2C6F, 'M', 'ɐ'), + (0x2C70, 'M', 'ɒ'), (0x2C71, 'V'), - (0x2C72, 'M', u'ⱳ'), + (0x2C72, 'M', 'ⱳ'), (0x2C73, 'V'), - (0x2C75, 'M', u'ⱶ'), + (0x2C75, 'M', 'ⱶ'), (0x2C76, 'V'), - (0x2C7C, 'M', u'j'), - (0x2C7D, 'M', u'v'), - (0x2C7E, 'M', u'ȿ'), - (0x2C7F, 'M', u'ɀ'), - (0x2C80, 'M', u'ⲁ'), + (0x2C7C, 'M', 'j'), + (0x2C7D, 'M', 'v'), + (0x2C7E, 'M', 'ȿ'), + (0x2C7F, 'M', 'ɀ'), + (0x2C80, 'M', 'ⲁ'), (0x2C81, 'V'), - (0x2C82, 'M', u'ⲃ'), + (0x2C82, 'M', 'ⲃ'), (0x2C83, 'V'), - (0x2C84, 'M', u'ⲅ'), + (0x2C84, 'M', 'ⲅ'), (0x2C85, 'V'), - (0x2C86, 'M', u'ⲇ'), + (0x2C86, 'M', 'ⲇ'), (0x2C87, 'V'), - (0x2C88, 'M', u'ⲉ'), + (0x2C88, 'M', 'ⲉ'), (0x2C89, 'V'), - (0x2C8A, 'M', u'ⲋ'), + (0x2C8A, 'M', 'ⲋ'), (0x2C8B, 'V'), - (0x2C8C, 'M', u'ⲍ'), + (0x2C8C, 'M', 'ⲍ'), (0x2C8D, 'V'), - (0x2C8E, 'M', u'ⲏ'), + (0x2C8E, 'M', 'ⲏ'), (0x2C8F, 'V'), - (0x2C90, 'M', u'ⲑ'), + (0x2C90, 'M', 'ⲑ'), (0x2C91, 'V'), - (0x2C92, 'M', u'ⲓ'), + (0x2C92, 'M', 'ⲓ'), (0x2C93, 'V'), - (0x2C94, 'M', u'ⲕ'), + (0x2C94, 'M', 'ⲕ'), (0x2C95, 'V'), - (0x2C96, 'M', u'ⲗ'), + (0x2C96, 'M', 'ⲗ'), (0x2C97, 'V'), - (0x2C98, 'M', u'ⲙ'), + (0x2C98, 'M', 'ⲙ'), (0x2C99, 'V'), - (0x2C9A, 'M', u'ⲛ'), + (0x2C9A, 'M', 'ⲛ'), (0x2C9B, 'V'), - (0x2C9C, 'M', u'ⲝ'), + (0x2C9C, 'M', 'ⲝ'), (0x2C9D, 'V'), - (0x2C9E, 'M', u'ⲟ'), + (0x2C9E, 'M', 'ⲟ'), (0x2C9F, 'V'), - (0x2CA0, 'M', u'ⲡ'), + (0x2CA0, 'M', 'ⲡ'), (0x2CA1, 'V'), - (0x2CA2, 'M', u'ⲣ'), + (0x2CA2, 'M', 'ⲣ'), (0x2CA3, 'V'), - (0x2CA4, 'M', u'ⲥ'), + (0x2CA4, 'M', 'ⲥ'), (0x2CA5, 'V'), - (0x2CA6, 'M', u'ⲧ'), + (0x2CA6, 'M', 'ⲧ'), (0x2CA7, 'V'), - (0x2CA8, 'M', u'ⲩ'), + (0x2CA8, 'M', 'ⲩ'), (0x2CA9, 'V'), - (0x2CAA, 'M', u'ⲫ'), + (0x2CAA, 'M', 'ⲫ'), (0x2CAB, 'V'), - (0x2CAC, 'M', u'ⲭ'), + (0x2CAC, 'M', 'ⲭ'), (0x2CAD, 'V'), - (0x2CAE, 'M', u'ⲯ'), + (0x2CAE, 'M', 'ⲯ'), (0x2CAF, 'V'), - (0x2CB0, 'M', u'ⲱ'), + (0x2CB0, 'M', 'ⲱ'), (0x2CB1, 'V'), - (0x2CB2, 'M', u'ⲳ'), + (0x2CB2, 'M', 'ⲳ'), (0x2CB3, 'V'), - (0x2CB4, 'M', u'ⲵ'), + (0x2CB4, 'M', 'ⲵ'), (0x2CB5, 'V'), - (0x2CB6, 'M', u'ⲷ'), + (0x2CB6, 'M', 'ⲷ'), (0x2CB7, 'V'), - (0x2CB8, 'M', u'ⲹ'), + (0x2CB8, 'M', 'ⲹ'), (0x2CB9, 'V'), - (0x2CBA, 'M', u'ⲻ'), + (0x2CBA, 'M', 'ⲻ'), (0x2CBB, 'V'), - (0x2CBC, 'M', u'ⲽ'), + (0x2CBC, 'M', 'ⲽ'), + (0x2CBD, 'V'), + (0x2CBE, 'M', 'ⲿ'), ] def _seg_26(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2CBD, 'V'), - (0x2CBE, 'M', u'ⲿ'), (0x2CBF, 'V'), - (0x2CC0, 'M', u'ⳁ'), + (0x2CC0, 'M', 'ⳁ'), (0x2CC1, 'V'), - (0x2CC2, 'M', u'ⳃ'), + (0x2CC2, 'M', 'ⳃ'), (0x2CC3, 'V'), - (0x2CC4, 'M', u'ⳅ'), + (0x2CC4, 'M', 'ⳅ'), (0x2CC5, 'V'), - (0x2CC6, 'M', u'ⳇ'), + (0x2CC6, 'M', 'ⳇ'), (0x2CC7, 'V'), - (0x2CC8, 'M', u'ⳉ'), + (0x2CC8, 'M', 'ⳉ'), (0x2CC9, 'V'), - (0x2CCA, 'M', u'ⳋ'), + (0x2CCA, 'M', 'ⳋ'), (0x2CCB, 'V'), - (0x2CCC, 'M', u'ⳍ'), + (0x2CCC, 'M', 'ⳍ'), (0x2CCD, 'V'), - (0x2CCE, 'M', u'ⳏ'), + (0x2CCE, 'M', 'ⳏ'), (0x2CCF, 'V'), - (0x2CD0, 'M', u'ⳑ'), + (0x2CD0, 'M', 'ⳑ'), (0x2CD1, 'V'), - (0x2CD2, 'M', u'ⳓ'), + (0x2CD2, 'M', 'ⳓ'), (0x2CD3, 'V'), - (0x2CD4, 'M', u'ⳕ'), + (0x2CD4, 'M', 'ⳕ'), (0x2CD5, 'V'), - (0x2CD6, 'M', u'ⳗ'), + (0x2CD6, 'M', 'ⳗ'), (0x2CD7, 'V'), - (0x2CD8, 'M', u'ⳙ'), + (0x2CD8, 'M', 'ⳙ'), (0x2CD9, 'V'), - (0x2CDA, 'M', u'ⳛ'), + (0x2CDA, 'M', 'ⳛ'), (0x2CDB, 'V'), - (0x2CDC, 'M', u'ⳝ'), + (0x2CDC, 'M', 'ⳝ'), (0x2CDD, 'V'), - (0x2CDE, 'M', u'ⳟ'), + (0x2CDE, 'M', 'ⳟ'), (0x2CDF, 'V'), - (0x2CE0, 'M', u'ⳡ'), + (0x2CE0, 'M', 'ⳡ'), (0x2CE1, 'V'), - (0x2CE2, 'M', u'ⳣ'), + (0x2CE2, 'M', 'ⳣ'), (0x2CE3, 'V'), - (0x2CEB, 'M', u'ⳬ'), + (0x2CEB, 'M', 'ⳬ'), (0x2CEC, 'V'), - (0x2CED, 'M', u'ⳮ'), + (0x2CED, 'M', 'ⳮ'), (0x2CEE, 'V'), - (0x2CF2, 'M', u'ⳳ'), + (0x2CF2, 'M', 'ⳳ'), (0x2CF3, 'V'), (0x2CF4, 'X'), (0x2CF9, 'V'), @@ -2765,7 +2791,7 @@ def _seg_26(): (0x2D2E, 'X'), (0x2D30, 'V'), (0x2D68, 'X'), - (0x2D6F, 'M', u'ⵡ'), + (0x2D6F, 'M', 'ⵡ'), (0x2D70, 'V'), (0x2D71, 'X'), (0x2D7F, 'V'), @@ -2787,1160 +2813,1172 @@ def _seg_26(): (0x2DD8, 'V'), (0x2DDF, 'X'), (0x2DE0, 'V'), - (0x2E50, 'X'), + (0x2E53, 'X'), (0x2E80, 'V'), (0x2E9A, 'X'), (0x2E9B, 'V'), - (0x2E9F, 'M', u'母'), + (0x2E9F, 'M', '母'), (0x2EA0, 'V'), - (0x2EF3, 'M', u'龟'), + (0x2EF3, 'M', '龟'), (0x2EF4, 'X'), - (0x2F00, 'M', u'一'), - (0x2F01, 'M', u'丨'), - (0x2F02, 'M', u'丶'), - (0x2F03, 'M', u'丿'), - (0x2F04, 'M', u'乙'), - (0x2F05, 'M', u'亅'), - (0x2F06, 'M', u'二'), - (0x2F07, 'M', u'亠'), - (0x2F08, 'M', u'人'), - (0x2F09, 'M', u'儿'), - (0x2F0A, 'M', u'入'), - (0x2F0B, 'M', u'八'), - (0x2F0C, 'M', u'冂'), - (0x2F0D, 'M', u'冖'), - (0x2F0E, 'M', u'冫'), - (0x2F0F, 'M', u'几'), + (0x2F00, 'M', '一'), + (0x2F01, 'M', '丨'), + (0x2F02, 'M', '丶'), + (0x2F03, 'M', '丿'), + (0x2F04, 'M', '乙'), + (0x2F05, 'M', '亅'), + (0x2F06, 'M', '二'), + (0x2F07, 'M', '亠'), + (0x2F08, 'M', '人'), + (0x2F09, 'M', '儿'), + (0x2F0A, 'M', '入'), + (0x2F0B, 'M', '八'), + (0x2F0C, 'M', '冂'), + (0x2F0D, 'M', '冖'), + (0x2F0E, 'M', '冫'), + (0x2F0F, 'M', '几'), + (0x2F10, 'M', '凵'), + (0x2F11, 'M', '刀'), ] def _seg_27(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2F10, 'M', u'凵'), - (0x2F11, 'M', u'刀'), - (0x2F12, 'M', u'力'), - (0x2F13, 'M', u'勹'), - (0x2F14, 'M', u'匕'), - (0x2F15, 'M', u'匚'), - (0x2F16, 'M', u'匸'), - (0x2F17, 'M', u'十'), - (0x2F18, 'M', u'卜'), - (0x2F19, 'M', u'卩'), - (0x2F1A, 'M', u'厂'), - (0x2F1B, 'M', u'厶'), - (0x2F1C, 'M', u'又'), - (0x2F1D, 'M', u'口'), - (0x2F1E, 'M', u'囗'), - (0x2F1F, 'M', u'土'), - (0x2F20, 'M', u'士'), - (0x2F21, 'M', u'夂'), - (0x2F22, 'M', u'夊'), - (0x2F23, 'M', u'夕'), - (0x2F24, 'M', u'大'), - (0x2F25, 'M', u'女'), - (0x2F26, 'M', u'子'), - (0x2F27, 'M', u'宀'), - (0x2F28, 'M', u'寸'), - (0x2F29, 'M', u'小'), - (0x2F2A, 'M', u'尢'), - (0x2F2B, 'M', u'尸'), - (0x2F2C, 'M', u'屮'), - (0x2F2D, 'M', u'山'), - (0x2F2E, 'M', u'巛'), - (0x2F2F, 'M', u'工'), - (0x2F30, 'M', u'己'), - (0x2F31, 'M', u'巾'), - (0x2F32, 'M', u'干'), - (0x2F33, 'M', u'幺'), - (0x2F34, 'M', u'广'), - (0x2F35, 'M', u'廴'), - (0x2F36, 'M', u'廾'), - (0x2F37, 'M', u'弋'), - (0x2F38, 'M', u'弓'), - (0x2F39, 'M', u'彐'), - (0x2F3A, 'M', u'彡'), - (0x2F3B, 'M', u'彳'), - (0x2F3C, 'M', u'心'), - (0x2F3D, 'M', u'戈'), - (0x2F3E, 'M', u'戶'), - (0x2F3F, 'M', u'手'), - (0x2F40, 'M', u'支'), - (0x2F41, 'M', u'攴'), - (0x2F42, 'M', u'文'), - (0x2F43, 'M', u'斗'), - (0x2F44, 'M', u'斤'), - (0x2F45, 'M', u'方'), - (0x2F46, 'M', u'无'), - (0x2F47, 'M', u'日'), - (0x2F48, 'M', u'曰'), - (0x2F49, 'M', u'月'), - (0x2F4A, 'M', u'木'), - (0x2F4B, 'M', u'欠'), - (0x2F4C, 'M', u'止'), - (0x2F4D, 'M', u'歹'), - (0x2F4E, 'M', u'殳'), - (0x2F4F, 'M', u'毋'), - (0x2F50, 'M', u'比'), - (0x2F51, 'M', u'毛'), - (0x2F52, 'M', u'氏'), - (0x2F53, 'M', u'气'), - (0x2F54, 'M', u'水'), - (0x2F55, 'M', u'火'), - (0x2F56, 'M', u'爪'), - (0x2F57, 'M', u'父'), - (0x2F58, 'M', u'爻'), - (0x2F59, 'M', u'爿'), - (0x2F5A, 'M', u'片'), - (0x2F5B, 'M', u'牙'), - (0x2F5C, 'M', u'牛'), - (0x2F5D, 'M', u'犬'), - (0x2F5E, 'M', u'玄'), - (0x2F5F, 'M', u'玉'), - (0x2F60, 'M', u'瓜'), - (0x2F61, 'M', u'瓦'), - (0x2F62, 'M', u'甘'), - (0x2F63, 'M', u'生'), - (0x2F64, 'M', u'用'), - (0x2F65, 'M', u'田'), - (0x2F66, 'M', u'疋'), - (0x2F67, 'M', u'疒'), - (0x2F68, 'M', u'癶'), - (0x2F69, 'M', u'白'), - (0x2F6A, 'M', u'皮'), - (0x2F6B, 'M', u'皿'), - (0x2F6C, 'M', u'目'), - (0x2F6D, 'M', u'矛'), - (0x2F6E, 'M', u'矢'), - (0x2F6F, 'M', u'石'), - (0x2F70, 'M', u'示'), - (0x2F71, 'M', u'禸'), - (0x2F72, 'M', u'禾'), - (0x2F73, 'M', u'穴'), + (0x2F12, 'M', '力'), + (0x2F13, 'M', '勹'), + (0x2F14, 'M', '匕'), + (0x2F15, 'M', '匚'), + (0x2F16, 'M', '匸'), + (0x2F17, 'M', '十'), + (0x2F18, 'M', '卜'), + (0x2F19, 'M', '卩'), + (0x2F1A, 'M', '厂'), + (0x2F1B, 'M', '厶'), + (0x2F1C, 'M', '又'), + (0x2F1D, 'M', '口'), + (0x2F1E, 'M', '囗'), + (0x2F1F, 'M', '土'), + (0x2F20, 'M', '士'), + (0x2F21, 'M', '夂'), + (0x2F22, 'M', '夊'), + (0x2F23, 'M', '夕'), + (0x2F24, 'M', '大'), + (0x2F25, 'M', '女'), + (0x2F26, 'M', '子'), + (0x2F27, 'M', '宀'), + (0x2F28, 'M', '寸'), + (0x2F29, 'M', '小'), + (0x2F2A, 'M', '尢'), + (0x2F2B, 'M', '尸'), + (0x2F2C, 'M', '屮'), + (0x2F2D, 'M', '山'), + (0x2F2E, 'M', '巛'), + (0x2F2F, 'M', '工'), + (0x2F30, 'M', '己'), + (0x2F31, 'M', '巾'), + (0x2F32, 'M', '干'), + (0x2F33, 'M', '幺'), + (0x2F34, 'M', '广'), + (0x2F35, 'M', '廴'), + (0x2F36, 'M', '廾'), + (0x2F37, 'M', '弋'), + (0x2F38, 'M', '弓'), + (0x2F39, 'M', '彐'), + (0x2F3A, 'M', '彡'), + (0x2F3B, 'M', '彳'), + (0x2F3C, 'M', '心'), + (0x2F3D, 'M', '戈'), + (0x2F3E, 'M', '戶'), + (0x2F3F, 'M', '手'), + (0x2F40, 'M', '支'), + (0x2F41, 'M', '攴'), + (0x2F42, 'M', '文'), + (0x2F43, 'M', '斗'), + (0x2F44, 'M', '斤'), + (0x2F45, 'M', '方'), + (0x2F46, 'M', '无'), + (0x2F47, 'M', '日'), + (0x2F48, 'M', '曰'), + (0x2F49, 'M', '月'), + (0x2F4A, 'M', '木'), + (0x2F4B, 'M', '欠'), + (0x2F4C, 'M', '止'), + (0x2F4D, 'M', '歹'), + (0x2F4E, 'M', '殳'), + (0x2F4F, 'M', '毋'), + (0x2F50, 'M', '比'), + (0x2F51, 'M', '毛'), + (0x2F52, 'M', '氏'), + (0x2F53, 'M', '气'), + (0x2F54, 'M', '水'), + (0x2F55, 'M', '火'), + (0x2F56, 'M', '爪'), + (0x2F57, 'M', '父'), + (0x2F58, 'M', '爻'), + (0x2F59, 'M', '爿'), + (0x2F5A, 'M', '片'), + (0x2F5B, 'M', '牙'), + (0x2F5C, 'M', '牛'), + (0x2F5D, 'M', '犬'), + (0x2F5E, 'M', '玄'), + (0x2F5F, 'M', '玉'), + (0x2F60, 'M', '瓜'), + (0x2F61, 'M', '瓦'), + (0x2F62, 'M', '甘'), + (0x2F63, 'M', '生'), + (0x2F64, 'M', '用'), + (0x2F65, 'M', '田'), + (0x2F66, 'M', '疋'), + (0x2F67, 'M', '疒'), + (0x2F68, 'M', '癶'), + (0x2F69, 'M', '白'), + (0x2F6A, 'M', '皮'), + (0x2F6B, 'M', '皿'), + (0x2F6C, 'M', '目'), + (0x2F6D, 'M', '矛'), + (0x2F6E, 'M', '矢'), + (0x2F6F, 'M', '石'), + (0x2F70, 'M', '示'), + (0x2F71, 'M', '禸'), + (0x2F72, 'M', '禾'), + (0x2F73, 'M', '穴'), + (0x2F74, 'M', '立'), + (0x2F75, 'M', '竹'), ] def _seg_28(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2F74, 'M', u'立'), - (0x2F75, 'M', u'竹'), - (0x2F76, 'M', u'米'), - (0x2F77, 'M', u'糸'), - (0x2F78, 'M', u'缶'), - (0x2F79, 'M', u'网'), - (0x2F7A, 'M', u'羊'), - (0x2F7B, 'M', u'羽'), - (0x2F7C, 'M', u'老'), - (0x2F7D, 'M', u'而'), - (0x2F7E, 'M', u'耒'), - (0x2F7F, 'M', u'耳'), - (0x2F80, 'M', u'聿'), - (0x2F81, 'M', u'肉'), - (0x2F82, 'M', u'臣'), - (0x2F83, 'M', u'自'), - (0x2F84, 'M', u'至'), - (0x2F85, 'M', u'臼'), - (0x2F86, 'M', u'舌'), - (0x2F87, 'M', u'舛'), - (0x2F88, 'M', u'舟'), - (0x2F89, 'M', u'艮'), - (0x2F8A, 'M', u'色'), - (0x2F8B, 'M', u'艸'), - (0x2F8C, 'M', u'虍'), - (0x2F8D, 'M', u'虫'), - (0x2F8E, 'M', u'血'), - (0x2F8F, 'M', u'行'), - (0x2F90, 'M', u'衣'), - (0x2F91, 'M', u'襾'), - (0x2F92, 'M', u'見'), - (0x2F93, 'M', u'角'), - (0x2F94, 'M', u'言'), - (0x2F95, 'M', u'谷'), - (0x2F96, 'M', u'豆'), - (0x2F97, 'M', u'豕'), - (0x2F98, 'M', u'豸'), - (0x2F99, 'M', u'貝'), - (0x2F9A, 'M', u'赤'), - (0x2F9B, 'M', u'走'), - (0x2F9C, 'M', u'足'), - (0x2F9D, 'M', u'身'), - (0x2F9E, 'M', u'車'), - (0x2F9F, 'M', u'辛'), - (0x2FA0, 'M', u'辰'), - (0x2FA1, 'M', u'辵'), - (0x2FA2, 'M', u'邑'), - (0x2FA3, 'M', u'酉'), - (0x2FA4, 'M', u'釆'), - (0x2FA5, 'M', u'里'), - (0x2FA6, 'M', u'金'), - (0x2FA7, 'M', u'長'), - (0x2FA8, 'M', u'門'), - (0x2FA9, 'M', u'阜'), - (0x2FAA, 'M', u'隶'), - (0x2FAB, 'M', u'隹'), - (0x2FAC, 'M', u'雨'), - (0x2FAD, 'M', u'靑'), - (0x2FAE, 'M', u'非'), - (0x2FAF, 'M', u'面'), - (0x2FB0, 'M', u'革'), - (0x2FB1, 'M', u'韋'), - (0x2FB2, 'M', u'韭'), - (0x2FB3, 'M', u'音'), - (0x2FB4, 'M', u'頁'), - (0x2FB5, 'M', u'風'), - (0x2FB6, 'M', u'飛'), - (0x2FB7, 'M', u'食'), - (0x2FB8, 'M', u'首'), - (0x2FB9, 'M', u'香'), - (0x2FBA, 'M', u'馬'), - (0x2FBB, 'M', u'骨'), - (0x2FBC, 'M', u'高'), - (0x2FBD, 'M', u'髟'), - (0x2FBE, 'M', u'鬥'), - (0x2FBF, 'M', u'鬯'), - (0x2FC0, 'M', u'鬲'), - (0x2FC1, 'M', u'鬼'), - (0x2FC2, 'M', u'魚'), - (0x2FC3, 'M', u'鳥'), - (0x2FC4, 'M', u'鹵'), - (0x2FC5, 'M', u'鹿'), - (0x2FC6, 'M', u'麥'), - (0x2FC7, 'M', u'麻'), - (0x2FC8, 'M', u'黃'), - (0x2FC9, 'M', u'黍'), - (0x2FCA, 'M', u'黑'), - (0x2FCB, 'M', u'黹'), - (0x2FCC, 'M', u'黽'), - (0x2FCD, 'M', u'鼎'), - (0x2FCE, 'M', u'鼓'), - (0x2FCF, 'M', u'鼠'), - (0x2FD0, 'M', u'鼻'), - (0x2FD1, 'M', u'齊'), - (0x2FD2, 'M', u'齒'), - (0x2FD3, 'M', u'龍'), - (0x2FD4, 'M', u'龜'), - (0x2FD5, 'M', u'龠'), + (0x2F76, 'M', '米'), + (0x2F77, 'M', '糸'), + (0x2F78, 'M', '缶'), + (0x2F79, 'M', '网'), + (0x2F7A, 'M', '羊'), + (0x2F7B, 'M', '羽'), + (0x2F7C, 'M', '老'), + (0x2F7D, 'M', '而'), + (0x2F7E, 'M', '耒'), + (0x2F7F, 'M', '耳'), + (0x2F80, 'M', '聿'), + (0x2F81, 'M', '肉'), + (0x2F82, 'M', '臣'), + (0x2F83, 'M', '自'), + (0x2F84, 'M', '至'), + (0x2F85, 'M', '臼'), + (0x2F86, 'M', '舌'), + (0x2F87, 'M', '舛'), + (0x2F88, 'M', '舟'), + (0x2F89, 'M', '艮'), + (0x2F8A, 'M', '色'), + (0x2F8B, 'M', '艸'), + (0x2F8C, 'M', '虍'), + (0x2F8D, 'M', '虫'), + (0x2F8E, 'M', '血'), + (0x2F8F, 'M', '行'), + (0x2F90, 'M', '衣'), + (0x2F91, 'M', '襾'), + (0x2F92, 'M', '見'), + (0x2F93, 'M', '角'), + (0x2F94, 'M', '言'), + (0x2F95, 'M', '谷'), + (0x2F96, 'M', '豆'), + (0x2F97, 'M', '豕'), + (0x2F98, 'M', '豸'), + (0x2F99, 'M', '貝'), + (0x2F9A, 'M', '赤'), + (0x2F9B, 'M', '走'), + (0x2F9C, 'M', '足'), + (0x2F9D, 'M', '身'), + (0x2F9E, 'M', '車'), + (0x2F9F, 'M', '辛'), + (0x2FA0, 'M', '辰'), + (0x2FA1, 'M', '辵'), + (0x2FA2, 'M', '邑'), + (0x2FA3, 'M', '酉'), + (0x2FA4, 'M', '釆'), + (0x2FA5, 'M', '里'), + (0x2FA6, 'M', '金'), + (0x2FA7, 'M', '長'), + (0x2FA8, 'M', '門'), + (0x2FA9, 'M', '阜'), + (0x2FAA, 'M', '隶'), + (0x2FAB, 'M', '隹'), + (0x2FAC, 'M', '雨'), + (0x2FAD, 'M', '靑'), + (0x2FAE, 'M', '非'), + (0x2FAF, 'M', '面'), + (0x2FB0, 'M', '革'), + (0x2FB1, 'M', '韋'), + (0x2FB2, 'M', '韭'), + (0x2FB3, 'M', '音'), + (0x2FB4, 'M', '頁'), + (0x2FB5, 'M', '風'), + (0x2FB6, 'M', '飛'), + (0x2FB7, 'M', '食'), + (0x2FB8, 'M', '首'), + (0x2FB9, 'M', '香'), + (0x2FBA, 'M', '馬'), + (0x2FBB, 'M', '骨'), + (0x2FBC, 'M', '高'), + (0x2FBD, 'M', '髟'), + (0x2FBE, 'M', '鬥'), + (0x2FBF, 'M', '鬯'), + (0x2FC0, 'M', '鬲'), + (0x2FC1, 'M', '鬼'), + (0x2FC2, 'M', '魚'), + (0x2FC3, 'M', '鳥'), + (0x2FC4, 'M', '鹵'), + (0x2FC5, 'M', '鹿'), + (0x2FC6, 'M', '麥'), + (0x2FC7, 'M', '麻'), + (0x2FC8, 'M', '黃'), + (0x2FC9, 'M', '黍'), + (0x2FCA, 'M', '黑'), + (0x2FCB, 'M', '黹'), + (0x2FCC, 'M', '黽'), + (0x2FCD, 'M', '鼎'), + (0x2FCE, 'M', '鼓'), + (0x2FCF, 'M', '鼠'), + (0x2FD0, 'M', '鼻'), + (0x2FD1, 'M', '齊'), + (0x2FD2, 'M', '齒'), + (0x2FD3, 'M', '龍'), + (0x2FD4, 'M', '龜'), + (0x2FD5, 'M', '龠'), (0x2FD6, 'X'), - (0x3000, '3', u' '), + (0x3000, '3', ' '), + (0x3001, 'V'), + (0x3002, 'M', '.'), ] def _seg_29(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x3001, 'V'), - (0x3002, 'M', u'.'), (0x3003, 'V'), - (0x3036, 'M', u'〒'), + (0x3036, 'M', '〒'), (0x3037, 'V'), - (0x3038, 'M', u'十'), - (0x3039, 'M', u'卄'), - (0x303A, 'M', u'卅'), + (0x3038, 'M', '十'), + (0x3039, 'M', '卄'), + (0x303A, 'M', '卅'), (0x303B, 'V'), (0x3040, 'X'), (0x3041, 'V'), (0x3097, 'X'), (0x3099, 'V'), - (0x309B, '3', u' ゙'), - (0x309C, '3', u' ゚'), + (0x309B, '3', ' ゙'), + (0x309C, '3', ' ゚'), (0x309D, 'V'), - (0x309F, 'M', u'より'), + (0x309F, 'M', 'より'), (0x30A0, 'V'), - (0x30FF, 'M', u'コト'), + (0x30FF, 'M', 'コト'), (0x3100, 'X'), (0x3105, 'V'), (0x3130, 'X'), - (0x3131, 'M', u'ᄀ'), - (0x3132, 'M', u'ᄁ'), - (0x3133, 'M', u'ᆪ'), - (0x3134, 'M', u'ᄂ'), - (0x3135, 'M', u'ᆬ'), - (0x3136, 'M', u'ᆭ'), - (0x3137, 'M', u'ᄃ'), - (0x3138, 'M', u'ᄄ'), - (0x3139, 'M', u'ᄅ'), - (0x313A, 'M', u'ᆰ'), - (0x313B, 'M', u'ᆱ'), - (0x313C, 'M', u'ᆲ'), - (0x313D, 'M', u'ᆳ'), - (0x313E, 'M', u'ᆴ'), - (0x313F, 'M', u'ᆵ'), - (0x3140, 'M', u'ᄚ'), - (0x3141, 'M', u'ᄆ'), - (0x3142, 'M', u'ᄇ'), - (0x3143, 'M', u'ᄈ'), - (0x3144, 'M', u'ᄡ'), - (0x3145, 'M', u'ᄉ'), - (0x3146, 'M', u'ᄊ'), - (0x3147, 'M', u'ᄋ'), - (0x3148, 'M', u'ᄌ'), - (0x3149, 'M', u'ᄍ'), - (0x314A, 'M', u'ᄎ'), - (0x314B, 'M', u'ᄏ'), - (0x314C, 'M', u'ᄐ'), - (0x314D, 'M', u'ᄑ'), - (0x314E, 'M', u'ᄒ'), - (0x314F, 'M', u'ᅡ'), - (0x3150, 'M', u'ᅢ'), - (0x3151, 'M', u'ᅣ'), - (0x3152, 'M', u'ᅤ'), - (0x3153, 'M', u'ᅥ'), - (0x3154, 'M', u'ᅦ'), - (0x3155, 'M', u'ᅧ'), - (0x3156, 'M', u'ᅨ'), - (0x3157, 'M', u'ᅩ'), - (0x3158, 'M', u'ᅪ'), - (0x3159, 'M', u'ᅫ'), - (0x315A, 'M', u'ᅬ'), - (0x315B, 'M', u'ᅭ'), - (0x315C, 'M', u'ᅮ'), - (0x315D, 'M', u'ᅯ'), - (0x315E, 'M', u'ᅰ'), - (0x315F, 'M', u'ᅱ'), - (0x3160, 'M', u'ᅲ'), - (0x3161, 'M', u'ᅳ'), - (0x3162, 'M', u'ᅴ'), - (0x3163, 'M', u'ᅵ'), + (0x3131, 'M', 'ᄀ'), + (0x3132, 'M', 'ᄁ'), + (0x3133, 'M', 'ᆪ'), + (0x3134, 'M', 'ᄂ'), + (0x3135, 'M', 'ᆬ'), + (0x3136, 'M', 'ᆭ'), + (0x3137, 'M', 'ᄃ'), + (0x3138, 'M', 'ᄄ'), + (0x3139, 'M', 'ᄅ'), + (0x313A, 'M', 'ᆰ'), + (0x313B, 'M', 'ᆱ'), + (0x313C, 'M', 'ᆲ'), + (0x313D, 'M', 'ᆳ'), + (0x313E, 'M', 'ᆴ'), + (0x313F, 'M', 'ᆵ'), + (0x3140, 'M', 'ᄚ'), + (0x3141, 'M', 'ᄆ'), + (0x3142, 'M', 'ᄇ'), + (0x3143, 'M', 'ᄈ'), + (0x3144, 'M', 'ᄡ'), + (0x3145, 'M', 'ᄉ'), + (0x3146, 'M', 'ᄊ'), + (0x3147, 'M', 'ᄋ'), + (0x3148, 'M', 'ᄌ'), + (0x3149, 'M', 'ᄍ'), + (0x314A, 'M', 'ᄎ'), + (0x314B, 'M', 'ᄏ'), + (0x314C, 'M', 'ᄐ'), + (0x314D, 'M', 'ᄑ'), + (0x314E, 'M', 'ᄒ'), + (0x314F, 'M', 'ᅡ'), + (0x3150, 'M', 'ᅢ'), + (0x3151, 'M', 'ᅣ'), + (0x3152, 'M', 'ᅤ'), + (0x3153, 'M', 'ᅥ'), + (0x3154, 'M', 'ᅦ'), + (0x3155, 'M', 'ᅧ'), + (0x3156, 'M', 'ᅨ'), + (0x3157, 'M', 'ᅩ'), + (0x3158, 'M', 'ᅪ'), + (0x3159, 'M', 'ᅫ'), + (0x315A, 'M', 'ᅬ'), + (0x315B, 'M', 'ᅭ'), + (0x315C, 'M', 'ᅮ'), + (0x315D, 'M', 'ᅯ'), + (0x315E, 'M', 'ᅰ'), + (0x315F, 'M', 'ᅱ'), + (0x3160, 'M', 'ᅲ'), + (0x3161, 'M', 'ᅳ'), + (0x3162, 'M', 'ᅴ'), + (0x3163, 'M', 'ᅵ'), (0x3164, 'X'), - (0x3165, 'M', u'ᄔ'), - (0x3166, 'M', u'ᄕ'), - (0x3167, 'M', u'ᇇ'), - (0x3168, 'M', u'ᇈ'), - (0x3169, 'M', u'ᇌ'), - (0x316A, 'M', u'ᇎ'), - (0x316B, 'M', u'ᇓ'), - (0x316C, 'M', u'ᇗ'), - (0x316D, 'M', u'ᇙ'), - (0x316E, 'M', u'ᄜ'), - (0x316F, 'M', u'ᇝ'), - (0x3170, 'M', u'ᇟ'), - (0x3171, 'M', u'ᄝ'), - (0x3172, 'M', u'ᄞ'), - (0x3173, 'M', u'ᄠ'), - (0x3174, 'M', u'ᄢ'), - (0x3175, 'M', u'ᄣ'), - (0x3176, 'M', u'ᄧ'), - (0x3177, 'M', u'ᄩ'), - (0x3178, 'M', u'ᄫ'), - (0x3179, 'M', u'ᄬ'), - (0x317A, 'M', u'ᄭ'), - (0x317B, 'M', u'ᄮ'), - (0x317C, 'M', u'ᄯ'), - (0x317D, 'M', u'ᄲ'), - (0x317E, 'M', u'ᄶ'), + (0x3165, 'M', 'ᄔ'), + (0x3166, 'M', 'ᄕ'), + (0x3167, 'M', 'ᇇ'), + (0x3168, 'M', 'ᇈ'), + (0x3169, 'M', 'ᇌ'), + (0x316A, 'M', 'ᇎ'), + (0x316B, 'M', 'ᇓ'), + (0x316C, 'M', 'ᇗ'), + (0x316D, 'M', 'ᇙ'), + (0x316E, 'M', 'ᄜ'), + (0x316F, 'M', 'ᇝ'), + (0x3170, 'M', 'ᇟ'), + (0x3171, 'M', 'ᄝ'), + (0x3172, 'M', 'ᄞ'), + (0x3173, 'M', 'ᄠ'), + (0x3174, 'M', 'ᄢ'), + (0x3175, 'M', 'ᄣ'), + (0x3176, 'M', 'ᄧ'), + (0x3177, 'M', 'ᄩ'), + (0x3178, 'M', 'ᄫ'), + (0x3179, 'M', 'ᄬ'), + (0x317A, 'M', 'ᄭ'), + (0x317B, 'M', 'ᄮ'), + (0x317C, 'M', 'ᄯ'), + (0x317D, 'M', 'ᄲ'), + (0x317E, 'M', 'ᄶ'), + (0x317F, 'M', 'ᅀ'), + (0x3180, 'M', 'ᅇ'), ] def _seg_30(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x317F, 'M', u'ᅀ'), - (0x3180, 'M', u'ᅇ'), - (0x3181, 'M', u'ᅌ'), - (0x3182, 'M', u'ᇱ'), - (0x3183, 'M', u'ᇲ'), - (0x3184, 'M', u'ᅗ'), - (0x3185, 'M', u'ᅘ'), - (0x3186, 'M', u'ᅙ'), - (0x3187, 'M', u'ᆄ'), - (0x3188, 'M', u'ᆅ'), - (0x3189, 'M', u'ᆈ'), - (0x318A, 'M', u'ᆑ'), - (0x318B, 'M', u'ᆒ'), - (0x318C, 'M', u'ᆔ'), - (0x318D, 'M', u'ᆞ'), - (0x318E, 'M', u'ᆡ'), + (0x3181, 'M', 'ᅌ'), + (0x3182, 'M', 'ᇱ'), + (0x3183, 'M', 'ᇲ'), + (0x3184, 'M', 'ᅗ'), + (0x3185, 'M', 'ᅘ'), + (0x3186, 'M', 'ᅙ'), + (0x3187, 'M', 'ᆄ'), + (0x3188, 'M', 'ᆅ'), + (0x3189, 'M', 'ᆈ'), + (0x318A, 'M', 'ᆑ'), + (0x318B, 'M', 'ᆒ'), + (0x318C, 'M', 'ᆔ'), + (0x318D, 'M', 'ᆞ'), + (0x318E, 'M', 'ᆡ'), (0x318F, 'X'), (0x3190, 'V'), - (0x3192, 'M', u'一'), - (0x3193, 'M', u'二'), - (0x3194, 'M', u'三'), - (0x3195, 'M', u'四'), - (0x3196, 'M', u'上'), - (0x3197, 'M', u'中'), - (0x3198, 'M', u'下'), - (0x3199, 'M', u'甲'), - (0x319A, 'M', u'乙'), - (0x319B, 'M', u'丙'), - (0x319C, 'M', u'丁'), - (0x319D, 'M', u'天'), - (0x319E, 'M', u'地'), - (0x319F, 'M', u'人'), + (0x3192, 'M', '一'), + (0x3193, 'M', '二'), + (0x3194, 'M', '三'), + (0x3195, 'M', '四'), + (0x3196, 'M', '上'), + (0x3197, 'M', '中'), + (0x3198, 'M', '下'), + (0x3199, 'M', '甲'), + (0x319A, 'M', '乙'), + (0x319B, 'M', '丙'), + (0x319C, 'M', '丁'), + (0x319D, 'M', '天'), + (0x319E, 'M', '地'), + (0x319F, 'M', '人'), (0x31A0, 'V'), - (0x31BB, 'X'), - (0x31C0, 'V'), (0x31E4, 'X'), (0x31F0, 'V'), - (0x3200, '3', u'(ᄀ)'), - (0x3201, '3', u'(ᄂ)'), - (0x3202, '3', u'(ᄃ)'), - (0x3203, '3', u'(ᄅ)'), - (0x3204, '3', u'(ᄆ)'), - (0x3205, '3', u'(ᄇ)'), - (0x3206, '3', u'(ᄉ)'), - (0x3207, '3', u'(ᄋ)'), - (0x3208, '3', u'(ᄌ)'), - (0x3209, '3', u'(ᄎ)'), - (0x320A, '3', u'(ᄏ)'), - (0x320B, '3', u'(ᄐ)'), - (0x320C, '3', u'(ᄑ)'), - (0x320D, '3', u'(ᄒ)'), - (0x320E, '3', u'(가)'), - (0x320F, '3', u'(나)'), - (0x3210, '3', u'(다)'), - (0x3211, '3', u'(라)'), - (0x3212, '3', u'(마)'), - (0x3213, '3', u'(바)'), - (0x3214, '3', u'(사)'), - (0x3215, '3', u'(아)'), - (0x3216, '3', u'(자)'), - (0x3217, '3', u'(차)'), - (0x3218, '3', u'(카)'), - (0x3219, '3', u'(타)'), - (0x321A, '3', u'(파)'), - (0x321B, '3', u'(하)'), - (0x321C, '3', u'(주)'), - (0x321D, '3', u'(오전)'), - (0x321E, '3', u'(오후)'), + (0x3200, '3', '(ᄀ)'), + (0x3201, '3', '(ᄂ)'), + (0x3202, '3', '(ᄃ)'), + (0x3203, '3', '(ᄅ)'), + (0x3204, '3', '(ᄆ)'), + (0x3205, '3', '(ᄇ)'), + (0x3206, '3', '(ᄉ)'), + (0x3207, '3', '(ᄋ)'), + (0x3208, '3', '(ᄌ)'), + (0x3209, '3', '(ᄎ)'), + (0x320A, '3', '(ᄏ)'), + (0x320B, '3', '(ᄐ)'), + (0x320C, '3', '(ᄑ)'), + (0x320D, '3', '(ᄒ)'), + (0x320E, '3', '(가)'), + (0x320F, '3', '(나)'), + (0x3210, '3', '(다)'), + (0x3211, '3', '(라)'), + (0x3212, '3', '(마)'), + (0x3213, '3', '(바)'), + (0x3214, '3', '(사)'), + (0x3215, '3', '(아)'), + (0x3216, '3', '(자)'), + (0x3217, '3', '(차)'), + (0x3218, '3', '(카)'), + (0x3219, '3', '(타)'), + (0x321A, '3', '(파)'), + (0x321B, '3', '(하)'), + (0x321C, '3', '(주)'), + (0x321D, '3', '(오전)'), + (0x321E, '3', '(오후)'), (0x321F, 'X'), - (0x3220, '3', u'(一)'), - (0x3221, '3', u'(二)'), - (0x3222, '3', u'(三)'), - (0x3223, '3', u'(四)'), - (0x3224, '3', u'(五)'), - (0x3225, '3', u'(六)'), - (0x3226, '3', u'(七)'), - (0x3227, '3', u'(八)'), - (0x3228, '3', u'(九)'), - (0x3229, '3', u'(十)'), - (0x322A, '3', u'(月)'), - (0x322B, '3', u'(火)'), - (0x322C, '3', u'(水)'), - (0x322D, '3', u'(木)'), - (0x322E, '3', u'(金)'), - (0x322F, '3', u'(土)'), - (0x3230, '3', u'(日)'), - (0x3231, '3', u'(株)'), - (0x3232, '3', u'(有)'), - (0x3233, '3', u'(社)'), - (0x3234, '3', u'(名)'), - (0x3235, '3', u'(特)'), - (0x3236, '3', u'(財)'), - (0x3237, '3', u'(祝)'), - (0x3238, '3', u'(労)'), - (0x3239, '3', u'(代)'), - (0x323A, '3', u'(呼)'), - (0x323B, '3', u'(学)'), - (0x323C, '3', u'(監)'), - (0x323D, '3', u'(企)'), - (0x323E, '3', u'(資)'), + (0x3220, '3', '(一)'), + (0x3221, '3', '(二)'), + (0x3222, '3', '(三)'), + (0x3223, '3', '(四)'), + (0x3224, '3', '(五)'), + (0x3225, '3', '(六)'), + (0x3226, '3', '(七)'), + (0x3227, '3', '(八)'), + (0x3228, '3', '(九)'), + (0x3229, '3', '(十)'), + (0x322A, '3', '(月)'), + (0x322B, '3', '(火)'), + (0x322C, '3', '(水)'), + (0x322D, '3', '(木)'), + (0x322E, '3', '(金)'), + (0x322F, '3', '(土)'), + (0x3230, '3', '(日)'), + (0x3231, '3', '(株)'), + (0x3232, '3', '(有)'), + (0x3233, '3', '(社)'), + (0x3234, '3', '(名)'), + (0x3235, '3', '(特)'), + (0x3236, '3', '(財)'), + (0x3237, '3', '(祝)'), + (0x3238, '3', '(労)'), + (0x3239, '3', '(代)'), + (0x323A, '3', '(呼)'), + (0x323B, '3', '(学)'), + (0x323C, '3', '(監)'), + (0x323D, '3', '(企)'), + (0x323E, '3', '(資)'), + (0x323F, '3', '(協)'), + (0x3240, '3', '(祭)'), + (0x3241, '3', '(休)'), + (0x3242, '3', '(自)'), ] def _seg_31(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x323F, '3', u'(協)'), - (0x3240, '3', u'(祭)'), - (0x3241, '3', u'(休)'), - (0x3242, '3', u'(自)'), - (0x3243, '3', u'(至)'), - (0x3244, 'M', u'問'), - (0x3245, 'M', u'幼'), - (0x3246, 'M', u'文'), - (0x3247, 'M', u'箏'), + (0x3243, '3', '(至)'), + (0x3244, 'M', '問'), + (0x3245, 'M', '幼'), + (0x3246, 'M', '文'), + (0x3247, 'M', '箏'), (0x3248, 'V'), - (0x3250, 'M', u'pte'), - (0x3251, 'M', u'21'), - (0x3252, 'M', u'22'), - (0x3253, 'M', u'23'), - (0x3254, 'M', u'24'), - (0x3255, 'M', u'25'), - (0x3256, 'M', u'26'), - (0x3257, 'M', u'27'), - (0x3258, 'M', u'28'), - (0x3259, 'M', u'29'), - (0x325A, 'M', u'30'), - (0x325B, 'M', u'31'), - (0x325C, 'M', u'32'), - (0x325D, 'M', u'33'), - (0x325E, 'M', u'34'), - (0x325F, 'M', u'35'), - (0x3260, 'M', u'ᄀ'), - (0x3261, 'M', u'ᄂ'), - (0x3262, 'M', u'ᄃ'), - (0x3263, 'M', u'ᄅ'), - (0x3264, 'M', u'ᄆ'), - (0x3265, 'M', u'ᄇ'), - (0x3266, 'M', u'ᄉ'), - (0x3267, 'M', u'ᄋ'), - (0x3268, 'M', u'ᄌ'), - (0x3269, 'M', u'ᄎ'), - (0x326A, 'M', u'ᄏ'), - (0x326B, 'M', u'ᄐ'), - (0x326C, 'M', u'ᄑ'), - (0x326D, 'M', u'ᄒ'), - (0x326E, 'M', u'가'), - (0x326F, 'M', u'나'), - (0x3270, 'M', u'다'), - (0x3271, 'M', u'라'), - (0x3272, 'M', u'마'), - (0x3273, 'M', u'바'), - (0x3274, 'M', u'사'), - (0x3275, 'M', u'아'), - (0x3276, 'M', u'자'), - (0x3277, 'M', u'차'), - (0x3278, 'M', u'카'), - (0x3279, 'M', u'타'), - (0x327A, 'M', u'파'), - (0x327B, 'M', u'하'), - (0x327C, 'M', u'참고'), - (0x327D, 'M', u'주의'), - (0x327E, 'M', u'우'), + (0x3250, 'M', 'pte'), + (0x3251, 'M', '21'), + (0x3252, 'M', '22'), + (0x3253, 'M', '23'), + (0x3254, 'M', '24'), + (0x3255, 'M', '25'), + (0x3256, 'M', '26'), + (0x3257, 'M', '27'), + (0x3258, 'M', '28'), + (0x3259, 'M', '29'), + (0x325A, 'M', '30'), + (0x325B, 'M', '31'), + (0x325C, 'M', '32'), + (0x325D, 'M', '33'), + (0x325E, 'M', '34'), + (0x325F, 'M', '35'), + (0x3260, 'M', 'ᄀ'), + (0x3261, 'M', 'ᄂ'), + (0x3262, 'M', 'ᄃ'), + (0x3263, 'M', 'ᄅ'), + (0x3264, 'M', 'ᄆ'), + (0x3265, 'M', 'ᄇ'), + (0x3266, 'M', 'ᄉ'), + (0x3267, 'M', 'ᄋ'), + (0x3268, 'M', 'ᄌ'), + (0x3269, 'M', 'ᄎ'), + (0x326A, 'M', 'ᄏ'), + (0x326B, 'M', 'ᄐ'), + (0x326C, 'M', 'ᄑ'), + (0x326D, 'M', 'ᄒ'), + (0x326E, 'M', '가'), + (0x326F, 'M', '나'), + (0x3270, 'M', '다'), + (0x3271, 'M', '라'), + (0x3272, 'M', '마'), + (0x3273, 'M', '바'), + (0x3274, 'M', '사'), + (0x3275, 'M', '아'), + (0x3276, 'M', '자'), + (0x3277, 'M', '차'), + (0x3278, 'M', '카'), + (0x3279, 'M', '타'), + (0x327A, 'M', '파'), + (0x327B, 'M', '하'), + (0x327C, 'M', '참고'), + (0x327D, 'M', '주의'), + (0x327E, 'M', '우'), (0x327F, 'V'), - (0x3280, 'M', u'一'), - (0x3281, 'M', u'二'), - (0x3282, 'M', u'三'), - (0x3283, 'M', u'四'), - (0x3284, 'M', u'五'), - (0x3285, 'M', u'六'), - (0x3286, 'M', u'七'), - (0x3287, 'M', u'八'), - (0x3288, 'M', u'九'), - (0x3289, 'M', u'十'), - (0x328A, 'M', u'月'), - (0x328B, 'M', u'火'), - (0x328C, 'M', u'水'), - (0x328D, 'M', u'木'), - (0x328E, 'M', u'金'), - (0x328F, 'M', u'土'), - (0x3290, 'M', u'日'), - (0x3291, 'M', u'株'), - (0x3292, 'M', u'有'), - (0x3293, 'M', u'社'), - (0x3294, 'M', u'名'), - (0x3295, 'M', u'特'), - (0x3296, 'M', u'財'), - (0x3297, 'M', u'祝'), - (0x3298, 'M', u'労'), - (0x3299, 'M', u'秘'), - (0x329A, 'M', u'男'), - (0x329B, 'M', u'女'), - (0x329C, 'M', u'適'), - (0x329D, 'M', u'優'), - (0x329E, 'M', u'印'), - (0x329F, 'M', u'注'), - (0x32A0, 'M', u'項'), - (0x32A1, 'M', u'休'), - (0x32A2, 'M', u'写'), - (0x32A3, 'M', u'正'), - (0x32A4, 'M', u'上'), - (0x32A5, 'M', u'中'), - (0x32A6, 'M', u'下'), - (0x32A7, 'M', u'左'), - (0x32A8, 'M', u'右'), - (0x32A9, 'M', u'医'), + (0x3280, 'M', '一'), + (0x3281, 'M', '二'), + (0x3282, 'M', '三'), + (0x3283, 'M', '四'), + (0x3284, 'M', '五'), + (0x3285, 'M', '六'), + (0x3286, 'M', '七'), + (0x3287, 'M', '八'), + (0x3288, 'M', '九'), + (0x3289, 'M', '十'), + (0x328A, 'M', '月'), + (0x328B, 'M', '火'), + (0x328C, 'M', '水'), + (0x328D, 'M', '木'), + (0x328E, 'M', '金'), + (0x328F, 'M', '土'), + (0x3290, 'M', '日'), + (0x3291, 'M', '株'), + (0x3292, 'M', '有'), + (0x3293, 'M', '社'), + (0x3294, 'M', '名'), + (0x3295, 'M', '特'), + (0x3296, 'M', '財'), + (0x3297, 'M', '祝'), + (0x3298, 'M', '労'), + (0x3299, 'M', '秘'), + (0x329A, 'M', '男'), + (0x329B, 'M', '女'), + (0x329C, 'M', '適'), + (0x329D, 'M', '優'), + (0x329E, 'M', '印'), + (0x329F, 'M', '注'), + (0x32A0, 'M', '項'), + (0x32A1, 'M', '休'), + (0x32A2, 'M', '写'), + (0x32A3, 'M', '正'), + (0x32A4, 'M', '上'), + (0x32A5, 'M', '中'), + (0x32A6, 'M', '下'), + (0x32A7, 'M', '左'), + (0x32A8, 'M', '右'), + (0x32A9, 'M', '医'), + (0x32AA, 'M', '宗'), + (0x32AB, 'M', '学'), + (0x32AC, 'M', '監'), + (0x32AD, 'M', '企'), ] def _seg_32(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x32AA, 'M', u'宗'), - (0x32AB, 'M', u'学'), - (0x32AC, 'M', u'監'), - (0x32AD, 'M', u'企'), - (0x32AE, 'M', u'資'), - (0x32AF, 'M', u'協'), - (0x32B0, 'M', u'夜'), - (0x32B1, 'M', u'36'), - (0x32B2, 'M', u'37'), - (0x32B3, 'M', u'38'), - (0x32B4, 'M', u'39'), - (0x32B5, 'M', u'40'), - (0x32B6, 'M', u'41'), - (0x32B7, 'M', u'42'), - (0x32B8, 'M', u'43'), - (0x32B9, 'M', u'44'), - (0x32BA, 'M', u'45'), - (0x32BB, 'M', u'46'), - (0x32BC, 'M', u'47'), - (0x32BD, 'M', u'48'), - (0x32BE, 'M', u'49'), - (0x32BF, 'M', u'50'), - (0x32C0, 'M', u'1月'), - (0x32C1, 'M', u'2月'), - (0x32C2, 'M', u'3月'), - (0x32C3, 'M', u'4月'), - (0x32C4, 'M', u'5月'), - (0x32C5, 'M', u'6月'), - (0x32C6, 'M', u'7月'), - (0x32C7, 'M', u'8月'), - (0x32C8, 'M', u'9月'), - (0x32C9, 'M', u'10月'), - (0x32CA, 'M', u'11月'), - (0x32CB, 'M', u'12月'), - (0x32CC, 'M', u'hg'), - (0x32CD, 'M', u'erg'), - (0x32CE, 'M', u'ev'), - (0x32CF, 'M', u'ltd'), - (0x32D0, 'M', u'ア'), - (0x32D1, 'M', u'イ'), - (0x32D2, 'M', u'ウ'), - (0x32D3, 'M', u'エ'), - (0x32D4, 'M', u'オ'), - (0x32D5, 'M', u'カ'), - (0x32D6, 'M', u'キ'), - (0x32D7, 'M', u'ク'), - (0x32D8, 'M', u'ケ'), - (0x32D9, 'M', u'コ'), - (0x32DA, 'M', u'サ'), - (0x32DB, 'M', u'シ'), - (0x32DC, 'M', u'ス'), - (0x32DD, 'M', u'セ'), - (0x32DE, 'M', u'ソ'), - (0x32DF, 'M', u'タ'), - (0x32E0, 'M', u'チ'), - (0x32E1, 'M', u'ツ'), - (0x32E2, 'M', u'テ'), - (0x32E3, 'M', u'ト'), - (0x32E4, 'M', u'ナ'), - (0x32E5, 'M', u'ニ'), - (0x32E6, 'M', u'ヌ'), - (0x32E7, 'M', u'ネ'), - (0x32E8, 'M', u'ノ'), - (0x32E9, 'M', u'ハ'), - (0x32EA, 'M', u'ヒ'), - (0x32EB, 'M', u'フ'), - (0x32EC, 'M', u'ヘ'), - (0x32ED, 'M', u'ホ'), - (0x32EE, 'M', u'マ'), - (0x32EF, 'M', u'ミ'), - (0x32F0, 'M', u'ム'), - (0x32F1, 'M', u'メ'), - (0x32F2, 'M', u'モ'), - (0x32F3, 'M', u'ヤ'), - (0x32F4, 'M', u'ユ'), - (0x32F5, 'M', u'ヨ'), - (0x32F6, 'M', u'ラ'), - (0x32F7, 'M', u'リ'), - (0x32F8, 'M', u'ル'), - (0x32F9, 'M', u'レ'), - (0x32FA, 'M', u'ロ'), - (0x32FB, 'M', u'ワ'), - (0x32FC, 'M', u'ヰ'), - (0x32FD, 'M', u'ヱ'), - (0x32FE, 'M', u'ヲ'), - (0x32FF, 'M', u'令和'), - (0x3300, 'M', u'アパート'), - (0x3301, 'M', u'アルファ'), - (0x3302, 'M', u'アンペア'), - (0x3303, 'M', u'アール'), - (0x3304, 'M', u'イニング'), - (0x3305, 'M', u'インチ'), - (0x3306, 'M', u'ウォン'), - (0x3307, 'M', u'エスクード'), - (0x3308, 'M', u'エーカー'), - (0x3309, 'M', u'オンス'), - (0x330A, 'M', u'オーム'), - (0x330B, 'M', u'カイリ'), - (0x330C, 'M', u'カラット'), - (0x330D, 'M', u'カロリー'), + (0x32AE, 'M', '資'), + (0x32AF, 'M', '協'), + (0x32B0, 'M', '夜'), + (0x32B1, 'M', '36'), + (0x32B2, 'M', '37'), + (0x32B3, 'M', '38'), + (0x32B4, 'M', '39'), + (0x32B5, 'M', '40'), + (0x32B6, 'M', '41'), + (0x32B7, 'M', '42'), + (0x32B8, 'M', '43'), + (0x32B9, 'M', '44'), + (0x32BA, 'M', '45'), + (0x32BB, 'M', '46'), + (0x32BC, 'M', '47'), + (0x32BD, 'M', '48'), + (0x32BE, 'M', '49'), + (0x32BF, 'M', '50'), + (0x32C0, 'M', '1月'), + (0x32C1, 'M', '2月'), + (0x32C2, 'M', '3月'), + (0x32C3, 'M', '4月'), + (0x32C4, 'M', '5月'), + (0x32C5, 'M', '6月'), + (0x32C6, 'M', '7月'), + (0x32C7, 'M', '8月'), + (0x32C8, 'M', '9月'), + (0x32C9, 'M', '10月'), + (0x32CA, 'M', '11月'), + (0x32CB, 'M', '12月'), + (0x32CC, 'M', 'hg'), + (0x32CD, 'M', 'erg'), + (0x32CE, 'M', 'ev'), + (0x32CF, 'M', 'ltd'), + (0x32D0, 'M', 'ア'), + (0x32D1, 'M', 'イ'), + (0x32D2, 'M', 'ウ'), + (0x32D3, 'M', 'エ'), + (0x32D4, 'M', 'オ'), + (0x32D5, 'M', 'カ'), + (0x32D6, 'M', 'キ'), + (0x32D7, 'M', 'ク'), + (0x32D8, 'M', 'ケ'), + (0x32D9, 'M', 'コ'), + (0x32DA, 'M', 'サ'), + (0x32DB, 'M', 'シ'), + (0x32DC, 'M', 'ス'), + (0x32DD, 'M', 'セ'), + (0x32DE, 'M', 'ソ'), + (0x32DF, 'M', 'タ'), + (0x32E0, 'M', 'チ'), + (0x32E1, 'M', 'ツ'), + (0x32E2, 'M', 'テ'), + (0x32E3, 'M', 'ト'), + (0x32E4, 'M', 'ナ'), + (0x32E5, 'M', 'ニ'), + (0x32E6, 'M', 'ヌ'), + (0x32E7, 'M', 'ネ'), + (0x32E8, 'M', 'ノ'), + (0x32E9, 'M', 'ハ'), + (0x32EA, 'M', 'ヒ'), + (0x32EB, 'M', 'フ'), + (0x32EC, 'M', 'ヘ'), + (0x32ED, 'M', 'ホ'), + (0x32EE, 'M', 'マ'), + (0x32EF, 'M', 'ミ'), + (0x32F0, 'M', 'ム'), + (0x32F1, 'M', 'メ'), + (0x32F2, 'M', 'モ'), + (0x32F3, 'M', 'ヤ'), + (0x32F4, 'M', 'ユ'), + (0x32F5, 'M', 'ヨ'), + (0x32F6, 'M', 'ラ'), + (0x32F7, 'M', 'リ'), + (0x32F8, 'M', 'ル'), + (0x32F9, 'M', 'レ'), + (0x32FA, 'M', 'ロ'), + (0x32FB, 'M', 'ワ'), + (0x32FC, 'M', 'ヰ'), + (0x32FD, 'M', 'ヱ'), + (0x32FE, 'M', 'ヲ'), + (0x32FF, 'M', '令和'), + (0x3300, 'M', 'アパート'), + (0x3301, 'M', 'アルファ'), + (0x3302, 'M', 'アンペア'), + (0x3303, 'M', 'アール'), + (0x3304, 'M', 'イニング'), + (0x3305, 'M', 'インチ'), + (0x3306, 'M', 'ウォン'), + (0x3307, 'M', 'エスクード'), + (0x3308, 'M', 'エーカー'), + (0x3309, 'M', 'オンス'), + (0x330A, 'M', 'オーム'), + (0x330B, 'M', 'カイリ'), + (0x330C, 'M', 'カラット'), + (0x330D, 'M', 'カロリー'), + (0x330E, 'M', 'ガロン'), + (0x330F, 'M', 'ガンマ'), + (0x3310, 'M', 'ギガ'), + (0x3311, 'M', 'ギニー'), ] def _seg_33(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x330E, 'M', u'ガロン'), - (0x330F, 'M', u'ガンマ'), - (0x3310, 'M', u'ギガ'), - (0x3311, 'M', u'ギニー'), - (0x3312, 'M', u'キュリー'), - (0x3313, 'M', u'ギルダー'), - (0x3314, 'M', u'キロ'), - (0x3315, 'M', u'キログラム'), - (0x3316, 'M', u'キロメートル'), - (0x3317, 'M', u'キロワット'), - (0x3318, 'M', u'グラム'), - (0x3319, 'M', u'グラムトン'), - (0x331A, 'M', u'クルゼイロ'), - (0x331B, 'M', u'クローネ'), - (0x331C, 'M', u'ケース'), - (0x331D, 'M', u'コルナ'), - (0x331E, 'M', u'コーポ'), - (0x331F, 'M', u'サイクル'), - (0x3320, 'M', u'サンチーム'), - (0x3321, 'M', u'シリング'), - (0x3322, 'M', u'センチ'), - (0x3323, 'M', u'セント'), - (0x3324, 'M', u'ダース'), - (0x3325, 'M', u'デシ'), - (0x3326, 'M', u'ドル'), - (0x3327, 'M', u'トン'), - (0x3328, 'M', u'ナノ'), - (0x3329, 'M', u'ノット'), - (0x332A, 'M', u'ハイツ'), - (0x332B, 'M', u'パーセント'), - (0x332C, 'M', u'パーツ'), - (0x332D, 'M', u'バーレル'), - (0x332E, 'M', u'ピアストル'), - (0x332F, 'M', u'ピクル'), - (0x3330, 'M', u'ピコ'), - (0x3331, 'M', u'ビル'), - (0x3332, 'M', u'ファラッド'), - (0x3333, 'M', u'フィート'), - (0x3334, 'M', u'ブッシェル'), - (0x3335, 'M', u'フラン'), - (0x3336, 'M', u'ヘクタール'), - (0x3337, 'M', u'ペソ'), - (0x3338, 'M', u'ペニヒ'), - (0x3339, 'M', u'ヘルツ'), - (0x333A, 'M', u'ペンス'), - (0x333B, 'M', u'ページ'), - (0x333C, 'M', u'ベータ'), - (0x333D, 'M', u'ポイント'), - (0x333E, 'M', u'ボルト'), - (0x333F, 'M', u'ホン'), - (0x3340, 'M', u'ポンド'), - (0x3341, 'M', u'ホール'), - (0x3342, 'M', u'ホーン'), - (0x3343, 'M', u'マイクロ'), - (0x3344, 'M', u'マイル'), - (0x3345, 'M', u'マッハ'), - (0x3346, 'M', u'マルク'), - (0x3347, 'M', u'マンション'), - (0x3348, 'M', u'ミクロン'), - (0x3349, 'M', u'ミリ'), - (0x334A, 'M', u'ミリバール'), - (0x334B, 'M', u'メガ'), - (0x334C, 'M', u'メガトン'), - (0x334D, 'M', u'メートル'), - (0x334E, 'M', u'ヤード'), - (0x334F, 'M', u'ヤール'), - (0x3350, 'M', u'ユアン'), - (0x3351, 'M', u'リットル'), - (0x3352, 'M', u'リラ'), - (0x3353, 'M', u'ルピー'), - (0x3354, 'M', u'ルーブル'), - (0x3355, 'M', u'レム'), - (0x3356, 'M', u'レントゲン'), - (0x3357, 'M', u'ワット'), - (0x3358, 'M', u'0点'), - (0x3359, 'M', u'1点'), - (0x335A, 'M', u'2点'), - (0x335B, 'M', u'3点'), - (0x335C, 'M', u'4点'), - (0x335D, 'M', u'5点'), - (0x335E, 'M', u'6点'), - (0x335F, 'M', u'7点'), - (0x3360, 'M', u'8点'), - (0x3361, 'M', u'9点'), - (0x3362, 'M', u'10点'), - (0x3363, 'M', u'11点'), - (0x3364, 'M', u'12点'), - (0x3365, 'M', u'13点'), - (0x3366, 'M', u'14点'), - (0x3367, 'M', u'15点'), - (0x3368, 'M', u'16点'), - (0x3369, 'M', u'17点'), - (0x336A, 'M', u'18点'), - (0x336B, 'M', u'19点'), - (0x336C, 'M', u'20点'), - (0x336D, 'M', u'21点'), - (0x336E, 'M', u'22点'), - (0x336F, 'M', u'23点'), - (0x3370, 'M', u'24点'), - (0x3371, 'M', u'hpa'), + (0x3312, 'M', 'キュリー'), + (0x3313, 'M', 'ギルダー'), + (0x3314, 'M', 'キロ'), + (0x3315, 'M', 'キログラム'), + (0x3316, 'M', 'キロメートル'), + (0x3317, 'M', 'キロワット'), + (0x3318, 'M', 'グラム'), + (0x3319, 'M', 'グラムトン'), + (0x331A, 'M', 'クルゼイロ'), + (0x331B, 'M', 'クローネ'), + (0x331C, 'M', 'ケース'), + (0x331D, 'M', 'コルナ'), + (0x331E, 'M', 'コーポ'), + (0x331F, 'M', 'サイクル'), + (0x3320, 'M', 'サンチーム'), + (0x3321, 'M', 'シリング'), + (0x3322, 'M', 'センチ'), + (0x3323, 'M', 'セント'), + (0x3324, 'M', 'ダース'), + (0x3325, 'M', 'デシ'), + (0x3326, 'M', 'ドル'), + (0x3327, 'M', 'トン'), + (0x3328, 'M', 'ナノ'), + (0x3329, 'M', 'ノット'), + (0x332A, 'M', 'ハイツ'), + (0x332B, 'M', 'パーセント'), + (0x332C, 'M', 'パーツ'), + (0x332D, 'M', 'バーレル'), + (0x332E, 'M', 'ピアストル'), + (0x332F, 'M', 'ピクル'), + (0x3330, 'M', 'ピコ'), + (0x3331, 'M', 'ビル'), + (0x3332, 'M', 'ファラッド'), + (0x3333, 'M', 'フィート'), + (0x3334, 'M', 'ブッシェル'), + (0x3335, 'M', 'フラン'), + (0x3336, 'M', 'ヘクタール'), + (0x3337, 'M', 'ペソ'), + (0x3338, 'M', 'ペニヒ'), + (0x3339, 'M', 'ヘルツ'), + (0x333A, 'M', 'ペンス'), + (0x333B, 'M', 'ページ'), + (0x333C, 'M', 'ベータ'), + (0x333D, 'M', 'ポイント'), + (0x333E, 'M', 'ボルト'), + (0x333F, 'M', 'ホン'), + (0x3340, 'M', 'ポンド'), + (0x3341, 'M', 'ホール'), + (0x3342, 'M', 'ホーン'), + (0x3343, 'M', 'マイクロ'), + (0x3344, 'M', 'マイル'), + (0x3345, 'M', 'マッハ'), + (0x3346, 'M', 'マルク'), + (0x3347, 'M', 'マンション'), + (0x3348, 'M', 'ミクロン'), + (0x3349, 'M', 'ミリ'), + (0x334A, 'M', 'ミリバール'), + (0x334B, 'M', 'メガ'), + (0x334C, 'M', 'メガトン'), + (0x334D, 'M', 'メートル'), + (0x334E, 'M', 'ヤード'), + (0x334F, 'M', 'ヤール'), + (0x3350, 'M', 'ユアン'), + (0x3351, 'M', 'リットル'), + (0x3352, 'M', 'リラ'), + (0x3353, 'M', 'ルピー'), + (0x3354, 'M', 'ルーブル'), + (0x3355, 'M', 'レム'), + (0x3356, 'M', 'レントゲン'), + (0x3357, 'M', 'ワット'), + (0x3358, 'M', '0点'), + (0x3359, 'M', '1点'), + (0x335A, 'M', '2点'), + (0x335B, 'M', '3点'), + (0x335C, 'M', '4点'), + (0x335D, 'M', '5点'), + (0x335E, 'M', '6点'), + (0x335F, 'M', '7点'), + (0x3360, 'M', '8点'), + (0x3361, 'M', '9点'), + (0x3362, 'M', '10点'), + (0x3363, 'M', '11点'), + (0x3364, 'M', '12点'), + (0x3365, 'M', '13点'), + (0x3366, 'M', '14点'), + (0x3367, 'M', '15点'), + (0x3368, 'M', '16点'), + (0x3369, 'M', '17点'), + (0x336A, 'M', '18点'), + (0x336B, 'M', '19点'), + (0x336C, 'M', '20点'), + (0x336D, 'M', '21点'), + (0x336E, 'M', '22点'), + (0x336F, 'M', '23点'), + (0x3370, 'M', '24点'), + (0x3371, 'M', 'hpa'), + (0x3372, 'M', 'da'), + (0x3373, 'M', 'au'), + (0x3374, 'M', 'bar'), + (0x3375, 'M', 'ov'), ] def _seg_34(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x3372, 'M', u'da'), - (0x3373, 'M', u'au'), - (0x3374, 'M', u'bar'), - (0x3375, 'M', u'ov'), - (0x3376, 'M', u'pc'), - (0x3377, 'M', u'dm'), - (0x3378, 'M', u'dm2'), - (0x3379, 'M', u'dm3'), - (0x337A, 'M', u'iu'), - (0x337B, 'M', u'平成'), - (0x337C, 'M', u'昭和'), - (0x337D, 'M', u'大正'), - (0x337E, 'M', u'明治'), - (0x337F, 'M', u'株式会社'), - (0x3380, 'M', u'pa'), - (0x3381, 'M', u'na'), - (0x3382, 'M', u'μa'), - (0x3383, 'M', u'ma'), - (0x3384, 'M', u'ka'), - (0x3385, 'M', u'kb'), - (0x3386, 'M', u'mb'), - (0x3387, 'M', u'gb'), - (0x3388, 'M', u'cal'), - (0x3389, 'M', u'kcal'), - (0x338A, 'M', u'pf'), - (0x338B, 'M', u'nf'), - (0x338C, 'M', u'μf'), - (0x338D, 'M', u'μg'), - (0x338E, 'M', u'mg'), - (0x338F, 'M', u'kg'), - (0x3390, 'M', u'hz'), - (0x3391, 'M', u'khz'), - (0x3392, 'M', u'mhz'), - (0x3393, 'M', u'ghz'), - (0x3394, 'M', u'thz'), - (0x3395, 'M', u'μl'), - (0x3396, 'M', u'ml'), - (0x3397, 'M', u'dl'), - (0x3398, 'M', u'kl'), - (0x3399, 'M', u'fm'), - (0x339A, 'M', u'nm'), - (0x339B, 'M', u'μm'), - (0x339C, 'M', u'mm'), - (0x339D, 'M', u'cm'), - (0x339E, 'M', u'km'), - (0x339F, 'M', u'mm2'), - (0x33A0, 'M', u'cm2'), - (0x33A1, 'M', u'm2'), - (0x33A2, 'M', u'km2'), - (0x33A3, 'M', u'mm3'), - (0x33A4, 'M', u'cm3'), - (0x33A5, 'M', u'm3'), - (0x33A6, 'M', u'km3'), - (0x33A7, 'M', u'm∕s'), - (0x33A8, 'M', u'm∕s2'), - (0x33A9, 'M', u'pa'), - (0x33AA, 'M', u'kpa'), - (0x33AB, 'M', u'mpa'), - (0x33AC, 'M', u'gpa'), - (0x33AD, 'M', u'rad'), - (0x33AE, 'M', u'rad∕s'), - (0x33AF, 'M', u'rad∕s2'), - (0x33B0, 'M', u'ps'), - (0x33B1, 'M', u'ns'), - (0x33B2, 'M', u'μs'), - (0x33B3, 'M', u'ms'), - (0x33B4, 'M', u'pv'), - (0x33B5, 'M', u'nv'), - (0x33B6, 'M', u'μv'), - (0x33B7, 'M', u'mv'), - (0x33B8, 'M', u'kv'), - (0x33B9, 'M', u'mv'), - (0x33BA, 'M', u'pw'), - (0x33BB, 'M', u'nw'), - (0x33BC, 'M', u'μw'), - (0x33BD, 'M', u'mw'), - (0x33BE, 'M', u'kw'), - (0x33BF, 'M', u'mw'), - (0x33C0, 'M', u'kω'), - (0x33C1, 'M', u'mω'), + (0x3376, 'M', 'pc'), + (0x3377, 'M', 'dm'), + (0x3378, 'M', 'dm2'), + (0x3379, 'M', 'dm3'), + (0x337A, 'M', 'iu'), + (0x337B, 'M', '平成'), + (0x337C, 'M', '昭和'), + (0x337D, 'M', '大正'), + (0x337E, 'M', '明治'), + (0x337F, 'M', '株式会社'), + (0x3380, 'M', 'pa'), + (0x3381, 'M', 'na'), + (0x3382, 'M', 'μa'), + (0x3383, 'M', 'ma'), + (0x3384, 'M', 'ka'), + (0x3385, 'M', 'kb'), + (0x3386, 'M', 'mb'), + (0x3387, 'M', 'gb'), + (0x3388, 'M', 'cal'), + (0x3389, 'M', 'kcal'), + (0x338A, 'M', 'pf'), + (0x338B, 'M', 'nf'), + (0x338C, 'M', 'μf'), + (0x338D, 'M', 'μg'), + (0x338E, 'M', 'mg'), + (0x338F, 'M', 'kg'), + (0x3390, 'M', 'hz'), + (0x3391, 'M', 'khz'), + (0x3392, 'M', 'mhz'), + (0x3393, 'M', 'ghz'), + (0x3394, 'M', 'thz'), + (0x3395, 'M', 'μl'), + (0x3396, 'M', 'ml'), + (0x3397, 'M', 'dl'), + (0x3398, 'M', 'kl'), + (0x3399, 'M', 'fm'), + (0x339A, 'M', 'nm'), + (0x339B, 'M', 'μm'), + (0x339C, 'M', 'mm'), + (0x339D, 'M', 'cm'), + (0x339E, 'M', 'km'), + (0x339F, 'M', 'mm2'), + (0x33A0, 'M', 'cm2'), + (0x33A1, 'M', 'm2'), + (0x33A2, 'M', 'km2'), + (0x33A3, 'M', 'mm3'), + (0x33A4, 'M', 'cm3'), + (0x33A5, 'M', 'm3'), + (0x33A6, 'M', 'km3'), + (0x33A7, 'M', 'm∕s'), + (0x33A8, 'M', 'm∕s2'), + (0x33A9, 'M', 'pa'), + (0x33AA, 'M', 'kpa'), + (0x33AB, 'M', 'mpa'), + (0x33AC, 'M', 'gpa'), + (0x33AD, 'M', 'rad'), + (0x33AE, 'M', 'rad∕s'), + (0x33AF, 'M', 'rad∕s2'), + (0x33B0, 'M', 'ps'), + (0x33B1, 'M', 'ns'), + (0x33B2, 'M', 'μs'), + (0x33B3, 'M', 'ms'), + (0x33B4, 'M', 'pv'), + (0x33B5, 'M', 'nv'), + (0x33B6, 'M', 'μv'), + (0x33B7, 'M', 'mv'), + (0x33B8, 'M', 'kv'), + (0x33B9, 'M', 'mv'), + (0x33BA, 'M', 'pw'), + (0x33BB, 'M', 'nw'), + (0x33BC, 'M', 'μw'), + (0x33BD, 'M', 'mw'), + (0x33BE, 'M', 'kw'), + (0x33BF, 'M', 'mw'), + (0x33C0, 'M', 'kω'), + (0x33C1, 'M', 'mω'), (0x33C2, 'X'), - (0x33C3, 'M', u'bq'), - (0x33C4, 'M', u'cc'), - (0x33C5, 'M', u'cd'), - (0x33C6, 'M', u'c∕kg'), + (0x33C3, 'M', 'bq'), + (0x33C4, 'M', 'cc'), + (0x33C5, 'M', 'cd'), + (0x33C6, 'M', 'c∕kg'), (0x33C7, 'X'), - (0x33C8, 'M', u'db'), - (0x33C9, 'M', u'gy'), - (0x33CA, 'M', u'ha'), - (0x33CB, 'M', u'hp'), - (0x33CC, 'M', u'in'), - (0x33CD, 'M', u'kk'), - (0x33CE, 'M', u'km'), - (0x33CF, 'M', u'kt'), - (0x33D0, 'M', u'lm'), - (0x33D1, 'M', u'ln'), - (0x33D2, 'M', u'log'), - (0x33D3, 'M', u'lx'), - (0x33D4, 'M', u'mb'), - (0x33D5, 'M', u'mil'), + (0x33C8, 'M', 'db'), + (0x33C9, 'M', 'gy'), + (0x33CA, 'M', 'ha'), + (0x33CB, 'M', 'hp'), + (0x33CC, 'M', 'in'), + (0x33CD, 'M', 'kk'), + (0x33CE, 'M', 'km'), + (0x33CF, 'M', 'kt'), + (0x33D0, 'M', 'lm'), + (0x33D1, 'M', 'ln'), + (0x33D2, 'M', 'log'), + (0x33D3, 'M', 'lx'), + (0x33D4, 'M', 'mb'), + (0x33D5, 'M', 'mil'), + (0x33D6, 'M', 'mol'), + (0x33D7, 'M', 'ph'), + (0x33D8, 'X'), + (0x33D9, 'M', 'ppm'), ] def _seg_35(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x33D6, 'M', u'mol'), - (0x33D7, 'M', u'ph'), - (0x33D8, 'X'), - (0x33D9, 'M', u'ppm'), - (0x33DA, 'M', u'pr'), - (0x33DB, 'M', u'sr'), - (0x33DC, 'M', u'sv'), - (0x33DD, 'M', u'wb'), - (0x33DE, 'M', u'v∕m'), - (0x33DF, 'M', u'a∕m'), - (0x33E0, 'M', u'1日'), - (0x33E1, 'M', u'2日'), - (0x33E2, 'M', u'3日'), - (0x33E3, 'M', u'4日'), - (0x33E4, 'M', u'5日'), - (0x33E5, 'M', u'6日'), - (0x33E6, 'M', u'7日'), - (0x33E7, 'M', u'8日'), - (0x33E8, 'M', u'9日'), - (0x33E9, 'M', u'10日'), - (0x33EA, 'M', u'11日'), - (0x33EB, 'M', u'12日'), - (0x33EC, 'M', u'13日'), - (0x33ED, 'M', u'14日'), - (0x33EE, 'M', u'15日'), - (0x33EF, 'M', u'16日'), - (0x33F0, 'M', u'17日'), - (0x33F1, 'M', u'18日'), - (0x33F2, 'M', u'19日'), - (0x33F3, 'M', u'20日'), - (0x33F4, 'M', u'21日'), - (0x33F5, 'M', u'22日'), - (0x33F6, 'M', u'23日'), - (0x33F7, 'M', u'24日'), - (0x33F8, 'M', u'25日'), - (0x33F9, 'M', u'26日'), - (0x33FA, 'M', u'27日'), - (0x33FB, 'M', u'28日'), - (0x33FC, 'M', u'29日'), - (0x33FD, 'M', u'30日'), - (0x33FE, 'M', u'31日'), - (0x33FF, 'M', u'gal'), + (0x33DA, 'M', 'pr'), + (0x33DB, 'M', 'sr'), + (0x33DC, 'M', 'sv'), + (0x33DD, 'M', 'wb'), + (0x33DE, 'M', 'v∕m'), + (0x33DF, 'M', 'a∕m'), + (0x33E0, 'M', '1日'), + (0x33E1, 'M', '2日'), + (0x33E2, 'M', '3日'), + (0x33E3, 'M', '4日'), + (0x33E4, 'M', '5日'), + (0x33E5, 'M', '6日'), + (0x33E6, 'M', '7日'), + (0x33E7, 'M', '8日'), + (0x33E8, 'M', '9日'), + (0x33E9, 'M', '10日'), + (0x33EA, 'M', '11日'), + (0x33EB, 'M', '12日'), + (0x33EC, 'M', '13日'), + (0x33ED, 'M', '14日'), + (0x33EE, 'M', '15日'), + (0x33EF, 'M', '16日'), + (0x33F0, 'M', '17日'), + (0x33F1, 'M', '18日'), + (0x33F2, 'M', '19日'), + (0x33F3, 'M', '20日'), + (0x33F4, 'M', '21日'), + (0x33F5, 'M', '22日'), + (0x33F6, 'M', '23日'), + (0x33F7, 'M', '24日'), + (0x33F8, 'M', '25日'), + (0x33F9, 'M', '26日'), + (0x33FA, 'M', '27日'), + (0x33FB, 'M', '28日'), + (0x33FC, 'M', '29日'), + (0x33FD, 'M', '30日'), + (0x33FE, 'M', '31日'), + (0x33FF, 'M', 'gal'), (0x3400, 'V'), - (0x4DB6, 'X'), - (0x4DC0, 'V'), - (0x9FF0, 'X'), + (0x9FFD, 'X'), (0xA000, 'V'), (0xA48D, 'X'), (0xA490, 'V'), (0xA4C7, 'X'), (0xA4D0, 'V'), (0xA62C, 'X'), - (0xA640, 'M', u'ꙁ'), + (0xA640, 'M', 'ꙁ'), (0xA641, 'V'), - (0xA642, 'M', u'ꙃ'), + (0xA642, 'M', 'ꙃ'), (0xA643, 'V'), - (0xA644, 'M', u'ꙅ'), + (0xA644, 'M', 'ꙅ'), (0xA645, 'V'), - (0xA646, 'M', u'ꙇ'), + (0xA646, 'M', 'ꙇ'), (0xA647, 'V'), - (0xA648, 'M', u'ꙉ'), + (0xA648, 'M', 'ꙉ'), (0xA649, 'V'), - (0xA64A, 'M', u'ꙋ'), + (0xA64A, 'M', 'ꙋ'), (0xA64B, 'V'), - (0xA64C, 'M', u'ꙍ'), + (0xA64C, 'M', 'ꙍ'), (0xA64D, 'V'), - (0xA64E, 'M', u'ꙏ'), + (0xA64E, 'M', 'ꙏ'), (0xA64F, 'V'), - (0xA650, 'M', u'ꙑ'), + (0xA650, 'M', 'ꙑ'), (0xA651, 'V'), - (0xA652, 'M', u'ꙓ'), + (0xA652, 'M', 'ꙓ'), (0xA653, 'V'), - (0xA654, 'M', u'ꙕ'), + (0xA654, 'M', 'ꙕ'), (0xA655, 'V'), - (0xA656, 'M', u'ꙗ'), + (0xA656, 'M', 'ꙗ'), (0xA657, 'V'), - (0xA658, 'M', u'ꙙ'), + (0xA658, 'M', 'ꙙ'), (0xA659, 'V'), - (0xA65A, 'M', u'ꙛ'), + (0xA65A, 'M', 'ꙛ'), (0xA65B, 'V'), - (0xA65C, 'M', u'ꙝ'), + (0xA65C, 'M', 'ꙝ'), (0xA65D, 'V'), - (0xA65E, 'M', u'ꙟ'), + (0xA65E, 'M', 'ꙟ'), (0xA65F, 'V'), - (0xA660, 'M', u'ꙡ'), + (0xA660, 'M', 'ꙡ'), (0xA661, 'V'), - (0xA662, 'M', u'ꙣ'), + (0xA662, 'M', 'ꙣ'), (0xA663, 'V'), - (0xA664, 'M', u'ꙥ'), + (0xA664, 'M', 'ꙥ'), (0xA665, 'V'), - (0xA666, 'M', u'ꙧ'), + (0xA666, 'M', 'ꙧ'), (0xA667, 'V'), - (0xA668, 'M', u'ꙩ'), + (0xA668, 'M', 'ꙩ'), (0xA669, 'V'), - (0xA66A, 'M', u'ꙫ'), + (0xA66A, 'M', 'ꙫ'), (0xA66B, 'V'), - (0xA66C, 'M', u'ꙭ'), + (0xA66C, 'M', 'ꙭ'), (0xA66D, 'V'), - (0xA680, 'M', u'ꚁ'), + (0xA680, 'M', 'ꚁ'), (0xA681, 'V'), + (0xA682, 'M', 'ꚃ'), + (0xA683, 'V'), + (0xA684, 'M', 'ꚅ'), + (0xA685, 'V'), + (0xA686, 'M', 'ꚇ'), + (0xA687, 'V'), ] def _seg_36(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xA682, 'M', u'ꚃ'), - (0xA683, 'V'), - (0xA684, 'M', u'ꚅ'), - (0xA685, 'V'), - (0xA686, 'M', u'ꚇ'), - (0xA687, 'V'), - (0xA688, 'M', u'ꚉ'), + (0xA688, 'M', 'ꚉ'), (0xA689, 'V'), - (0xA68A, 'M', u'ꚋ'), + (0xA68A, 'M', 'ꚋ'), (0xA68B, 'V'), - (0xA68C, 'M', u'ꚍ'), + (0xA68C, 'M', 'ꚍ'), (0xA68D, 'V'), - (0xA68E, 'M', u'ꚏ'), + (0xA68E, 'M', 'ꚏ'), (0xA68F, 'V'), - (0xA690, 'M', u'ꚑ'), + (0xA690, 'M', 'ꚑ'), (0xA691, 'V'), - (0xA692, 'M', u'ꚓ'), + (0xA692, 'M', 'ꚓ'), (0xA693, 'V'), - (0xA694, 'M', u'ꚕ'), + (0xA694, 'M', 'ꚕ'), (0xA695, 'V'), - (0xA696, 'M', u'ꚗ'), + (0xA696, 'M', 'ꚗ'), (0xA697, 'V'), - (0xA698, 'M', u'ꚙ'), + (0xA698, 'M', 'ꚙ'), (0xA699, 'V'), - (0xA69A, 'M', u'ꚛ'), + (0xA69A, 'M', 'ꚛ'), (0xA69B, 'V'), - (0xA69C, 'M', u'ъ'), - (0xA69D, 'M', u'ь'), + (0xA69C, 'M', 'ъ'), + (0xA69D, 'M', 'ь'), (0xA69E, 'V'), (0xA6F8, 'X'), (0xA700, 'V'), - (0xA722, 'M', u'ꜣ'), + (0xA722, 'M', 'ꜣ'), (0xA723, 'V'), - (0xA724, 'M', u'ꜥ'), + (0xA724, 'M', 'ꜥ'), (0xA725, 'V'), - (0xA726, 'M', u'ꜧ'), + (0xA726, 'M', 'ꜧ'), (0xA727, 'V'), - (0xA728, 'M', u'ꜩ'), + (0xA728, 'M', 'ꜩ'), (0xA729, 'V'), - (0xA72A, 'M', u'ꜫ'), + (0xA72A, 'M', 'ꜫ'), (0xA72B, 'V'), - (0xA72C, 'M', u'ꜭ'), + (0xA72C, 'M', 'ꜭ'), (0xA72D, 'V'), - (0xA72E, 'M', u'ꜯ'), + (0xA72E, 'M', 'ꜯ'), (0xA72F, 'V'), - (0xA732, 'M', u'ꜳ'), + (0xA732, 'M', 'ꜳ'), (0xA733, 'V'), - (0xA734, 'M', u'ꜵ'), + (0xA734, 'M', 'ꜵ'), (0xA735, 'V'), - (0xA736, 'M', u'ꜷ'), + (0xA736, 'M', 'ꜷ'), (0xA737, 'V'), - (0xA738, 'M', u'ꜹ'), + (0xA738, 'M', 'ꜹ'), (0xA739, 'V'), - (0xA73A, 'M', u'ꜻ'), + (0xA73A, 'M', 'ꜻ'), (0xA73B, 'V'), - (0xA73C, 'M', u'ꜽ'), + (0xA73C, 'M', 'ꜽ'), (0xA73D, 'V'), - (0xA73E, 'M', u'ꜿ'), + (0xA73E, 'M', 'ꜿ'), (0xA73F, 'V'), - (0xA740, 'M', u'ꝁ'), + (0xA740, 'M', 'ꝁ'), (0xA741, 'V'), - (0xA742, 'M', u'ꝃ'), + (0xA742, 'M', 'ꝃ'), (0xA743, 'V'), - (0xA744, 'M', u'ꝅ'), + (0xA744, 'M', 'ꝅ'), (0xA745, 'V'), - (0xA746, 'M', u'ꝇ'), + (0xA746, 'M', 'ꝇ'), (0xA747, 'V'), - (0xA748, 'M', u'ꝉ'), + (0xA748, 'M', 'ꝉ'), (0xA749, 'V'), - (0xA74A, 'M', u'ꝋ'), + (0xA74A, 'M', 'ꝋ'), (0xA74B, 'V'), - (0xA74C, 'M', u'ꝍ'), + (0xA74C, 'M', 'ꝍ'), (0xA74D, 'V'), - (0xA74E, 'M', u'ꝏ'), + (0xA74E, 'M', 'ꝏ'), (0xA74F, 'V'), - (0xA750, 'M', u'ꝑ'), + (0xA750, 'M', 'ꝑ'), (0xA751, 'V'), - (0xA752, 'M', u'ꝓ'), + (0xA752, 'M', 'ꝓ'), (0xA753, 'V'), - (0xA754, 'M', u'ꝕ'), + (0xA754, 'M', 'ꝕ'), (0xA755, 'V'), - (0xA756, 'M', u'ꝗ'), + (0xA756, 'M', 'ꝗ'), (0xA757, 'V'), - (0xA758, 'M', u'ꝙ'), + (0xA758, 'M', 'ꝙ'), (0xA759, 'V'), - (0xA75A, 'M', u'ꝛ'), + (0xA75A, 'M', 'ꝛ'), (0xA75B, 'V'), - (0xA75C, 'M', u'ꝝ'), + (0xA75C, 'M', 'ꝝ'), (0xA75D, 'V'), - (0xA75E, 'M', u'ꝟ'), + (0xA75E, 'M', 'ꝟ'), (0xA75F, 'V'), - (0xA760, 'M', u'ꝡ'), + (0xA760, 'M', 'ꝡ'), (0xA761, 'V'), - (0xA762, 'M', u'ꝣ'), + (0xA762, 'M', 'ꝣ'), (0xA763, 'V'), - (0xA764, 'M', u'ꝥ'), + (0xA764, 'M', 'ꝥ'), (0xA765, 'V'), - (0xA766, 'M', u'ꝧ'), + (0xA766, 'M', 'ꝧ'), (0xA767, 'V'), - (0xA768, 'M', u'ꝩ'), + (0xA768, 'M', 'ꝩ'), + (0xA769, 'V'), + (0xA76A, 'M', 'ꝫ'), + (0xA76B, 'V'), + (0xA76C, 'M', 'ꝭ'), + (0xA76D, 'V'), + (0xA76E, 'M', 'ꝯ'), ] def _seg_37(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xA769, 'V'), - (0xA76A, 'M', u'ꝫ'), - (0xA76B, 'V'), - (0xA76C, 'M', u'ꝭ'), - (0xA76D, 'V'), - (0xA76E, 'M', u'ꝯ'), (0xA76F, 'V'), - (0xA770, 'M', u'ꝯ'), + (0xA770, 'M', 'ꝯ'), (0xA771, 'V'), - (0xA779, 'M', u'ꝺ'), + (0xA779, 'M', 'ꝺ'), (0xA77A, 'V'), - (0xA77B, 'M', u'ꝼ'), + (0xA77B, 'M', 'ꝼ'), (0xA77C, 'V'), - (0xA77D, 'M', u'ᵹ'), - (0xA77E, 'M', u'ꝿ'), + (0xA77D, 'M', 'ᵹ'), + (0xA77E, 'M', 'ꝿ'), (0xA77F, 'V'), - (0xA780, 'M', u'ꞁ'), + (0xA780, 'M', 'ꞁ'), (0xA781, 'V'), - (0xA782, 'M', u'ꞃ'), + (0xA782, 'M', 'ꞃ'), (0xA783, 'V'), - (0xA784, 'M', u'ꞅ'), + (0xA784, 'M', 'ꞅ'), (0xA785, 'V'), - (0xA786, 'M', u'ꞇ'), + (0xA786, 'M', 'ꞇ'), (0xA787, 'V'), - (0xA78B, 'M', u'ꞌ'), + (0xA78B, 'M', 'ꞌ'), (0xA78C, 'V'), - (0xA78D, 'M', u'ɥ'), + (0xA78D, 'M', 'ɥ'), (0xA78E, 'V'), - (0xA790, 'M', u'ꞑ'), + (0xA790, 'M', 'ꞑ'), (0xA791, 'V'), - (0xA792, 'M', u'ꞓ'), + (0xA792, 'M', 'ꞓ'), (0xA793, 'V'), - (0xA796, 'M', u'ꞗ'), + (0xA796, 'M', 'ꞗ'), (0xA797, 'V'), - (0xA798, 'M', u'ꞙ'), + (0xA798, 'M', 'ꞙ'), (0xA799, 'V'), - (0xA79A, 'M', u'ꞛ'), + (0xA79A, 'M', 'ꞛ'), (0xA79B, 'V'), - (0xA79C, 'M', u'ꞝ'), + (0xA79C, 'M', 'ꞝ'), (0xA79D, 'V'), - (0xA79E, 'M', u'ꞟ'), + (0xA79E, 'M', 'ꞟ'), (0xA79F, 'V'), - (0xA7A0, 'M', u'ꞡ'), + (0xA7A0, 'M', 'ꞡ'), (0xA7A1, 'V'), - (0xA7A2, 'M', u'ꞣ'), + (0xA7A2, 'M', 'ꞣ'), (0xA7A3, 'V'), - (0xA7A4, 'M', u'ꞥ'), + (0xA7A4, 'M', 'ꞥ'), (0xA7A5, 'V'), - (0xA7A6, 'M', u'ꞧ'), + (0xA7A6, 'M', 'ꞧ'), (0xA7A7, 'V'), - (0xA7A8, 'M', u'ꞩ'), + (0xA7A8, 'M', 'ꞩ'), (0xA7A9, 'V'), - (0xA7AA, 'M', u'ɦ'), - (0xA7AB, 'M', u'ɜ'), - (0xA7AC, 'M', u'ɡ'), - (0xA7AD, 'M', u'ɬ'), - (0xA7AE, 'M', u'ɪ'), + (0xA7AA, 'M', 'ɦ'), + (0xA7AB, 'M', 'ɜ'), + (0xA7AC, 'M', 'ɡ'), + (0xA7AD, 'M', 'ɬ'), + (0xA7AE, 'M', 'ɪ'), (0xA7AF, 'V'), - (0xA7B0, 'M', u'ʞ'), - (0xA7B1, 'M', u'ʇ'), - (0xA7B2, 'M', u'ʝ'), - (0xA7B3, 'M', u'ꭓ'), - (0xA7B4, 'M', u'ꞵ'), + (0xA7B0, 'M', 'ʞ'), + (0xA7B1, 'M', 'ʇ'), + (0xA7B2, 'M', 'ʝ'), + (0xA7B3, 'M', 'ꭓ'), + (0xA7B4, 'M', 'ꞵ'), (0xA7B5, 'V'), - (0xA7B6, 'M', u'ꞷ'), + (0xA7B6, 'M', 'ꞷ'), (0xA7B7, 'V'), - (0xA7B8, 'M', u'ꞹ'), + (0xA7B8, 'M', 'ꞹ'), (0xA7B9, 'V'), - (0xA7BA, 'M', u'ꞻ'), + (0xA7BA, 'M', 'ꞻ'), (0xA7BB, 'V'), - (0xA7BC, 'M', u'ꞽ'), + (0xA7BC, 'M', 'ꞽ'), (0xA7BD, 'V'), - (0xA7BE, 'M', u'ꞿ'), + (0xA7BE, 'M', 'ꞿ'), (0xA7BF, 'V'), (0xA7C0, 'X'), - (0xA7C2, 'M', u'ꟃ'), + (0xA7C2, 'M', 'ꟃ'), (0xA7C3, 'V'), - (0xA7C4, 'M', u'ꞔ'), - (0xA7C5, 'M', u'ʂ'), - (0xA7C6, 'M', u'ᶎ'), - (0xA7C7, 'X'), - (0xA7F7, 'V'), - (0xA7F8, 'M', u'ħ'), - (0xA7F9, 'M', u'œ'), + (0xA7C4, 'M', 'ꞔ'), + (0xA7C5, 'M', 'ʂ'), + (0xA7C6, 'M', 'ᶎ'), + (0xA7C7, 'M', 'ꟈ'), + (0xA7C8, 'V'), + (0xA7C9, 'M', 'ꟊ'), + (0xA7CA, 'V'), + (0xA7CB, 'X'), + (0xA7F5, 'M', 'ꟶ'), + (0xA7F6, 'V'), + (0xA7F8, 'M', 'ħ'), + (0xA7F9, 'M', 'œ'), (0xA7FA, 'V'), - (0xA82C, 'X'), + (0xA82D, 'X'), (0xA830, 'V'), (0xA83A, 'X'), (0xA840, 'V'), @@ -3955,11 +3993,12 @@ def _seg_37(): (0xA97D, 'X'), (0xA980, 'V'), (0xA9CE, 'X'), + (0xA9CF, 'V'), ] def _seg_38(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xA9CF, 'V'), (0xA9DA, 'X'), (0xA9DE, 'V'), (0xA9FF, 'X'), @@ -3984,96 +4023,99 @@ def _seg_38(): (0xAB28, 'V'), (0xAB2F, 'X'), (0xAB30, 'V'), - (0xAB5C, 'M', u'ꜧ'), - (0xAB5D, 'M', u'ꬷ'), - (0xAB5E, 'M', u'ɫ'), - (0xAB5F, 'M', u'ꭒ'), + (0xAB5C, 'M', 'ꜧ'), + (0xAB5D, 'M', 'ꬷ'), + (0xAB5E, 'M', 'ɫ'), + (0xAB5F, 'M', 'ꭒ'), (0xAB60, 'V'), - (0xAB68, 'X'), - (0xAB70, 'M', u'Ꭰ'), - (0xAB71, 'M', u'Ꭱ'), - (0xAB72, 'M', u'Ꭲ'), - (0xAB73, 'M', u'Ꭳ'), - (0xAB74, 'M', u'Ꭴ'), - (0xAB75, 'M', u'Ꭵ'), - (0xAB76, 'M', u'Ꭶ'), - (0xAB77, 'M', u'Ꭷ'), - (0xAB78, 'M', u'Ꭸ'), - (0xAB79, 'M', u'Ꭹ'), - (0xAB7A, 'M', u'Ꭺ'), - (0xAB7B, 'M', u'Ꭻ'), - (0xAB7C, 'M', u'Ꭼ'), - (0xAB7D, 'M', u'Ꭽ'), - (0xAB7E, 'M', u'Ꭾ'), - (0xAB7F, 'M', u'Ꭿ'), - (0xAB80, 'M', u'Ꮀ'), - (0xAB81, 'M', u'Ꮁ'), - (0xAB82, 'M', u'Ꮂ'), - (0xAB83, 'M', u'Ꮃ'), - (0xAB84, 'M', u'Ꮄ'), - (0xAB85, 'M', u'Ꮅ'), - (0xAB86, 'M', u'Ꮆ'), - (0xAB87, 'M', u'Ꮇ'), - (0xAB88, 'M', u'Ꮈ'), - (0xAB89, 'M', u'Ꮉ'), - (0xAB8A, 'M', u'Ꮊ'), - (0xAB8B, 'M', u'Ꮋ'), - (0xAB8C, 'M', u'Ꮌ'), - (0xAB8D, 'M', u'Ꮍ'), - (0xAB8E, 'M', u'Ꮎ'), - (0xAB8F, 'M', u'Ꮏ'), - (0xAB90, 'M', u'Ꮐ'), - (0xAB91, 'M', u'Ꮑ'), - (0xAB92, 'M', u'Ꮒ'), - (0xAB93, 'M', u'Ꮓ'), - (0xAB94, 'M', u'Ꮔ'), - (0xAB95, 'M', u'Ꮕ'), - (0xAB96, 'M', u'Ꮖ'), - (0xAB97, 'M', u'Ꮗ'), - (0xAB98, 'M', u'Ꮘ'), - (0xAB99, 'M', u'Ꮙ'), - (0xAB9A, 'M', u'Ꮚ'), - (0xAB9B, 'M', u'Ꮛ'), - (0xAB9C, 'M', u'Ꮜ'), - (0xAB9D, 'M', u'Ꮝ'), - (0xAB9E, 'M', u'Ꮞ'), - (0xAB9F, 'M', u'Ꮟ'), - (0xABA0, 'M', u'Ꮠ'), - (0xABA1, 'M', u'Ꮡ'), - (0xABA2, 'M', u'Ꮢ'), - (0xABA3, 'M', u'Ꮣ'), - (0xABA4, 'M', u'Ꮤ'), - (0xABA5, 'M', u'Ꮥ'), - (0xABA6, 'M', u'Ꮦ'), - (0xABA7, 'M', u'Ꮧ'), - (0xABA8, 'M', u'Ꮨ'), - (0xABA9, 'M', u'Ꮩ'), - (0xABAA, 'M', u'Ꮪ'), - (0xABAB, 'M', u'Ꮫ'), - (0xABAC, 'M', u'Ꮬ'), - (0xABAD, 'M', u'Ꮭ'), - (0xABAE, 'M', u'Ꮮ'), - (0xABAF, 'M', u'Ꮯ'), - (0xABB0, 'M', u'Ꮰ'), - (0xABB1, 'M', u'Ꮱ'), - (0xABB2, 'M', u'Ꮲ'), - (0xABB3, 'M', u'Ꮳ'), - (0xABB4, 'M', u'Ꮴ'), + (0xAB69, 'M', 'ʍ'), + (0xAB6A, 'V'), + (0xAB6C, 'X'), + (0xAB70, 'M', 'Ꭰ'), + (0xAB71, 'M', 'Ꭱ'), + (0xAB72, 'M', 'Ꭲ'), + (0xAB73, 'M', 'Ꭳ'), + (0xAB74, 'M', 'Ꭴ'), + (0xAB75, 'M', 'Ꭵ'), + (0xAB76, 'M', 'Ꭶ'), + (0xAB77, 'M', 'Ꭷ'), + (0xAB78, 'M', 'Ꭸ'), + (0xAB79, 'M', 'Ꭹ'), + (0xAB7A, 'M', 'Ꭺ'), + (0xAB7B, 'M', 'Ꭻ'), + (0xAB7C, 'M', 'Ꭼ'), + (0xAB7D, 'M', 'Ꭽ'), + (0xAB7E, 'M', 'Ꭾ'), + (0xAB7F, 'M', 'Ꭿ'), + (0xAB80, 'M', 'Ꮀ'), + (0xAB81, 'M', 'Ꮁ'), + (0xAB82, 'M', 'Ꮂ'), + (0xAB83, 'M', 'Ꮃ'), + (0xAB84, 'M', 'Ꮄ'), + (0xAB85, 'M', 'Ꮅ'), + (0xAB86, 'M', 'Ꮆ'), + (0xAB87, 'M', 'Ꮇ'), + (0xAB88, 'M', 'Ꮈ'), + (0xAB89, 'M', 'Ꮉ'), + (0xAB8A, 'M', 'Ꮊ'), + (0xAB8B, 'M', 'Ꮋ'), + (0xAB8C, 'M', 'Ꮌ'), + (0xAB8D, 'M', 'Ꮍ'), + (0xAB8E, 'M', 'Ꮎ'), + (0xAB8F, 'M', 'Ꮏ'), + (0xAB90, 'M', 'Ꮐ'), + (0xAB91, 'M', 'Ꮑ'), + (0xAB92, 'M', 'Ꮒ'), + (0xAB93, 'M', 'Ꮓ'), + (0xAB94, 'M', 'Ꮔ'), + (0xAB95, 'M', 'Ꮕ'), + (0xAB96, 'M', 'Ꮖ'), + (0xAB97, 'M', 'Ꮗ'), + (0xAB98, 'M', 'Ꮘ'), + (0xAB99, 'M', 'Ꮙ'), + (0xAB9A, 'M', 'Ꮚ'), + (0xAB9B, 'M', 'Ꮛ'), + (0xAB9C, 'M', 'Ꮜ'), + (0xAB9D, 'M', 'Ꮝ'), + (0xAB9E, 'M', 'Ꮞ'), + (0xAB9F, 'M', 'Ꮟ'), + (0xABA0, 'M', 'Ꮠ'), + (0xABA1, 'M', 'Ꮡ'), + (0xABA2, 'M', 'Ꮢ'), + (0xABA3, 'M', 'Ꮣ'), + (0xABA4, 'M', 'Ꮤ'), + (0xABA5, 'M', 'Ꮥ'), + (0xABA6, 'M', 'Ꮦ'), + (0xABA7, 'M', 'Ꮧ'), + (0xABA8, 'M', 'Ꮨ'), + (0xABA9, 'M', 'Ꮩ'), + (0xABAA, 'M', 'Ꮪ'), + (0xABAB, 'M', 'Ꮫ'), + (0xABAC, 'M', 'Ꮬ'), + (0xABAD, 'M', 'Ꮭ'), + (0xABAE, 'M', 'Ꮮ'), + (0xABAF, 'M', 'Ꮯ'), + (0xABB0, 'M', 'Ꮰ'), + (0xABB1, 'M', 'Ꮱ'), + (0xABB2, 'M', 'Ꮲ'), + (0xABB3, 'M', 'Ꮳ'), ] def _seg_39(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xABB5, 'M', u'Ꮵ'), - (0xABB6, 'M', u'Ꮶ'), - (0xABB7, 'M', u'Ꮷ'), - (0xABB8, 'M', u'Ꮸ'), - (0xABB9, 'M', u'Ꮹ'), - (0xABBA, 'M', u'Ꮺ'), - (0xABBB, 'M', u'Ꮻ'), - (0xABBC, 'M', u'Ꮼ'), - (0xABBD, 'M', u'Ꮽ'), - (0xABBE, 'M', u'Ꮾ'), - (0xABBF, 'M', u'Ꮿ'), + (0xABB4, 'M', 'Ꮴ'), + (0xABB5, 'M', 'Ꮵ'), + (0xABB6, 'M', 'Ꮶ'), + (0xABB7, 'M', 'Ꮷ'), + (0xABB8, 'M', 'Ꮸ'), + (0xABB9, 'M', 'Ꮹ'), + (0xABBA, 'M', 'Ꮺ'), + (0xABBB, 'M', 'Ꮻ'), + (0xABBC, 'M', 'Ꮼ'), + (0xABBD, 'M', 'Ꮽ'), + (0xABBE, 'M', 'Ꮾ'), + (0xABBF, 'M', 'Ꮿ'), (0xABC0, 'V'), (0xABEE, 'X'), (0xABF0, 'V'), @@ -4084,1441 +4126,1455 @@ def _seg_39(): (0xD7C7, 'X'), (0xD7CB, 'V'), (0xD7FC, 'X'), - (0xF900, 'M', u'豈'), - (0xF901, 'M', u'更'), - (0xF902, 'M', u'車'), - (0xF903, 'M', u'賈'), - (0xF904, 'M', u'滑'), - (0xF905, 'M', u'串'), - (0xF906, 'M', u'句'), - (0xF907, 'M', u'龜'), - (0xF909, 'M', u'契'), - (0xF90A, 'M', u'金'), - (0xF90B, 'M', u'喇'), - (0xF90C, 'M', u'奈'), - (0xF90D, 'M', u'懶'), - (0xF90E, 'M', u'癩'), - (0xF90F, 'M', u'羅'), - (0xF910, 'M', u'蘿'), - (0xF911, 'M', u'螺'), - (0xF912, 'M', u'裸'), - (0xF913, 'M', u'邏'), - (0xF914, 'M', u'樂'), - (0xF915, 'M', u'洛'), - (0xF916, 'M', u'烙'), - (0xF917, 'M', u'珞'), - (0xF918, 'M', u'落'), - (0xF919, 'M', u'酪'), - (0xF91A, 'M', u'駱'), - (0xF91B, 'M', u'亂'), - (0xF91C, 'M', u'卵'), - (0xF91D, 'M', u'欄'), - (0xF91E, 'M', u'爛'), - (0xF91F, 'M', u'蘭'), - (0xF920, 'M', u'鸞'), - (0xF921, 'M', u'嵐'), - (0xF922, 'M', u'濫'), - (0xF923, 'M', u'藍'), - (0xF924, 'M', u'襤'), - (0xF925, 'M', u'拉'), - (0xF926, 'M', u'臘'), - (0xF927, 'M', u'蠟'), - (0xF928, 'M', u'廊'), - (0xF929, 'M', u'朗'), - (0xF92A, 'M', u'浪'), - (0xF92B, 'M', u'狼'), - (0xF92C, 'M', u'郎'), - (0xF92D, 'M', u'來'), - (0xF92E, 'M', u'冷'), - (0xF92F, 'M', u'勞'), - (0xF930, 'M', u'擄'), - (0xF931, 'M', u'櫓'), - (0xF932, 'M', u'爐'), - (0xF933, 'M', u'盧'), - (0xF934, 'M', u'老'), - (0xF935, 'M', u'蘆'), - (0xF936, 'M', u'虜'), - (0xF937, 'M', u'路'), - (0xF938, 'M', u'露'), - (0xF939, 'M', u'魯'), - (0xF93A, 'M', u'鷺'), - (0xF93B, 'M', u'碌'), - (0xF93C, 'M', u'祿'), - (0xF93D, 'M', u'綠'), - (0xF93E, 'M', u'菉'), - (0xF93F, 'M', u'錄'), - (0xF940, 'M', u'鹿'), - (0xF941, 'M', u'論'), - (0xF942, 'M', u'壟'), - (0xF943, 'M', u'弄'), - (0xF944, 'M', u'籠'), - (0xF945, 'M', u'聾'), - (0xF946, 'M', u'牢'), - (0xF947, 'M', u'磊'), - (0xF948, 'M', u'賂'), - (0xF949, 'M', u'雷'), - (0xF94A, 'M', u'壘'), - (0xF94B, 'M', u'屢'), - (0xF94C, 'M', u'樓'), - (0xF94D, 'M', u'淚'), - (0xF94E, 'M', u'漏'), - (0xF94F, 'M', u'累'), + (0xF900, 'M', '豈'), + (0xF901, 'M', '更'), + (0xF902, 'M', '車'), + (0xF903, 'M', '賈'), + (0xF904, 'M', '滑'), + (0xF905, 'M', '串'), + (0xF906, 'M', '句'), + (0xF907, 'M', '龜'), + (0xF909, 'M', '契'), + (0xF90A, 'M', '金'), + (0xF90B, 'M', '喇'), + (0xF90C, 'M', '奈'), + (0xF90D, 'M', '懶'), + (0xF90E, 'M', '癩'), + (0xF90F, 'M', '羅'), + (0xF910, 'M', '蘿'), + (0xF911, 'M', '螺'), + (0xF912, 'M', '裸'), + (0xF913, 'M', '邏'), + (0xF914, 'M', '樂'), + (0xF915, 'M', '洛'), + (0xF916, 'M', '烙'), + (0xF917, 'M', '珞'), + (0xF918, 'M', '落'), + (0xF919, 'M', '酪'), + (0xF91A, 'M', '駱'), + (0xF91B, 'M', '亂'), + (0xF91C, 'M', '卵'), + (0xF91D, 'M', '欄'), + (0xF91E, 'M', '爛'), + (0xF91F, 'M', '蘭'), + (0xF920, 'M', '鸞'), + (0xF921, 'M', '嵐'), + (0xF922, 'M', '濫'), + (0xF923, 'M', '藍'), + (0xF924, 'M', '襤'), + (0xF925, 'M', '拉'), + (0xF926, 'M', '臘'), + (0xF927, 'M', '蠟'), + (0xF928, 'M', '廊'), + (0xF929, 'M', '朗'), + (0xF92A, 'M', '浪'), + (0xF92B, 'M', '狼'), + (0xF92C, 'M', '郎'), + (0xF92D, 'M', '來'), + (0xF92E, 'M', '冷'), + (0xF92F, 'M', '勞'), + (0xF930, 'M', '擄'), + (0xF931, 'M', '櫓'), + (0xF932, 'M', '爐'), + (0xF933, 'M', '盧'), + (0xF934, 'M', '老'), + (0xF935, 'M', '蘆'), + (0xF936, 'M', '虜'), + (0xF937, 'M', '路'), + (0xF938, 'M', '露'), + (0xF939, 'M', '魯'), + (0xF93A, 'M', '鷺'), + (0xF93B, 'M', '碌'), + (0xF93C, 'M', '祿'), + (0xF93D, 'M', '綠'), + (0xF93E, 'M', '菉'), + (0xF93F, 'M', '錄'), + (0xF940, 'M', '鹿'), + (0xF941, 'M', '論'), + (0xF942, 'M', '壟'), + (0xF943, 'M', '弄'), + (0xF944, 'M', '籠'), + (0xF945, 'M', '聾'), + (0xF946, 'M', '牢'), + (0xF947, 'M', '磊'), + (0xF948, 'M', '賂'), + (0xF949, 'M', '雷'), + (0xF94A, 'M', '壘'), + (0xF94B, 'M', '屢'), + (0xF94C, 'M', '樓'), + (0xF94D, 'M', '淚'), + (0xF94E, 'M', '漏'), ] def _seg_40(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xF950, 'M', u'縷'), - (0xF951, 'M', u'陋'), - (0xF952, 'M', u'勒'), - (0xF953, 'M', u'肋'), - (0xF954, 'M', u'凜'), - (0xF955, 'M', u'凌'), - (0xF956, 'M', u'稜'), - (0xF957, 'M', u'綾'), - (0xF958, 'M', u'菱'), - (0xF959, 'M', u'陵'), - (0xF95A, 'M', u'讀'), - (0xF95B, 'M', u'拏'), - (0xF95C, 'M', u'樂'), - (0xF95D, 'M', u'諾'), - (0xF95E, 'M', u'丹'), - (0xF95F, 'M', u'寧'), - (0xF960, 'M', u'怒'), - (0xF961, 'M', u'率'), - (0xF962, 'M', u'異'), - (0xF963, 'M', u'北'), - (0xF964, 'M', u'磻'), - (0xF965, 'M', u'便'), - (0xF966, 'M', u'復'), - (0xF967, 'M', u'不'), - (0xF968, 'M', u'泌'), - (0xF969, 'M', u'數'), - (0xF96A, 'M', u'索'), - (0xF96B, 'M', u'參'), - (0xF96C, 'M', u'塞'), - (0xF96D, 'M', u'省'), - (0xF96E, 'M', u'葉'), - (0xF96F, 'M', u'說'), - (0xF970, 'M', u'殺'), - (0xF971, 'M', u'辰'), - (0xF972, 'M', u'沈'), - (0xF973, 'M', u'拾'), - (0xF974, 'M', u'若'), - (0xF975, 'M', u'掠'), - (0xF976, 'M', u'略'), - (0xF977, 'M', u'亮'), - (0xF978, 'M', u'兩'), - (0xF979, 'M', u'凉'), - (0xF97A, 'M', u'梁'), - (0xF97B, 'M', u'糧'), - (0xF97C, 'M', u'良'), - (0xF97D, 'M', u'諒'), - (0xF97E, 'M', u'量'), - (0xF97F, 'M', u'勵'), - (0xF980, 'M', u'呂'), - (0xF981, 'M', u'女'), - (0xF982, 'M', u'廬'), - (0xF983, 'M', u'旅'), - (0xF984, 'M', u'濾'), - (0xF985, 'M', u'礪'), - (0xF986, 'M', u'閭'), - (0xF987, 'M', u'驪'), - (0xF988, 'M', u'麗'), - (0xF989, 'M', u'黎'), - (0xF98A, 'M', u'力'), - (0xF98B, 'M', u'曆'), - (0xF98C, 'M', u'歷'), - (0xF98D, 'M', u'轢'), - (0xF98E, 'M', u'年'), - (0xF98F, 'M', u'憐'), - (0xF990, 'M', u'戀'), - (0xF991, 'M', u'撚'), - (0xF992, 'M', u'漣'), - (0xF993, 'M', u'煉'), - (0xF994, 'M', u'璉'), - (0xF995, 'M', u'秊'), - (0xF996, 'M', u'練'), - (0xF997, 'M', u'聯'), - (0xF998, 'M', u'輦'), - (0xF999, 'M', u'蓮'), - (0xF99A, 'M', u'連'), - (0xF99B, 'M', u'鍊'), - (0xF99C, 'M', u'列'), - (0xF99D, 'M', u'劣'), - (0xF99E, 'M', u'咽'), - (0xF99F, 'M', u'烈'), - (0xF9A0, 'M', u'裂'), - (0xF9A1, 'M', u'說'), - (0xF9A2, 'M', u'廉'), - (0xF9A3, 'M', u'念'), - (0xF9A4, 'M', u'捻'), - (0xF9A5, 'M', u'殮'), - (0xF9A6, 'M', u'簾'), - (0xF9A7, 'M', u'獵'), - (0xF9A8, 'M', u'令'), - (0xF9A9, 'M', u'囹'), - (0xF9AA, 'M', u'寧'), - (0xF9AB, 'M', u'嶺'), - (0xF9AC, 'M', u'怜'), - (0xF9AD, 'M', u'玲'), - (0xF9AE, 'M', u'瑩'), - (0xF9AF, 'M', u'羚'), - (0xF9B0, 'M', u'聆'), - (0xF9B1, 'M', u'鈴'), - (0xF9B2, 'M', u'零'), - (0xF9B3, 'M', u'靈'), + (0xF94F, 'M', '累'), + (0xF950, 'M', '縷'), + (0xF951, 'M', '陋'), + (0xF952, 'M', '勒'), + (0xF953, 'M', '肋'), + (0xF954, 'M', '凜'), + (0xF955, 'M', '凌'), + (0xF956, 'M', '稜'), + (0xF957, 'M', '綾'), + (0xF958, 'M', '菱'), + (0xF959, 'M', '陵'), + (0xF95A, 'M', '讀'), + (0xF95B, 'M', '拏'), + (0xF95C, 'M', '樂'), + (0xF95D, 'M', '諾'), + (0xF95E, 'M', '丹'), + (0xF95F, 'M', '寧'), + (0xF960, 'M', '怒'), + (0xF961, 'M', '率'), + (0xF962, 'M', '異'), + (0xF963, 'M', '北'), + (0xF964, 'M', '磻'), + (0xF965, 'M', '便'), + (0xF966, 'M', '復'), + (0xF967, 'M', '不'), + (0xF968, 'M', '泌'), + (0xF969, 'M', '數'), + (0xF96A, 'M', '索'), + (0xF96B, 'M', '參'), + (0xF96C, 'M', '塞'), + (0xF96D, 'M', '省'), + (0xF96E, 'M', '葉'), + (0xF96F, 'M', '說'), + (0xF970, 'M', '殺'), + (0xF971, 'M', '辰'), + (0xF972, 'M', '沈'), + (0xF973, 'M', '拾'), + (0xF974, 'M', '若'), + (0xF975, 'M', '掠'), + (0xF976, 'M', '略'), + (0xF977, 'M', '亮'), + (0xF978, 'M', '兩'), + (0xF979, 'M', '凉'), + (0xF97A, 'M', '梁'), + (0xF97B, 'M', '糧'), + (0xF97C, 'M', '良'), + (0xF97D, 'M', '諒'), + (0xF97E, 'M', '量'), + (0xF97F, 'M', '勵'), + (0xF980, 'M', '呂'), + (0xF981, 'M', '女'), + (0xF982, 'M', '廬'), + (0xF983, 'M', '旅'), + (0xF984, 'M', '濾'), + (0xF985, 'M', '礪'), + (0xF986, 'M', '閭'), + (0xF987, 'M', '驪'), + (0xF988, 'M', '麗'), + (0xF989, 'M', '黎'), + (0xF98A, 'M', '力'), + (0xF98B, 'M', '曆'), + (0xF98C, 'M', '歷'), + (0xF98D, 'M', '轢'), + (0xF98E, 'M', '年'), + (0xF98F, 'M', '憐'), + (0xF990, 'M', '戀'), + (0xF991, 'M', '撚'), + (0xF992, 'M', '漣'), + (0xF993, 'M', '煉'), + (0xF994, 'M', '璉'), + (0xF995, 'M', '秊'), + (0xF996, 'M', '練'), + (0xF997, 'M', '聯'), + (0xF998, 'M', '輦'), + (0xF999, 'M', '蓮'), + (0xF99A, 'M', '連'), + (0xF99B, 'M', '鍊'), + (0xF99C, 'M', '列'), + (0xF99D, 'M', '劣'), + (0xF99E, 'M', '咽'), + (0xF99F, 'M', '烈'), + (0xF9A0, 'M', '裂'), + (0xF9A1, 'M', '說'), + (0xF9A2, 'M', '廉'), + (0xF9A3, 'M', '念'), + (0xF9A4, 'M', '捻'), + (0xF9A5, 'M', '殮'), + (0xF9A6, 'M', '簾'), + (0xF9A7, 'M', '獵'), + (0xF9A8, 'M', '令'), + (0xF9A9, 'M', '囹'), + (0xF9AA, 'M', '寧'), + (0xF9AB, 'M', '嶺'), + (0xF9AC, 'M', '怜'), + (0xF9AD, 'M', '玲'), + (0xF9AE, 'M', '瑩'), + (0xF9AF, 'M', '羚'), + (0xF9B0, 'M', '聆'), + (0xF9B1, 'M', '鈴'), + (0xF9B2, 'M', '零'), ] def _seg_41(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xF9B4, 'M', u'領'), - (0xF9B5, 'M', u'例'), - (0xF9B6, 'M', u'禮'), - (0xF9B7, 'M', u'醴'), - (0xF9B8, 'M', u'隸'), - (0xF9B9, 'M', u'惡'), - (0xF9BA, 'M', u'了'), - (0xF9BB, 'M', u'僚'), - (0xF9BC, 'M', u'寮'), - (0xF9BD, 'M', u'尿'), - (0xF9BE, 'M', u'料'), - (0xF9BF, 'M', u'樂'), - (0xF9C0, 'M', u'燎'), - (0xF9C1, 'M', u'療'), - (0xF9C2, 'M', u'蓼'), - (0xF9C3, 'M', u'遼'), - (0xF9C4, 'M', u'龍'), - (0xF9C5, 'M', u'暈'), - (0xF9C6, 'M', u'阮'), - (0xF9C7, 'M', u'劉'), - (0xF9C8, 'M', u'杻'), - (0xF9C9, 'M', u'柳'), - (0xF9CA, 'M', u'流'), - (0xF9CB, 'M', u'溜'), - (0xF9CC, 'M', u'琉'), - (0xF9CD, 'M', u'留'), - (0xF9CE, 'M', u'硫'), - (0xF9CF, 'M', u'紐'), - (0xF9D0, 'M', u'類'), - (0xF9D1, 'M', u'六'), - (0xF9D2, 'M', u'戮'), - (0xF9D3, 'M', u'陸'), - (0xF9D4, 'M', u'倫'), - (0xF9D5, 'M', u'崙'), - (0xF9D6, 'M', u'淪'), - (0xF9D7, 'M', u'輪'), - (0xF9D8, 'M', u'律'), - (0xF9D9, 'M', u'慄'), - (0xF9DA, 'M', u'栗'), - (0xF9DB, 'M', u'率'), - (0xF9DC, 'M', u'隆'), - (0xF9DD, 'M', u'利'), - (0xF9DE, 'M', u'吏'), - (0xF9DF, 'M', u'履'), - (0xF9E0, 'M', u'易'), - (0xF9E1, 'M', u'李'), - (0xF9E2, 'M', u'梨'), - (0xF9E3, 'M', u'泥'), - (0xF9E4, 'M', u'理'), - (0xF9E5, 'M', u'痢'), - (0xF9E6, 'M', u'罹'), - (0xF9E7, 'M', u'裏'), - (0xF9E8, 'M', u'裡'), - (0xF9E9, 'M', u'里'), - (0xF9EA, 'M', u'離'), - (0xF9EB, 'M', u'匿'), - (0xF9EC, 'M', u'溺'), - (0xF9ED, 'M', u'吝'), - (0xF9EE, 'M', u'燐'), - (0xF9EF, 'M', u'璘'), - (0xF9F0, 'M', u'藺'), - (0xF9F1, 'M', u'隣'), - (0xF9F2, 'M', u'鱗'), - (0xF9F3, 'M', u'麟'), - (0xF9F4, 'M', u'林'), - (0xF9F5, 'M', u'淋'), - (0xF9F6, 'M', u'臨'), - (0xF9F7, 'M', u'立'), - (0xF9F8, 'M', u'笠'), - (0xF9F9, 'M', u'粒'), - (0xF9FA, 'M', u'狀'), - (0xF9FB, 'M', u'炙'), - (0xF9FC, 'M', u'識'), - (0xF9FD, 'M', u'什'), - (0xF9FE, 'M', u'茶'), - (0xF9FF, 'M', u'刺'), - (0xFA00, 'M', u'切'), - (0xFA01, 'M', u'度'), - (0xFA02, 'M', u'拓'), - (0xFA03, 'M', u'糖'), - (0xFA04, 'M', u'宅'), - (0xFA05, 'M', u'洞'), - (0xFA06, 'M', u'暴'), - (0xFA07, 'M', u'輻'), - (0xFA08, 'M', u'行'), - (0xFA09, 'M', u'降'), - (0xFA0A, 'M', u'見'), - (0xFA0B, 'M', u'廓'), - (0xFA0C, 'M', u'兀'), - (0xFA0D, 'M', u'嗀'), + (0xF9B3, 'M', '靈'), + (0xF9B4, 'M', '領'), + (0xF9B5, 'M', '例'), + (0xF9B6, 'M', '禮'), + (0xF9B7, 'M', '醴'), + (0xF9B8, 'M', '隸'), + (0xF9B9, 'M', '惡'), + (0xF9BA, 'M', '了'), + (0xF9BB, 'M', '僚'), + (0xF9BC, 'M', '寮'), + (0xF9BD, 'M', '尿'), + (0xF9BE, 'M', '料'), + (0xF9BF, 'M', '樂'), + (0xF9C0, 'M', '燎'), + (0xF9C1, 'M', '療'), + (0xF9C2, 'M', '蓼'), + (0xF9C3, 'M', '遼'), + (0xF9C4, 'M', '龍'), + (0xF9C5, 'M', '暈'), + (0xF9C6, 'M', '阮'), + (0xF9C7, 'M', '劉'), + (0xF9C8, 'M', '杻'), + (0xF9C9, 'M', '柳'), + (0xF9CA, 'M', '流'), + (0xF9CB, 'M', '溜'), + (0xF9CC, 'M', '琉'), + (0xF9CD, 'M', '留'), + (0xF9CE, 'M', '硫'), + (0xF9CF, 'M', '紐'), + (0xF9D0, 'M', '類'), + (0xF9D1, 'M', '六'), + (0xF9D2, 'M', '戮'), + (0xF9D3, 'M', '陸'), + (0xF9D4, 'M', '倫'), + (0xF9D5, 'M', '崙'), + (0xF9D6, 'M', '淪'), + (0xF9D7, 'M', '輪'), + (0xF9D8, 'M', '律'), + (0xF9D9, 'M', '慄'), + (0xF9DA, 'M', '栗'), + (0xF9DB, 'M', '率'), + (0xF9DC, 'M', '隆'), + (0xF9DD, 'M', '利'), + (0xF9DE, 'M', '吏'), + (0xF9DF, 'M', '履'), + (0xF9E0, 'M', '易'), + (0xF9E1, 'M', '李'), + (0xF9E2, 'M', '梨'), + (0xF9E3, 'M', '泥'), + (0xF9E4, 'M', '理'), + (0xF9E5, 'M', '痢'), + (0xF9E6, 'M', '罹'), + (0xF9E7, 'M', '裏'), + (0xF9E8, 'M', '裡'), + (0xF9E9, 'M', '里'), + (0xF9EA, 'M', '離'), + (0xF9EB, 'M', '匿'), + (0xF9EC, 'M', '溺'), + (0xF9ED, 'M', '吝'), + (0xF9EE, 'M', '燐'), + (0xF9EF, 'M', '璘'), + (0xF9F0, 'M', '藺'), + (0xF9F1, 'M', '隣'), + (0xF9F2, 'M', '鱗'), + (0xF9F3, 'M', '麟'), + (0xF9F4, 'M', '林'), + (0xF9F5, 'M', '淋'), + (0xF9F6, 'M', '臨'), + (0xF9F7, 'M', '立'), + (0xF9F8, 'M', '笠'), + (0xF9F9, 'M', '粒'), + (0xF9FA, 'M', '狀'), + (0xF9FB, 'M', '炙'), + (0xF9FC, 'M', '識'), + (0xF9FD, 'M', '什'), + (0xF9FE, 'M', '茶'), + (0xF9FF, 'M', '刺'), + (0xFA00, 'M', '切'), + (0xFA01, 'M', '度'), + (0xFA02, 'M', '拓'), + (0xFA03, 'M', '糖'), + (0xFA04, 'M', '宅'), + (0xFA05, 'M', '洞'), + (0xFA06, 'M', '暴'), + (0xFA07, 'M', '輻'), + (0xFA08, 'M', '行'), + (0xFA09, 'M', '降'), + (0xFA0A, 'M', '見'), + (0xFA0B, 'M', '廓'), + (0xFA0C, 'M', '兀'), + (0xFA0D, 'M', '嗀'), (0xFA0E, 'V'), - (0xFA10, 'M', u'塚'), + (0xFA10, 'M', '塚'), (0xFA11, 'V'), - (0xFA12, 'M', u'晴'), + (0xFA12, 'M', '晴'), (0xFA13, 'V'), - (0xFA15, 'M', u'凞'), - (0xFA16, 'M', u'猪'), - (0xFA17, 'M', u'益'), - (0xFA18, 'M', u'礼'), - (0xFA19, 'M', u'神'), + (0xFA15, 'M', '凞'), + (0xFA16, 'M', '猪'), + (0xFA17, 'M', '益'), + (0xFA18, 'M', '礼'), ] def _seg_42(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFA1A, 'M', u'祥'), - (0xFA1B, 'M', u'福'), - (0xFA1C, 'M', u'靖'), - (0xFA1D, 'M', u'精'), - (0xFA1E, 'M', u'羽'), + (0xFA19, 'M', '神'), + (0xFA1A, 'M', '祥'), + (0xFA1B, 'M', '福'), + (0xFA1C, 'M', '靖'), + (0xFA1D, 'M', '精'), + (0xFA1E, 'M', '羽'), (0xFA1F, 'V'), - (0xFA20, 'M', u'蘒'), + (0xFA20, 'M', '蘒'), (0xFA21, 'V'), - (0xFA22, 'M', u'諸'), + (0xFA22, 'M', '諸'), (0xFA23, 'V'), - (0xFA25, 'M', u'逸'), - (0xFA26, 'M', u'都'), + (0xFA25, 'M', '逸'), + (0xFA26, 'M', '都'), (0xFA27, 'V'), - (0xFA2A, 'M', u'飯'), - (0xFA2B, 'M', u'飼'), - (0xFA2C, 'M', u'館'), - (0xFA2D, 'M', u'鶴'), - (0xFA2E, 'M', u'郞'), - (0xFA2F, 'M', u'隷'), - (0xFA30, 'M', u'侮'), - (0xFA31, 'M', u'僧'), - (0xFA32, 'M', u'免'), - (0xFA33, 'M', u'勉'), - (0xFA34, 'M', u'勤'), - (0xFA35, 'M', u'卑'), - (0xFA36, 'M', u'喝'), - (0xFA37, 'M', u'嘆'), - (0xFA38, 'M', u'器'), - (0xFA39, 'M', u'塀'), - (0xFA3A, 'M', u'墨'), - (0xFA3B, 'M', u'層'), - (0xFA3C, 'M', u'屮'), - (0xFA3D, 'M', u'悔'), - (0xFA3E, 'M', u'慨'), - (0xFA3F, 'M', u'憎'), - (0xFA40, 'M', u'懲'), - (0xFA41, 'M', u'敏'), - (0xFA42, 'M', u'既'), - (0xFA43, 'M', u'暑'), - (0xFA44, 'M', u'梅'), - (0xFA45, 'M', u'海'), - (0xFA46, 'M', u'渚'), - (0xFA47, 'M', u'漢'), - (0xFA48, 'M', u'煮'), - (0xFA49, 'M', u'爫'), - (0xFA4A, 'M', u'琢'), - (0xFA4B, 'M', u'碑'), - (0xFA4C, 'M', u'社'), - (0xFA4D, 'M', u'祉'), - (0xFA4E, 'M', u'祈'), - (0xFA4F, 'M', u'祐'), - (0xFA50, 'M', u'祖'), - (0xFA51, 'M', u'祝'), - (0xFA52, 'M', u'禍'), - (0xFA53, 'M', u'禎'), - (0xFA54, 'M', u'穀'), - (0xFA55, 'M', u'突'), - (0xFA56, 'M', u'節'), - (0xFA57, 'M', u'練'), - (0xFA58, 'M', u'縉'), - (0xFA59, 'M', u'繁'), - (0xFA5A, 'M', u'署'), - (0xFA5B, 'M', u'者'), - (0xFA5C, 'M', u'臭'), - (0xFA5D, 'M', u'艹'), - (0xFA5F, 'M', u'著'), - (0xFA60, 'M', u'褐'), - (0xFA61, 'M', u'視'), - (0xFA62, 'M', u'謁'), - (0xFA63, 'M', u'謹'), - (0xFA64, 'M', u'賓'), - (0xFA65, 'M', u'贈'), - (0xFA66, 'M', u'辶'), - (0xFA67, 'M', u'逸'), - (0xFA68, 'M', u'難'), - (0xFA69, 'M', u'響'), - (0xFA6A, 'M', u'頻'), - (0xFA6B, 'M', u'恵'), - (0xFA6C, 'M', u'𤋮'), - (0xFA6D, 'M', u'舘'), + (0xFA2A, 'M', '飯'), + (0xFA2B, 'M', '飼'), + (0xFA2C, 'M', '館'), + (0xFA2D, 'M', '鶴'), + (0xFA2E, 'M', '郞'), + (0xFA2F, 'M', '隷'), + (0xFA30, 'M', '侮'), + (0xFA31, 'M', '僧'), + (0xFA32, 'M', '免'), + (0xFA33, 'M', '勉'), + (0xFA34, 'M', '勤'), + (0xFA35, 'M', '卑'), + (0xFA36, 'M', '喝'), + (0xFA37, 'M', '嘆'), + (0xFA38, 'M', '器'), + (0xFA39, 'M', '塀'), + (0xFA3A, 'M', '墨'), + (0xFA3B, 'M', '層'), + (0xFA3C, 'M', '屮'), + (0xFA3D, 'M', '悔'), + (0xFA3E, 'M', '慨'), + (0xFA3F, 'M', '憎'), + (0xFA40, 'M', '懲'), + (0xFA41, 'M', '敏'), + (0xFA42, 'M', '既'), + (0xFA43, 'M', '暑'), + (0xFA44, 'M', '梅'), + (0xFA45, 'M', '海'), + (0xFA46, 'M', '渚'), + (0xFA47, 'M', '漢'), + (0xFA48, 'M', '煮'), + (0xFA49, 'M', '爫'), + (0xFA4A, 'M', '琢'), + (0xFA4B, 'M', '碑'), + (0xFA4C, 'M', '社'), + (0xFA4D, 'M', '祉'), + (0xFA4E, 'M', '祈'), + (0xFA4F, 'M', '祐'), + (0xFA50, 'M', '祖'), + (0xFA51, 'M', '祝'), + (0xFA52, 'M', '禍'), + (0xFA53, 'M', '禎'), + (0xFA54, 'M', '穀'), + (0xFA55, 'M', '突'), + (0xFA56, 'M', '節'), + (0xFA57, 'M', '練'), + (0xFA58, 'M', '縉'), + (0xFA59, 'M', '繁'), + (0xFA5A, 'M', '署'), + (0xFA5B, 'M', '者'), + (0xFA5C, 'M', '臭'), + (0xFA5D, 'M', '艹'), + (0xFA5F, 'M', '著'), + (0xFA60, 'M', '褐'), + (0xFA61, 'M', '視'), + (0xFA62, 'M', '謁'), + (0xFA63, 'M', '謹'), + (0xFA64, 'M', '賓'), + (0xFA65, 'M', '贈'), + (0xFA66, 'M', '辶'), + (0xFA67, 'M', '逸'), + (0xFA68, 'M', '難'), + (0xFA69, 'M', '響'), + (0xFA6A, 'M', '頻'), + (0xFA6B, 'M', '恵'), + (0xFA6C, 'M', '𤋮'), + (0xFA6D, 'M', '舘'), (0xFA6E, 'X'), - (0xFA70, 'M', u'並'), - (0xFA71, 'M', u'况'), - (0xFA72, 'M', u'全'), - (0xFA73, 'M', u'侀'), - (0xFA74, 'M', u'充'), - (0xFA75, 'M', u'冀'), - (0xFA76, 'M', u'勇'), - (0xFA77, 'M', u'勺'), - (0xFA78, 'M', u'喝'), - (0xFA79, 'M', u'啕'), - (0xFA7A, 'M', u'喙'), - (0xFA7B, 'M', u'嗢'), - (0xFA7C, 'M', u'塚'), - (0xFA7D, 'M', u'墳'), - (0xFA7E, 'M', u'奄'), - (0xFA7F, 'M', u'奔'), - (0xFA80, 'M', u'婢'), - (0xFA81, 'M', u'嬨'), - (0xFA82, 'M', u'廒'), + (0xFA70, 'M', '並'), + (0xFA71, 'M', '况'), + (0xFA72, 'M', '全'), + (0xFA73, 'M', '侀'), + (0xFA74, 'M', '充'), + (0xFA75, 'M', '冀'), + (0xFA76, 'M', '勇'), + (0xFA77, 'M', '勺'), + (0xFA78, 'M', '喝'), + (0xFA79, 'M', '啕'), + (0xFA7A, 'M', '喙'), + (0xFA7B, 'M', '嗢'), + (0xFA7C, 'M', '塚'), + (0xFA7D, 'M', '墳'), + (0xFA7E, 'M', '奄'), + (0xFA7F, 'M', '奔'), + (0xFA80, 'M', '婢'), + (0xFA81, 'M', '嬨'), ] def _seg_43(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFA83, 'M', u'廙'), - (0xFA84, 'M', u'彩'), - (0xFA85, 'M', u'徭'), - (0xFA86, 'M', u'惘'), - (0xFA87, 'M', u'慎'), - (0xFA88, 'M', u'愈'), - (0xFA89, 'M', u'憎'), - (0xFA8A, 'M', u'慠'), - (0xFA8B, 'M', u'懲'), - (0xFA8C, 'M', u'戴'), - (0xFA8D, 'M', u'揄'), - (0xFA8E, 'M', u'搜'), - (0xFA8F, 'M', u'摒'), - (0xFA90, 'M', u'敖'), - (0xFA91, 'M', u'晴'), - (0xFA92, 'M', u'朗'), - (0xFA93, 'M', u'望'), - (0xFA94, 'M', u'杖'), - (0xFA95, 'M', u'歹'), - (0xFA96, 'M', u'殺'), - (0xFA97, 'M', u'流'), - (0xFA98, 'M', u'滛'), - (0xFA99, 'M', u'滋'), - (0xFA9A, 'M', u'漢'), - (0xFA9B, 'M', u'瀞'), - (0xFA9C, 'M', u'煮'), - (0xFA9D, 'M', u'瞧'), - (0xFA9E, 'M', u'爵'), - (0xFA9F, 'M', u'犯'), - (0xFAA0, 'M', u'猪'), - (0xFAA1, 'M', u'瑱'), - (0xFAA2, 'M', u'甆'), - (0xFAA3, 'M', u'画'), - (0xFAA4, 'M', u'瘝'), - (0xFAA5, 'M', u'瘟'), - (0xFAA6, 'M', u'益'), - (0xFAA7, 'M', u'盛'), - (0xFAA8, 'M', u'直'), - (0xFAA9, 'M', u'睊'), - (0xFAAA, 'M', u'着'), - (0xFAAB, 'M', u'磌'), - (0xFAAC, 'M', u'窱'), - (0xFAAD, 'M', u'節'), - (0xFAAE, 'M', u'类'), - (0xFAAF, 'M', u'絛'), - (0xFAB0, 'M', u'練'), - (0xFAB1, 'M', u'缾'), - (0xFAB2, 'M', u'者'), - (0xFAB3, 'M', u'荒'), - (0xFAB4, 'M', u'華'), - (0xFAB5, 'M', u'蝹'), - (0xFAB6, 'M', u'襁'), - (0xFAB7, 'M', u'覆'), - (0xFAB8, 'M', u'視'), - (0xFAB9, 'M', u'調'), - (0xFABA, 'M', u'諸'), - (0xFABB, 'M', u'請'), - (0xFABC, 'M', u'謁'), - (0xFABD, 'M', u'諾'), - (0xFABE, 'M', u'諭'), - (0xFABF, 'M', u'謹'), - (0xFAC0, 'M', u'變'), - (0xFAC1, 'M', u'贈'), - (0xFAC2, 'M', u'輸'), - (0xFAC3, 'M', u'遲'), - (0xFAC4, 'M', u'醙'), - (0xFAC5, 'M', u'鉶'), - (0xFAC6, 'M', u'陼'), - (0xFAC7, 'M', u'難'), - (0xFAC8, 'M', u'靖'), - (0xFAC9, 'M', u'韛'), - (0xFACA, 'M', u'響'), - (0xFACB, 'M', u'頋'), - (0xFACC, 'M', u'頻'), - (0xFACD, 'M', u'鬒'), - (0xFACE, 'M', u'龜'), - (0xFACF, 'M', u'𢡊'), - (0xFAD0, 'M', u'𢡄'), - (0xFAD1, 'M', u'𣏕'), - (0xFAD2, 'M', u'㮝'), - (0xFAD3, 'M', u'䀘'), - (0xFAD4, 'M', u'䀹'), - (0xFAD5, 'M', u'𥉉'), - (0xFAD6, 'M', u'𥳐'), - (0xFAD7, 'M', u'𧻓'), - (0xFAD8, 'M', u'齃'), - (0xFAD9, 'M', u'龎'), + (0xFA82, 'M', '廒'), + (0xFA83, 'M', '廙'), + (0xFA84, 'M', '彩'), + (0xFA85, 'M', '徭'), + (0xFA86, 'M', '惘'), + (0xFA87, 'M', '慎'), + (0xFA88, 'M', '愈'), + (0xFA89, 'M', '憎'), + (0xFA8A, 'M', '慠'), + (0xFA8B, 'M', '懲'), + (0xFA8C, 'M', '戴'), + (0xFA8D, 'M', '揄'), + (0xFA8E, 'M', '搜'), + (0xFA8F, 'M', '摒'), + (0xFA90, 'M', '敖'), + (0xFA91, 'M', '晴'), + (0xFA92, 'M', '朗'), + (0xFA93, 'M', '望'), + (0xFA94, 'M', '杖'), + (0xFA95, 'M', '歹'), + (0xFA96, 'M', '殺'), + (0xFA97, 'M', '流'), + (0xFA98, 'M', '滛'), + (0xFA99, 'M', '滋'), + (0xFA9A, 'M', '漢'), + (0xFA9B, 'M', '瀞'), + (0xFA9C, 'M', '煮'), + (0xFA9D, 'M', '瞧'), + (0xFA9E, 'M', '爵'), + (0xFA9F, 'M', '犯'), + (0xFAA0, 'M', '猪'), + (0xFAA1, 'M', '瑱'), + (0xFAA2, 'M', '甆'), + (0xFAA3, 'M', '画'), + (0xFAA4, 'M', '瘝'), + (0xFAA5, 'M', '瘟'), + (0xFAA6, 'M', '益'), + (0xFAA7, 'M', '盛'), + (0xFAA8, 'M', '直'), + (0xFAA9, 'M', '睊'), + (0xFAAA, 'M', '着'), + (0xFAAB, 'M', '磌'), + (0xFAAC, 'M', '窱'), + (0xFAAD, 'M', '節'), + (0xFAAE, 'M', '类'), + (0xFAAF, 'M', '絛'), + (0xFAB0, 'M', '練'), + (0xFAB1, 'M', '缾'), + (0xFAB2, 'M', '者'), + (0xFAB3, 'M', '荒'), + (0xFAB4, 'M', '華'), + (0xFAB5, 'M', '蝹'), + (0xFAB6, 'M', '襁'), + (0xFAB7, 'M', '覆'), + (0xFAB8, 'M', '視'), + (0xFAB9, 'M', '調'), + (0xFABA, 'M', '諸'), + (0xFABB, 'M', '請'), + (0xFABC, 'M', '謁'), + (0xFABD, 'M', '諾'), + (0xFABE, 'M', '諭'), + (0xFABF, 'M', '謹'), + (0xFAC0, 'M', '變'), + (0xFAC1, 'M', '贈'), + (0xFAC2, 'M', '輸'), + (0xFAC3, 'M', '遲'), + (0xFAC4, 'M', '醙'), + (0xFAC5, 'M', '鉶'), + (0xFAC6, 'M', '陼'), + (0xFAC7, 'M', '難'), + (0xFAC8, 'M', '靖'), + (0xFAC9, 'M', '韛'), + (0xFACA, 'M', '響'), + (0xFACB, 'M', '頋'), + (0xFACC, 'M', '頻'), + (0xFACD, 'M', '鬒'), + (0xFACE, 'M', '龜'), + (0xFACF, 'M', '𢡊'), + (0xFAD0, 'M', '𢡄'), + (0xFAD1, 'M', '𣏕'), + (0xFAD2, 'M', '㮝'), + (0xFAD3, 'M', '䀘'), + (0xFAD4, 'M', '䀹'), + (0xFAD5, 'M', '𥉉'), + (0xFAD6, 'M', '𥳐'), + (0xFAD7, 'M', '𧻓'), + (0xFAD8, 'M', '齃'), + (0xFAD9, 'M', '龎'), (0xFADA, 'X'), - (0xFB00, 'M', u'ff'), - (0xFB01, 'M', u'fi'), - (0xFB02, 'M', u'fl'), - (0xFB03, 'M', u'ffi'), - (0xFB04, 'M', u'ffl'), - (0xFB05, 'M', u'st'), + (0xFB00, 'M', 'ff'), + (0xFB01, 'M', 'fi'), + (0xFB02, 'M', 'fl'), + (0xFB03, 'M', 'ffi'), + (0xFB04, 'M', 'ffl'), + (0xFB05, 'M', 'st'), (0xFB07, 'X'), - (0xFB13, 'M', u'մն'), - (0xFB14, 'M', u'մե'), - (0xFB15, 'M', u'մի'), - (0xFB16, 'M', u'վն'), - (0xFB17, 'M', u'մխ'), + (0xFB13, 'M', 'մն'), + (0xFB14, 'M', 'մե'), + (0xFB15, 'M', 'մի'), + (0xFB16, 'M', 'վն'), ] def _seg_44(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ + (0xFB17, 'M', 'մխ'), (0xFB18, 'X'), - (0xFB1D, 'M', u'יִ'), + (0xFB1D, 'M', 'יִ'), (0xFB1E, 'V'), - (0xFB1F, 'M', u'ײַ'), - (0xFB20, 'M', u'ע'), - (0xFB21, 'M', u'א'), - (0xFB22, 'M', u'ד'), - (0xFB23, 'M', u'ה'), - (0xFB24, 'M', u'כ'), - (0xFB25, 'M', u'ל'), - (0xFB26, 'M', u'ם'), - (0xFB27, 'M', u'ר'), - (0xFB28, 'M', u'ת'), - (0xFB29, '3', u'+'), - (0xFB2A, 'M', u'שׁ'), - (0xFB2B, 'M', u'שׂ'), - (0xFB2C, 'M', u'שּׁ'), - (0xFB2D, 'M', u'שּׂ'), - (0xFB2E, 'M', u'אַ'), - (0xFB2F, 'M', u'אָ'), - (0xFB30, 'M', u'אּ'), - (0xFB31, 'M', u'בּ'), - (0xFB32, 'M', u'גּ'), - (0xFB33, 'M', u'דּ'), - (0xFB34, 'M', u'הּ'), - (0xFB35, 'M', u'וּ'), - (0xFB36, 'M', u'זּ'), + (0xFB1F, 'M', 'ײַ'), + (0xFB20, 'M', 'ע'), + (0xFB21, 'M', 'א'), + (0xFB22, 'M', 'ד'), + (0xFB23, 'M', 'ה'), + (0xFB24, 'M', 'כ'), + (0xFB25, 'M', 'ל'), + (0xFB26, 'M', 'ם'), + (0xFB27, 'M', 'ר'), + (0xFB28, 'M', 'ת'), + (0xFB29, '3', '+'), + (0xFB2A, 'M', 'שׁ'), + (0xFB2B, 'M', 'שׂ'), + (0xFB2C, 'M', 'שּׁ'), + (0xFB2D, 'M', 'שּׂ'), + (0xFB2E, 'M', 'אַ'), + (0xFB2F, 'M', 'אָ'), + (0xFB30, 'M', 'אּ'), + (0xFB31, 'M', 'בּ'), + (0xFB32, 'M', 'גּ'), + (0xFB33, 'M', 'דּ'), + (0xFB34, 'M', 'הּ'), + (0xFB35, 'M', 'וּ'), + (0xFB36, 'M', 'זּ'), (0xFB37, 'X'), - (0xFB38, 'M', u'טּ'), - (0xFB39, 'M', u'יּ'), - (0xFB3A, 'M', u'ךּ'), - (0xFB3B, 'M', u'כּ'), - (0xFB3C, 'M', u'לּ'), + (0xFB38, 'M', 'טּ'), + (0xFB39, 'M', 'יּ'), + (0xFB3A, 'M', 'ךּ'), + (0xFB3B, 'M', 'כּ'), + (0xFB3C, 'M', 'לּ'), (0xFB3D, 'X'), - (0xFB3E, 'M', u'מּ'), + (0xFB3E, 'M', 'מּ'), (0xFB3F, 'X'), - (0xFB40, 'M', u'נּ'), - (0xFB41, 'M', u'סּ'), + (0xFB40, 'M', 'נּ'), + (0xFB41, 'M', 'סּ'), (0xFB42, 'X'), - (0xFB43, 'M', u'ףּ'), - (0xFB44, 'M', u'פּ'), + (0xFB43, 'M', 'ףּ'), + (0xFB44, 'M', 'פּ'), (0xFB45, 'X'), - (0xFB46, 'M', u'צּ'), - (0xFB47, 'M', u'קּ'), - (0xFB48, 'M', u'רּ'), - (0xFB49, 'M', u'שּ'), - (0xFB4A, 'M', u'תּ'), - (0xFB4B, 'M', u'וֹ'), - (0xFB4C, 'M', u'בֿ'), - (0xFB4D, 'M', u'כֿ'), - (0xFB4E, 'M', u'פֿ'), - (0xFB4F, 'M', u'אל'), - (0xFB50, 'M', u'ٱ'), - (0xFB52, 'M', u'ٻ'), - (0xFB56, 'M', u'پ'), - (0xFB5A, 'M', u'ڀ'), - (0xFB5E, 'M', u'ٺ'), - (0xFB62, 'M', u'ٿ'), - (0xFB66, 'M', u'ٹ'), - (0xFB6A, 'M', u'ڤ'), - (0xFB6E, 'M', u'ڦ'), - (0xFB72, 'M', u'ڄ'), - (0xFB76, 'M', u'ڃ'), - (0xFB7A, 'M', u'چ'), - (0xFB7E, 'M', u'ڇ'), - (0xFB82, 'M', u'ڍ'), - (0xFB84, 'M', u'ڌ'), - (0xFB86, 'M', u'ڎ'), - (0xFB88, 'M', u'ڈ'), - (0xFB8A, 'M', u'ژ'), - (0xFB8C, 'M', u'ڑ'), - (0xFB8E, 'M', u'ک'), - (0xFB92, 'M', u'گ'), - (0xFB96, 'M', u'ڳ'), - (0xFB9A, 'M', u'ڱ'), - (0xFB9E, 'M', u'ں'), - (0xFBA0, 'M', u'ڻ'), - (0xFBA4, 'M', u'ۀ'), - (0xFBA6, 'M', u'ہ'), - (0xFBAA, 'M', u'ھ'), - (0xFBAE, 'M', u'ے'), - (0xFBB0, 'M', u'ۓ'), + (0xFB46, 'M', 'צּ'), + (0xFB47, 'M', 'קּ'), + (0xFB48, 'M', 'רּ'), + (0xFB49, 'M', 'שּ'), + (0xFB4A, 'M', 'תּ'), + (0xFB4B, 'M', 'וֹ'), + (0xFB4C, 'M', 'בֿ'), + (0xFB4D, 'M', 'כֿ'), + (0xFB4E, 'M', 'פֿ'), + (0xFB4F, 'M', 'אל'), + (0xFB50, 'M', 'ٱ'), + (0xFB52, 'M', 'ٻ'), + (0xFB56, 'M', 'پ'), + (0xFB5A, 'M', 'ڀ'), + (0xFB5E, 'M', 'ٺ'), + (0xFB62, 'M', 'ٿ'), + (0xFB66, 'M', 'ٹ'), + (0xFB6A, 'M', 'ڤ'), + (0xFB6E, 'M', 'ڦ'), + (0xFB72, 'M', 'ڄ'), + (0xFB76, 'M', 'ڃ'), + (0xFB7A, 'M', 'چ'), + (0xFB7E, 'M', 'ڇ'), + (0xFB82, 'M', 'ڍ'), + (0xFB84, 'M', 'ڌ'), + (0xFB86, 'M', 'ڎ'), + (0xFB88, 'M', 'ڈ'), + (0xFB8A, 'M', 'ژ'), + (0xFB8C, 'M', 'ڑ'), + (0xFB8E, 'M', 'ک'), + (0xFB92, 'M', 'گ'), + (0xFB96, 'M', 'ڳ'), + (0xFB9A, 'M', 'ڱ'), + (0xFB9E, 'M', 'ں'), + (0xFBA0, 'M', 'ڻ'), + (0xFBA4, 'M', 'ۀ'), + (0xFBA6, 'M', 'ہ'), + (0xFBAA, 'M', 'ھ'), + (0xFBAE, 'M', 'ے'), + (0xFBB0, 'M', 'ۓ'), (0xFBB2, 'V'), (0xFBC2, 'X'), - (0xFBD3, 'M', u'ڭ'), - (0xFBD7, 'M', u'ۇ'), - (0xFBD9, 'M', u'ۆ'), - (0xFBDB, 'M', u'ۈ'), - (0xFBDD, 'M', u'ۇٴ'), - (0xFBDE, 'M', u'ۋ'), - (0xFBE0, 'M', u'ۅ'), - (0xFBE2, 'M', u'ۉ'), - (0xFBE4, 'M', u'ې'), - (0xFBE8, 'M', u'ى'), - (0xFBEA, 'M', u'ئا'), - (0xFBEC, 'M', u'ئە'), - (0xFBEE, 'M', u'ئو'), - (0xFBF0, 'M', u'ئۇ'), - (0xFBF2, 'M', u'ئۆ'), - (0xFBF4, 'M', u'ئۈ'), + (0xFBD3, 'M', 'ڭ'), + (0xFBD7, 'M', 'ۇ'), + (0xFBD9, 'M', 'ۆ'), + (0xFBDB, 'M', 'ۈ'), + (0xFBDD, 'M', 'ۇٴ'), + (0xFBDE, 'M', 'ۋ'), + (0xFBE0, 'M', 'ۅ'), + (0xFBE2, 'M', 'ۉ'), + (0xFBE4, 'M', 'ې'), + (0xFBE8, 'M', 'ى'), + (0xFBEA, 'M', 'ئا'), + (0xFBEC, 'M', 'ئە'), + (0xFBEE, 'M', 'ئو'), + (0xFBF0, 'M', 'ئۇ'), + (0xFBF2, 'M', 'ئۆ'), ] def _seg_45(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFBF6, 'M', u'ئې'), - (0xFBF9, 'M', u'ئى'), - (0xFBFC, 'M', u'ی'), - (0xFC00, 'M', u'ئج'), - (0xFC01, 'M', u'ئح'), - (0xFC02, 'M', u'ئم'), - (0xFC03, 'M', u'ئى'), - (0xFC04, 'M', u'ئي'), - (0xFC05, 'M', u'بج'), - (0xFC06, 'M', u'بح'), - (0xFC07, 'M', u'بخ'), - (0xFC08, 'M', u'بم'), - (0xFC09, 'M', u'بى'), - (0xFC0A, 'M', u'بي'), - (0xFC0B, 'M', u'تج'), - (0xFC0C, 'M', u'تح'), - (0xFC0D, 'M', u'تخ'), - (0xFC0E, 'M', u'تم'), - (0xFC0F, 'M', u'تى'), - (0xFC10, 'M', u'تي'), - (0xFC11, 'M', u'ثج'), - (0xFC12, 'M', u'ثم'), - (0xFC13, 'M', u'ثى'), - (0xFC14, 'M', u'ثي'), - (0xFC15, 'M', u'جح'), - (0xFC16, 'M', u'جم'), - (0xFC17, 'M', u'حج'), - (0xFC18, 'M', u'حم'), - (0xFC19, 'M', u'خج'), - (0xFC1A, 'M', u'خح'), - (0xFC1B, 'M', u'خم'), - (0xFC1C, 'M', u'سج'), - (0xFC1D, 'M', u'سح'), - (0xFC1E, 'M', u'سخ'), - (0xFC1F, 'M', u'سم'), - (0xFC20, 'M', u'صح'), - (0xFC21, 'M', u'صم'), - (0xFC22, 'M', u'ضج'), - (0xFC23, 'M', u'ضح'), - (0xFC24, 'M', u'ضخ'), - (0xFC25, 'M', u'ضم'), - (0xFC26, 'M', u'طح'), - (0xFC27, 'M', u'طم'), - (0xFC28, 'M', u'ظم'), - (0xFC29, 'M', u'عج'), - (0xFC2A, 'M', u'عم'), - (0xFC2B, 'M', u'غج'), - (0xFC2C, 'M', u'غم'), - (0xFC2D, 'M', u'فج'), - (0xFC2E, 'M', u'فح'), - (0xFC2F, 'M', u'فخ'), - (0xFC30, 'M', u'فم'), - (0xFC31, 'M', u'فى'), - (0xFC32, 'M', u'في'), - (0xFC33, 'M', u'قح'), - (0xFC34, 'M', u'قم'), - (0xFC35, 'M', u'قى'), - (0xFC36, 'M', u'قي'), - (0xFC37, 'M', u'كا'), - (0xFC38, 'M', u'كج'), - (0xFC39, 'M', u'كح'), - (0xFC3A, 'M', u'كخ'), - (0xFC3B, 'M', u'كل'), - (0xFC3C, 'M', u'كم'), - (0xFC3D, 'M', u'كى'), - (0xFC3E, 'M', u'كي'), - (0xFC3F, 'M', u'لج'), - (0xFC40, 'M', u'لح'), - (0xFC41, 'M', u'لخ'), - (0xFC42, 'M', u'لم'), - (0xFC43, 'M', u'لى'), - (0xFC44, 'M', u'لي'), - (0xFC45, 'M', u'مج'), - (0xFC46, 'M', u'مح'), - (0xFC47, 'M', u'مخ'), - (0xFC48, 'M', u'مم'), - (0xFC49, 'M', u'مى'), - (0xFC4A, 'M', u'مي'), - (0xFC4B, 'M', u'نج'), - (0xFC4C, 'M', u'نح'), - (0xFC4D, 'M', u'نخ'), - (0xFC4E, 'M', u'نم'), - (0xFC4F, 'M', u'نى'), - (0xFC50, 'M', u'ني'), - (0xFC51, 'M', u'هج'), - (0xFC52, 'M', u'هم'), - (0xFC53, 'M', u'هى'), - (0xFC54, 'M', u'هي'), - (0xFC55, 'M', u'يج'), - (0xFC56, 'M', u'يح'), - (0xFC57, 'M', u'يخ'), - (0xFC58, 'M', u'يم'), - (0xFC59, 'M', u'يى'), - (0xFC5A, 'M', u'يي'), - (0xFC5B, 'M', u'ذٰ'), - (0xFC5C, 'M', u'رٰ'), - (0xFC5D, 'M', u'ىٰ'), - (0xFC5E, '3', u' ٌّ'), - (0xFC5F, '3', u' ٍّ'), - (0xFC60, '3', u' َّ'), + (0xFBF4, 'M', 'ئۈ'), + (0xFBF6, 'M', 'ئې'), + (0xFBF9, 'M', 'ئى'), + (0xFBFC, 'M', 'ی'), + (0xFC00, 'M', 'ئج'), + (0xFC01, 'M', 'ئح'), + (0xFC02, 'M', 'ئم'), + (0xFC03, 'M', 'ئى'), + (0xFC04, 'M', 'ئي'), + (0xFC05, 'M', 'بج'), + (0xFC06, 'M', 'بح'), + (0xFC07, 'M', 'بخ'), + (0xFC08, 'M', 'بم'), + (0xFC09, 'M', 'بى'), + (0xFC0A, 'M', 'بي'), + (0xFC0B, 'M', 'تج'), + (0xFC0C, 'M', 'تح'), + (0xFC0D, 'M', 'تخ'), + (0xFC0E, 'M', 'تم'), + (0xFC0F, 'M', 'تى'), + (0xFC10, 'M', 'تي'), + (0xFC11, 'M', 'ثج'), + (0xFC12, 'M', 'ثم'), + (0xFC13, 'M', 'ثى'), + (0xFC14, 'M', 'ثي'), + (0xFC15, 'M', 'جح'), + (0xFC16, 'M', 'جم'), + (0xFC17, 'M', 'حج'), + (0xFC18, 'M', 'حم'), + (0xFC19, 'M', 'خج'), + (0xFC1A, 'M', 'خح'), + (0xFC1B, 'M', 'خم'), + (0xFC1C, 'M', 'سج'), + (0xFC1D, 'M', 'سح'), + (0xFC1E, 'M', 'سخ'), + (0xFC1F, 'M', 'سم'), + (0xFC20, 'M', 'صح'), + (0xFC21, 'M', 'صم'), + (0xFC22, 'M', 'ضج'), + (0xFC23, 'M', 'ضح'), + (0xFC24, 'M', 'ضخ'), + (0xFC25, 'M', 'ضم'), + (0xFC26, 'M', 'طح'), + (0xFC27, 'M', 'طم'), + (0xFC28, 'M', 'ظم'), + (0xFC29, 'M', 'عج'), + (0xFC2A, 'M', 'عم'), + (0xFC2B, 'M', 'غج'), + (0xFC2C, 'M', 'غم'), + (0xFC2D, 'M', 'فج'), + (0xFC2E, 'M', 'فح'), + (0xFC2F, 'M', 'فخ'), + (0xFC30, 'M', 'فم'), + (0xFC31, 'M', 'فى'), + (0xFC32, 'M', 'في'), + (0xFC33, 'M', 'قح'), + (0xFC34, 'M', 'قم'), + (0xFC35, 'M', 'قى'), + (0xFC36, 'M', 'قي'), + (0xFC37, 'M', 'كا'), + (0xFC38, 'M', 'كج'), + (0xFC39, 'M', 'كح'), + (0xFC3A, 'M', 'كخ'), + (0xFC3B, 'M', 'كل'), + (0xFC3C, 'M', 'كم'), + (0xFC3D, 'M', 'كى'), + (0xFC3E, 'M', 'كي'), + (0xFC3F, 'M', 'لج'), + (0xFC40, 'M', 'لح'), + (0xFC41, 'M', 'لخ'), + (0xFC42, 'M', 'لم'), + (0xFC43, 'M', 'لى'), + (0xFC44, 'M', 'لي'), + (0xFC45, 'M', 'مج'), + (0xFC46, 'M', 'مح'), + (0xFC47, 'M', 'مخ'), + (0xFC48, 'M', 'مم'), + (0xFC49, 'M', 'مى'), + (0xFC4A, 'M', 'مي'), + (0xFC4B, 'M', 'نج'), + (0xFC4C, 'M', 'نح'), + (0xFC4D, 'M', 'نخ'), + (0xFC4E, 'M', 'نم'), + (0xFC4F, 'M', 'نى'), + (0xFC50, 'M', 'ني'), + (0xFC51, 'M', 'هج'), + (0xFC52, 'M', 'هم'), + (0xFC53, 'M', 'هى'), + (0xFC54, 'M', 'هي'), + (0xFC55, 'M', 'يج'), + (0xFC56, 'M', 'يح'), + (0xFC57, 'M', 'يخ'), + (0xFC58, 'M', 'يم'), + (0xFC59, 'M', 'يى'), + (0xFC5A, 'M', 'يي'), + (0xFC5B, 'M', 'ذٰ'), + (0xFC5C, 'M', 'رٰ'), + (0xFC5D, 'M', 'ىٰ'), + (0xFC5E, '3', ' ٌّ'), + (0xFC5F, '3', ' ٍّ'), ] def _seg_46(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFC61, '3', u' ُّ'), - (0xFC62, '3', u' ِّ'), - (0xFC63, '3', u' ّٰ'), - (0xFC64, 'M', u'ئر'), - (0xFC65, 'M', u'ئز'), - (0xFC66, 'M', u'ئم'), - (0xFC67, 'M', u'ئن'), - (0xFC68, 'M', u'ئى'), - (0xFC69, 'M', u'ئي'), - (0xFC6A, 'M', u'بر'), - (0xFC6B, 'M', u'بز'), - (0xFC6C, 'M', u'بم'), - (0xFC6D, 'M', u'بن'), - (0xFC6E, 'M', u'بى'), - (0xFC6F, 'M', u'بي'), - (0xFC70, 'M', u'تر'), - (0xFC71, 'M', u'تز'), - (0xFC72, 'M', u'تم'), - (0xFC73, 'M', u'تن'), - (0xFC74, 'M', u'تى'), - (0xFC75, 'M', u'تي'), - (0xFC76, 'M', u'ثر'), - (0xFC77, 'M', u'ثز'), - (0xFC78, 'M', u'ثم'), - (0xFC79, 'M', u'ثن'), - (0xFC7A, 'M', u'ثى'), - (0xFC7B, 'M', u'ثي'), - (0xFC7C, 'M', u'فى'), - (0xFC7D, 'M', u'في'), - (0xFC7E, 'M', u'قى'), - (0xFC7F, 'M', u'قي'), - (0xFC80, 'M', u'كا'), - (0xFC81, 'M', u'كل'), - (0xFC82, 'M', u'كم'), - (0xFC83, 'M', u'كى'), - (0xFC84, 'M', u'كي'), - (0xFC85, 'M', u'لم'), - (0xFC86, 'M', u'لى'), - (0xFC87, 'M', u'لي'), - (0xFC88, 'M', u'ما'), - (0xFC89, 'M', u'مم'), - (0xFC8A, 'M', u'نر'), - (0xFC8B, 'M', u'نز'), - (0xFC8C, 'M', u'نم'), - (0xFC8D, 'M', u'نن'), - (0xFC8E, 'M', u'نى'), - (0xFC8F, 'M', u'ني'), - (0xFC90, 'M', u'ىٰ'), - (0xFC91, 'M', u'ير'), - (0xFC92, 'M', u'يز'), - (0xFC93, 'M', u'يم'), - (0xFC94, 'M', u'ين'), - (0xFC95, 'M', u'يى'), - (0xFC96, 'M', u'يي'), - (0xFC97, 'M', u'ئج'), - (0xFC98, 'M', u'ئح'), - (0xFC99, 'M', u'ئخ'), - (0xFC9A, 'M', u'ئم'), - (0xFC9B, 'M', u'ئه'), - (0xFC9C, 'M', u'بج'), - (0xFC9D, 'M', u'بح'), - (0xFC9E, 'M', u'بخ'), - (0xFC9F, 'M', u'بم'), - (0xFCA0, 'M', u'به'), - (0xFCA1, 'M', u'تج'), - (0xFCA2, 'M', u'تح'), - (0xFCA3, 'M', u'تخ'), - (0xFCA4, 'M', u'تم'), - (0xFCA5, 'M', u'ته'), - (0xFCA6, 'M', u'ثم'), - (0xFCA7, 'M', u'جح'), - (0xFCA8, 'M', u'جم'), - (0xFCA9, 'M', u'حج'), - (0xFCAA, 'M', u'حم'), - (0xFCAB, 'M', u'خج'), - (0xFCAC, 'M', u'خم'), - (0xFCAD, 'M', u'سج'), - (0xFCAE, 'M', u'سح'), - (0xFCAF, 'M', u'سخ'), - (0xFCB0, 'M', u'سم'), - (0xFCB1, 'M', u'صح'), - (0xFCB2, 'M', u'صخ'), - (0xFCB3, 'M', u'صم'), - (0xFCB4, 'M', u'ضج'), - (0xFCB5, 'M', u'ضح'), - (0xFCB6, 'M', u'ضخ'), - (0xFCB7, 'M', u'ضم'), - (0xFCB8, 'M', u'طح'), - (0xFCB9, 'M', u'ظم'), - (0xFCBA, 'M', u'عج'), - (0xFCBB, 'M', u'عم'), - (0xFCBC, 'M', u'غج'), - (0xFCBD, 'M', u'غم'), - (0xFCBE, 'M', u'فج'), - (0xFCBF, 'M', u'فح'), - (0xFCC0, 'M', u'فخ'), - (0xFCC1, 'M', u'فم'), - (0xFCC2, 'M', u'قح'), - (0xFCC3, 'M', u'قم'), - (0xFCC4, 'M', u'كج'), + (0xFC60, '3', ' َّ'), + (0xFC61, '3', ' ُّ'), + (0xFC62, '3', ' ِّ'), + (0xFC63, '3', ' ّٰ'), + (0xFC64, 'M', 'ئر'), + (0xFC65, 'M', 'ئز'), + (0xFC66, 'M', 'ئم'), + (0xFC67, 'M', 'ئن'), + (0xFC68, 'M', 'ئى'), + (0xFC69, 'M', 'ئي'), + (0xFC6A, 'M', 'بر'), + (0xFC6B, 'M', 'بز'), + (0xFC6C, 'M', 'بم'), + (0xFC6D, 'M', 'بن'), + (0xFC6E, 'M', 'بى'), + (0xFC6F, 'M', 'بي'), + (0xFC70, 'M', 'تر'), + (0xFC71, 'M', 'تز'), + (0xFC72, 'M', 'تم'), + (0xFC73, 'M', 'تن'), + (0xFC74, 'M', 'تى'), + (0xFC75, 'M', 'تي'), + (0xFC76, 'M', 'ثر'), + (0xFC77, 'M', 'ثز'), + (0xFC78, 'M', 'ثم'), + (0xFC79, 'M', 'ثن'), + (0xFC7A, 'M', 'ثى'), + (0xFC7B, 'M', 'ثي'), + (0xFC7C, 'M', 'فى'), + (0xFC7D, 'M', 'في'), + (0xFC7E, 'M', 'قى'), + (0xFC7F, 'M', 'قي'), + (0xFC80, 'M', 'كا'), + (0xFC81, 'M', 'كل'), + (0xFC82, 'M', 'كم'), + (0xFC83, 'M', 'كى'), + (0xFC84, 'M', 'كي'), + (0xFC85, 'M', 'لم'), + (0xFC86, 'M', 'لى'), + (0xFC87, 'M', 'لي'), + (0xFC88, 'M', 'ما'), + (0xFC89, 'M', 'مم'), + (0xFC8A, 'M', 'نر'), + (0xFC8B, 'M', 'نز'), + (0xFC8C, 'M', 'نم'), + (0xFC8D, 'M', 'نن'), + (0xFC8E, 'M', 'نى'), + (0xFC8F, 'M', 'ني'), + (0xFC90, 'M', 'ىٰ'), + (0xFC91, 'M', 'ير'), + (0xFC92, 'M', 'يز'), + (0xFC93, 'M', 'يم'), + (0xFC94, 'M', 'ين'), + (0xFC95, 'M', 'يى'), + (0xFC96, 'M', 'يي'), + (0xFC97, 'M', 'ئج'), + (0xFC98, 'M', 'ئح'), + (0xFC99, 'M', 'ئخ'), + (0xFC9A, 'M', 'ئم'), + (0xFC9B, 'M', 'ئه'), + (0xFC9C, 'M', 'بج'), + (0xFC9D, 'M', 'بح'), + (0xFC9E, 'M', 'بخ'), + (0xFC9F, 'M', 'بم'), + (0xFCA0, 'M', 'به'), + (0xFCA1, 'M', 'تج'), + (0xFCA2, 'M', 'تح'), + (0xFCA3, 'M', 'تخ'), + (0xFCA4, 'M', 'تم'), + (0xFCA5, 'M', 'ته'), + (0xFCA6, 'M', 'ثم'), + (0xFCA7, 'M', 'جح'), + (0xFCA8, 'M', 'جم'), + (0xFCA9, 'M', 'حج'), + (0xFCAA, 'M', 'حم'), + (0xFCAB, 'M', 'خج'), + (0xFCAC, 'M', 'خم'), + (0xFCAD, 'M', 'سج'), + (0xFCAE, 'M', 'سح'), + (0xFCAF, 'M', 'سخ'), + (0xFCB0, 'M', 'سم'), + (0xFCB1, 'M', 'صح'), + (0xFCB2, 'M', 'صخ'), + (0xFCB3, 'M', 'صم'), + (0xFCB4, 'M', 'ضج'), + (0xFCB5, 'M', 'ضح'), + (0xFCB6, 'M', 'ضخ'), + (0xFCB7, 'M', 'ضم'), + (0xFCB8, 'M', 'طح'), + (0xFCB9, 'M', 'ظم'), + (0xFCBA, 'M', 'عج'), + (0xFCBB, 'M', 'عم'), + (0xFCBC, 'M', 'غج'), + (0xFCBD, 'M', 'غم'), + (0xFCBE, 'M', 'فج'), + (0xFCBF, 'M', 'فح'), + (0xFCC0, 'M', 'فخ'), + (0xFCC1, 'M', 'فم'), + (0xFCC2, 'M', 'قح'), + (0xFCC3, 'M', 'قم'), ] def _seg_47(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFCC5, 'M', u'كح'), - (0xFCC6, 'M', u'كخ'), - (0xFCC7, 'M', u'كل'), - (0xFCC8, 'M', u'كم'), - (0xFCC9, 'M', u'لج'), - (0xFCCA, 'M', u'لح'), - (0xFCCB, 'M', u'لخ'), - (0xFCCC, 'M', u'لم'), - (0xFCCD, 'M', u'له'), - (0xFCCE, 'M', u'مج'), - (0xFCCF, 'M', u'مح'), - (0xFCD0, 'M', u'مخ'), - (0xFCD1, 'M', u'مم'), - (0xFCD2, 'M', u'نج'), - (0xFCD3, 'M', u'نح'), - (0xFCD4, 'M', u'نخ'), - (0xFCD5, 'M', u'نم'), - (0xFCD6, 'M', u'نه'), - (0xFCD7, 'M', u'هج'), - (0xFCD8, 'M', u'هم'), - (0xFCD9, 'M', u'هٰ'), - (0xFCDA, 'M', u'يج'), - (0xFCDB, 'M', u'يح'), - (0xFCDC, 'M', u'يخ'), - (0xFCDD, 'M', u'يم'), - (0xFCDE, 'M', u'يه'), - (0xFCDF, 'M', u'ئم'), - (0xFCE0, 'M', u'ئه'), - (0xFCE1, 'M', u'بم'), - (0xFCE2, 'M', u'به'), - (0xFCE3, 'M', u'تم'), - (0xFCE4, 'M', u'ته'), - (0xFCE5, 'M', u'ثم'), - (0xFCE6, 'M', u'ثه'), - (0xFCE7, 'M', u'سم'), - (0xFCE8, 'M', u'سه'), - (0xFCE9, 'M', u'شم'), - (0xFCEA, 'M', u'شه'), - (0xFCEB, 'M', u'كل'), - (0xFCEC, 'M', u'كم'), - (0xFCED, 'M', u'لم'), - (0xFCEE, 'M', u'نم'), - (0xFCEF, 'M', u'نه'), - (0xFCF0, 'M', u'يم'), - (0xFCF1, 'M', u'يه'), - (0xFCF2, 'M', u'ـَّ'), - (0xFCF3, 'M', u'ـُّ'), - (0xFCF4, 'M', u'ـِّ'), - (0xFCF5, 'M', u'طى'), - (0xFCF6, 'M', u'طي'), - (0xFCF7, 'M', u'عى'), - (0xFCF8, 'M', u'عي'), - (0xFCF9, 'M', u'غى'), - (0xFCFA, 'M', u'غي'), - (0xFCFB, 'M', u'سى'), - (0xFCFC, 'M', u'سي'), - (0xFCFD, 'M', u'شى'), - (0xFCFE, 'M', u'شي'), - (0xFCFF, 'M', u'حى'), - (0xFD00, 'M', u'حي'), - (0xFD01, 'M', u'جى'), - (0xFD02, 'M', u'جي'), - (0xFD03, 'M', u'خى'), - (0xFD04, 'M', u'خي'), - (0xFD05, 'M', u'صى'), - (0xFD06, 'M', u'صي'), - (0xFD07, 'M', u'ضى'), - (0xFD08, 'M', u'ضي'), - (0xFD09, 'M', u'شج'), - (0xFD0A, 'M', u'شح'), - (0xFD0B, 'M', u'شخ'), - (0xFD0C, 'M', u'شم'), - (0xFD0D, 'M', u'شر'), - (0xFD0E, 'M', u'سر'), - (0xFD0F, 'M', u'صر'), - (0xFD10, 'M', u'ضر'), - (0xFD11, 'M', u'طى'), - (0xFD12, 'M', u'طي'), - (0xFD13, 'M', u'عى'), - (0xFD14, 'M', u'عي'), - (0xFD15, 'M', u'غى'), - (0xFD16, 'M', u'غي'), - (0xFD17, 'M', u'سى'), - (0xFD18, 'M', u'سي'), - (0xFD19, 'M', u'شى'), - (0xFD1A, 'M', u'شي'), - (0xFD1B, 'M', u'حى'), - (0xFD1C, 'M', u'حي'), - (0xFD1D, 'M', u'جى'), - (0xFD1E, 'M', u'جي'), - (0xFD1F, 'M', u'خى'), - (0xFD20, 'M', u'خي'), - (0xFD21, 'M', u'صى'), - (0xFD22, 'M', u'صي'), - (0xFD23, 'M', u'ضى'), - (0xFD24, 'M', u'ضي'), - (0xFD25, 'M', u'شج'), - (0xFD26, 'M', u'شح'), - (0xFD27, 'M', u'شخ'), - (0xFD28, 'M', u'شم'), + (0xFCC4, 'M', 'كج'), + (0xFCC5, 'M', 'كح'), + (0xFCC6, 'M', 'كخ'), + (0xFCC7, 'M', 'كل'), + (0xFCC8, 'M', 'كم'), + (0xFCC9, 'M', 'لج'), + (0xFCCA, 'M', 'لح'), + (0xFCCB, 'M', 'لخ'), + (0xFCCC, 'M', 'لم'), + (0xFCCD, 'M', 'له'), + (0xFCCE, 'M', 'مج'), + (0xFCCF, 'M', 'مح'), + (0xFCD0, 'M', 'مخ'), + (0xFCD1, 'M', 'مم'), + (0xFCD2, 'M', 'نج'), + (0xFCD3, 'M', 'نح'), + (0xFCD4, 'M', 'نخ'), + (0xFCD5, 'M', 'نم'), + (0xFCD6, 'M', 'نه'), + (0xFCD7, 'M', 'هج'), + (0xFCD8, 'M', 'هم'), + (0xFCD9, 'M', 'هٰ'), + (0xFCDA, 'M', 'يج'), + (0xFCDB, 'M', 'يح'), + (0xFCDC, 'M', 'يخ'), + (0xFCDD, 'M', 'يم'), + (0xFCDE, 'M', 'يه'), + (0xFCDF, 'M', 'ئم'), + (0xFCE0, 'M', 'ئه'), + (0xFCE1, 'M', 'بم'), + (0xFCE2, 'M', 'به'), + (0xFCE3, 'M', 'تم'), + (0xFCE4, 'M', 'ته'), + (0xFCE5, 'M', 'ثم'), + (0xFCE6, 'M', 'ثه'), + (0xFCE7, 'M', 'سم'), + (0xFCE8, 'M', 'سه'), + (0xFCE9, 'M', 'شم'), + (0xFCEA, 'M', 'شه'), + (0xFCEB, 'M', 'كل'), + (0xFCEC, 'M', 'كم'), + (0xFCED, 'M', 'لم'), + (0xFCEE, 'M', 'نم'), + (0xFCEF, 'M', 'نه'), + (0xFCF0, 'M', 'يم'), + (0xFCF1, 'M', 'يه'), + (0xFCF2, 'M', 'ـَّ'), + (0xFCF3, 'M', 'ـُّ'), + (0xFCF4, 'M', 'ـِّ'), + (0xFCF5, 'M', 'طى'), + (0xFCF6, 'M', 'طي'), + (0xFCF7, 'M', 'عى'), + (0xFCF8, 'M', 'عي'), + (0xFCF9, 'M', 'غى'), + (0xFCFA, 'M', 'غي'), + (0xFCFB, 'M', 'سى'), + (0xFCFC, 'M', 'سي'), + (0xFCFD, 'M', 'شى'), + (0xFCFE, 'M', 'شي'), + (0xFCFF, 'M', 'حى'), + (0xFD00, 'M', 'حي'), + (0xFD01, 'M', 'جى'), + (0xFD02, 'M', 'جي'), + (0xFD03, 'M', 'خى'), + (0xFD04, 'M', 'خي'), + (0xFD05, 'M', 'صى'), + (0xFD06, 'M', 'صي'), + (0xFD07, 'M', 'ضى'), + (0xFD08, 'M', 'ضي'), + (0xFD09, 'M', 'شج'), + (0xFD0A, 'M', 'شح'), + (0xFD0B, 'M', 'شخ'), + (0xFD0C, 'M', 'شم'), + (0xFD0D, 'M', 'شر'), + (0xFD0E, 'M', 'سر'), + (0xFD0F, 'M', 'صر'), + (0xFD10, 'M', 'ضر'), + (0xFD11, 'M', 'طى'), + (0xFD12, 'M', 'طي'), + (0xFD13, 'M', 'عى'), + (0xFD14, 'M', 'عي'), + (0xFD15, 'M', 'غى'), + (0xFD16, 'M', 'غي'), + (0xFD17, 'M', 'سى'), + (0xFD18, 'M', 'سي'), + (0xFD19, 'M', 'شى'), + (0xFD1A, 'M', 'شي'), + (0xFD1B, 'M', 'حى'), + (0xFD1C, 'M', 'حي'), + (0xFD1D, 'M', 'جى'), + (0xFD1E, 'M', 'جي'), + (0xFD1F, 'M', 'خى'), + (0xFD20, 'M', 'خي'), + (0xFD21, 'M', 'صى'), + (0xFD22, 'M', 'صي'), + (0xFD23, 'M', 'ضى'), + (0xFD24, 'M', 'ضي'), + (0xFD25, 'M', 'شج'), + (0xFD26, 'M', 'شح'), + (0xFD27, 'M', 'شخ'), ] def _seg_48(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFD29, 'M', u'شر'), - (0xFD2A, 'M', u'سر'), - (0xFD2B, 'M', u'صر'), - (0xFD2C, 'M', u'ضر'), - (0xFD2D, 'M', u'شج'), - (0xFD2E, 'M', u'شح'), - (0xFD2F, 'M', u'شخ'), - (0xFD30, 'M', u'شم'), - (0xFD31, 'M', u'سه'), - (0xFD32, 'M', u'شه'), - (0xFD33, 'M', u'طم'), - (0xFD34, 'M', u'سج'), - (0xFD35, 'M', u'سح'), - (0xFD36, 'M', u'سخ'), - (0xFD37, 'M', u'شج'), - (0xFD38, 'M', u'شح'), - (0xFD39, 'M', u'شخ'), - (0xFD3A, 'M', u'طم'), - (0xFD3B, 'M', u'ظم'), - (0xFD3C, 'M', u'اً'), + (0xFD28, 'M', 'شم'), + (0xFD29, 'M', 'شر'), + (0xFD2A, 'M', 'سر'), + (0xFD2B, 'M', 'صر'), + (0xFD2C, 'M', 'ضر'), + (0xFD2D, 'M', 'شج'), + (0xFD2E, 'M', 'شح'), + (0xFD2F, 'M', 'شخ'), + (0xFD30, 'M', 'شم'), + (0xFD31, 'M', 'سه'), + (0xFD32, 'M', 'شه'), + (0xFD33, 'M', 'طم'), + (0xFD34, 'M', 'سج'), + (0xFD35, 'M', 'سح'), + (0xFD36, 'M', 'سخ'), + (0xFD37, 'M', 'شج'), + (0xFD38, 'M', 'شح'), + (0xFD39, 'M', 'شخ'), + (0xFD3A, 'M', 'طم'), + (0xFD3B, 'M', 'ظم'), + (0xFD3C, 'M', 'اً'), (0xFD3E, 'V'), (0xFD40, 'X'), - (0xFD50, 'M', u'تجم'), - (0xFD51, 'M', u'تحج'), - (0xFD53, 'M', u'تحم'), - (0xFD54, 'M', u'تخم'), - (0xFD55, 'M', u'تمج'), - (0xFD56, 'M', u'تمح'), - (0xFD57, 'M', u'تمخ'), - (0xFD58, 'M', u'جمح'), - (0xFD5A, 'M', u'حمي'), - (0xFD5B, 'M', u'حمى'), - (0xFD5C, 'M', u'سحج'), - (0xFD5D, 'M', u'سجح'), - (0xFD5E, 'M', u'سجى'), - (0xFD5F, 'M', u'سمح'), - (0xFD61, 'M', u'سمج'), - (0xFD62, 'M', u'سمم'), - (0xFD64, 'M', u'صحح'), - (0xFD66, 'M', u'صمم'), - (0xFD67, 'M', u'شحم'), - (0xFD69, 'M', u'شجي'), - (0xFD6A, 'M', u'شمخ'), - (0xFD6C, 'M', u'شمم'), - (0xFD6E, 'M', u'ضحى'), - (0xFD6F, 'M', u'ضخم'), - (0xFD71, 'M', u'طمح'), - (0xFD73, 'M', u'طمم'), - (0xFD74, 'M', u'طمي'), - (0xFD75, 'M', u'عجم'), - (0xFD76, 'M', u'عمم'), - (0xFD78, 'M', u'عمى'), - (0xFD79, 'M', u'غمم'), - (0xFD7A, 'M', u'غمي'), - (0xFD7B, 'M', u'غمى'), - (0xFD7C, 'M', u'فخم'), - (0xFD7E, 'M', u'قمح'), - (0xFD7F, 'M', u'قمم'), - (0xFD80, 'M', u'لحم'), - (0xFD81, 'M', u'لحي'), - (0xFD82, 'M', u'لحى'), - (0xFD83, 'M', u'لجج'), - (0xFD85, 'M', u'لخم'), - (0xFD87, 'M', u'لمح'), - (0xFD89, 'M', u'محج'), - (0xFD8A, 'M', u'محم'), - (0xFD8B, 'M', u'محي'), - (0xFD8C, 'M', u'مجح'), - (0xFD8D, 'M', u'مجم'), - (0xFD8E, 'M', u'مخج'), - (0xFD8F, 'M', u'مخم'), + (0xFD50, 'M', 'تجم'), + (0xFD51, 'M', 'تحج'), + (0xFD53, 'M', 'تحم'), + (0xFD54, 'M', 'تخم'), + (0xFD55, 'M', 'تمج'), + (0xFD56, 'M', 'تمح'), + (0xFD57, 'M', 'تمخ'), + (0xFD58, 'M', 'جمح'), + (0xFD5A, 'M', 'حمي'), + (0xFD5B, 'M', 'حمى'), + (0xFD5C, 'M', 'سحج'), + (0xFD5D, 'M', 'سجح'), + (0xFD5E, 'M', 'سجى'), + (0xFD5F, 'M', 'سمح'), + (0xFD61, 'M', 'سمج'), + (0xFD62, 'M', 'سمم'), + (0xFD64, 'M', 'صحح'), + (0xFD66, 'M', 'صمم'), + (0xFD67, 'M', 'شحم'), + (0xFD69, 'M', 'شجي'), + (0xFD6A, 'M', 'شمخ'), + (0xFD6C, 'M', 'شمم'), + (0xFD6E, 'M', 'ضحى'), + (0xFD6F, 'M', 'ضخم'), + (0xFD71, 'M', 'طمح'), + (0xFD73, 'M', 'طمم'), + (0xFD74, 'M', 'طمي'), + (0xFD75, 'M', 'عجم'), + (0xFD76, 'M', 'عمم'), + (0xFD78, 'M', 'عمى'), + (0xFD79, 'M', 'غمم'), + (0xFD7A, 'M', 'غمي'), + (0xFD7B, 'M', 'غمى'), + (0xFD7C, 'M', 'فخم'), + (0xFD7E, 'M', 'قمح'), + (0xFD7F, 'M', 'قمم'), + (0xFD80, 'M', 'لحم'), + (0xFD81, 'M', 'لحي'), + (0xFD82, 'M', 'لحى'), + (0xFD83, 'M', 'لجج'), + (0xFD85, 'M', 'لخم'), + (0xFD87, 'M', 'لمح'), + (0xFD89, 'M', 'محج'), + (0xFD8A, 'M', 'محم'), + (0xFD8B, 'M', 'محي'), + (0xFD8C, 'M', 'مجح'), + (0xFD8D, 'M', 'مجم'), + (0xFD8E, 'M', 'مخج'), + (0xFD8F, 'M', 'مخم'), (0xFD90, 'X'), - (0xFD92, 'M', u'مجخ'), - (0xFD93, 'M', u'همج'), - (0xFD94, 'M', u'همم'), - (0xFD95, 'M', u'نحم'), - (0xFD96, 'M', u'نحى'), - (0xFD97, 'M', u'نجم'), - (0xFD99, 'M', u'نجى'), - (0xFD9A, 'M', u'نمي'), - (0xFD9B, 'M', u'نمى'), - (0xFD9C, 'M', u'يمم'), - (0xFD9E, 'M', u'بخي'), - (0xFD9F, 'M', u'تجي'), - (0xFDA0, 'M', u'تجى'), - (0xFDA1, 'M', u'تخي'), - (0xFDA2, 'M', u'تخى'), - (0xFDA3, 'M', u'تمي'), - (0xFDA4, 'M', u'تمى'), - (0xFDA5, 'M', u'جمي'), - (0xFDA6, 'M', u'جحى'), - (0xFDA7, 'M', u'جمى'), - (0xFDA8, 'M', u'سخى'), - (0xFDA9, 'M', u'صحي'), - (0xFDAA, 'M', u'شحي'), - (0xFDAB, 'M', u'ضحي'), - (0xFDAC, 'M', u'لجي'), - (0xFDAD, 'M', u'لمي'), - (0xFDAE, 'M', u'يحي'), - (0xFDAF, 'M', u'يجي'), + (0xFD92, 'M', 'مجخ'), + (0xFD93, 'M', 'همج'), + (0xFD94, 'M', 'همم'), + (0xFD95, 'M', 'نحم'), + (0xFD96, 'M', 'نحى'), + (0xFD97, 'M', 'نجم'), + (0xFD99, 'M', 'نجى'), + (0xFD9A, 'M', 'نمي'), + (0xFD9B, 'M', 'نمى'), + (0xFD9C, 'M', 'يمم'), + (0xFD9E, 'M', 'بخي'), + (0xFD9F, 'M', 'تجي'), + (0xFDA0, 'M', 'تجى'), + (0xFDA1, 'M', 'تخي'), + (0xFDA2, 'M', 'تخى'), + (0xFDA3, 'M', 'تمي'), + (0xFDA4, 'M', 'تمى'), + (0xFDA5, 'M', 'جمي'), + (0xFDA6, 'M', 'جحى'), + (0xFDA7, 'M', 'جمى'), + (0xFDA8, 'M', 'سخى'), + (0xFDA9, 'M', 'صحي'), + (0xFDAA, 'M', 'شحي'), + (0xFDAB, 'M', 'ضحي'), + (0xFDAC, 'M', 'لجي'), + (0xFDAD, 'M', 'لمي'), + (0xFDAE, 'M', 'يحي'), ] def _seg_49(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFDB0, 'M', u'يمي'), - (0xFDB1, 'M', u'ممي'), - (0xFDB2, 'M', u'قمي'), - (0xFDB3, 'M', u'نحي'), - (0xFDB4, 'M', u'قمح'), - (0xFDB5, 'M', u'لحم'), - (0xFDB6, 'M', u'عمي'), - (0xFDB7, 'M', u'كمي'), - (0xFDB8, 'M', u'نجح'), - (0xFDB9, 'M', u'مخي'), - (0xFDBA, 'M', u'لجم'), - (0xFDBB, 'M', u'كمم'), - (0xFDBC, 'M', u'لجم'), - (0xFDBD, 'M', u'نجح'), - (0xFDBE, 'M', u'جحي'), - (0xFDBF, 'M', u'حجي'), - (0xFDC0, 'M', u'مجي'), - (0xFDC1, 'M', u'فمي'), - (0xFDC2, 'M', u'بحي'), - (0xFDC3, 'M', u'كمم'), - (0xFDC4, 'M', u'عجم'), - (0xFDC5, 'M', u'صمم'), - (0xFDC6, 'M', u'سخي'), - (0xFDC7, 'M', u'نجي'), + (0xFDAF, 'M', 'يجي'), + (0xFDB0, 'M', 'يمي'), + (0xFDB1, 'M', 'ممي'), + (0xFDB2, 'M', 'قمي'), + (0xFDB3, 'M', 'نحي'), + (0xFDB4, 'M', 'قمح'), + (0xFDB5, 'M', 'لحم'), + (0xFDB6, 'M', 'عمي'), + (0xFDB7, 'M', 'كمي'), + (0xFDB8, 'M', 'نجح'), + (0xFDB9, 'M', 'مخي'), + (0xFDBA, 'M', 'لجم'), + (0xFDBB, 'M', 'كمم'), + (0xFDBC, 'M', 'لجم'), + (0xFDBD, 'M', 'نجح'), + (0xFDBE, 'M', 'جحي'), + (0xFDBF, 'M', 'حجي'), + (0xFDC0, 'M', 'مجي'), + (0xFDC1, 'M', 'فمي'), + (0xFDC2, 'M', 'بحي'), + (0xFDC3, 'M', 'كمم'), + (0xFDC4, 'M', 'عجم'), + (0xFDC5, 'M', 'صمم'), + (0xFDC6, 'M', 'سخي'), + (0xFDC7, 'M', 'نجي'), (0xFDC8, 'X'), - (0xFDF0, 'M', u'صلے'), - (0xFDF1, 'M', u'قلے'), - (0xFDF2, 'M', u'الله'), - (0xFDF3, 'M', u'اكبر'), - (0xFDF4, 'M', u'محمد'), - (0xFDF5, 'M', u'صلعم'), - (0xFDF6, 'M', u'رسول'), - (0xFDF7, 'M', u'عليه'), - (0xFDF8, 'M', u'وسلم'), - (0xFDF9, 'M', u'صلى'), - (0xFDFA, '3', u'صلى الله عليه وسلم'), - (0xFDFB, '3', u'جل جلاله'), - (0xFDFC, 'M', u'ریال'), + (0xFDF0, 'M', 'صلے'), + (0xFDF1, 'M', 'قلے'), + (0xFDF2, 'M', 'الله'), + (0xFDF3, 'M', 'اكبر'), + (0xFDF4, 'M', 'محمد'), + (0xFDF5, 'M', 'صلعم'), + (0xFDF6, 'M', 'رسول'), + (0xFDF7, 'M', 'عليه'), + (0xFDF8, 'M', 'وسلم'), + (0xFDF9, 'M', 'صلى'), + (0xFDFA, '3', 'صلى الله عليه وسلم'), + (0xFDFB, '3', 'جل جلاله'), + (0xFDFC, 'M', 'ریال'), (0xFDFD, 'V'), (0xFDFE, 'X'), (0xFE00, 'I'), - (0xFE10, '3', u','), - (0xFE11, 'M', u'、'), + (0xFE10, '3', ','), + (0xFE11, 'M', '、'), (0xFE12, 'X'), - (0xFE13, '3', u':'), - (0xFE14, '3', u';'), - (0xFE15, '3', u'!'), - (0xFE16, '3', u'?'), - (0xFE17, 'M', u'〖'), - (0xFE18, 'M', u'〗'), + (0xFE13, '3', ':'), + (0xFE14, '3', ';'), + (0xFE15, '3', '!'), + (0xFE16, '3', '?'), + (0xFE17, 'M', '〖'), + (0xFE18, 'M', '〗'), (0xFE19, 'X'), (0xFE20, 'V'), (0xFE30, 'X'), - (0xFE31, 'M', u'—'), - (0xFE32, 'M', u'–'), - (0xFE33, '3', u'_'), - (0xFE35, '3', u'('), - (0xFE36, '3', u')'), - (0xFE37, '3', u'{'), - (0xFE38, '3', u'}'), - (0xFE39, 'M', u'〔'), - (0xFE3A, 'M', u'〕'), - (0xFE3B, 'M', u'【'), - (0xFE3C, 'M', u'】'), - (0xFE3D, 'M', u'《'), - (0xFE3E, 'M', u'》'), - (0xFE3F, 'M', u'〈'), - (0xFE40, 'M', u'〉'), - (0xFE41, 'M', u'「'), - (0xFE42, 'M', u'」'), - (0xFE43, 'M', u'『'), - (0xFE44, 'M', u'』'), + (0xFE31, 'M', '—'), + (0xFE32, 'M', '–'), + (0xFE33, '3', '_'), + (0xFE35, '3', '('), + (0xFE36, '3', ')'), + (0xFE37, '3', '{'), + (0xFE38, '3', '}'), + (0xFE39, 'M', '〔'), + (0xFE3A, 'M', '〕'), + (0xFE3B, 'M', '【'), + (0xFE3C, 'M', '】'), + (0xFE3D, 'M', '《'), + (0xFE3E, 'M', '》'), + (0xFE3F, 'M', '〈'), + (0xFE40, 'M', '〉'), + (0xFE41, 'M', '「'), + (0xFE42, 'M', '」'), + (0xFE43, 'M', '『'), + (0xFE44, 'M', '』'), (0xFE45, 'V'), - (0xFE47, '3', u'['), - (0xFE48, '3', u']'), - (0xFE49, '3', u' ̅'), - (0xFE4D, '3', u'_'), - (0xFE50, '3', u','), - (0xFE51, 'M', u'、'), + (0xFE47, '3', '['), + (0xFE48, '3', ']'), + (0xFE49, '3', ' ̅'), + (0xFE4D, '3', '_'), + (0xFE50, '3', ','), + (0xFE51, 'M', '、'), (0xFE52, 'X'), - (0xFE54, '3', u';'), - (0xFE55, '3', u':'), - (0xFE56, '3', u'?'), - (0xFE57, '3', u'!'), - (0xFE58, 'M', u'—'), - (0xFE59, '3', u'('), - (0xFE5A, '3', u')'), - (0xFE5B, '3', u'{'), - (0xFE5C, '3', u'}'), - (0xFE5D, 'M', u'〔'), - (0xFE5E, 'M', u'〕'), - (0xFE5F, '3', u'#'), - (0xFE60, '3', u'&'), - (0xFE61, '3', u'*'), - (0xFE62, '3', u'+'), - (0xFE63, 'M', u'-'), - (0xFE64, '3', u'<'), - (0xFE65, '3', u'>'), - (0xFE66, '3', u'='), - (0xFE67, 'X'), + (0xFE54, '3', ';'), + (0xFE55, '3', ':'), + (0xFE56, '3', '?'), + (0xFE57, '3', '!'), + (0xFE58, 'M', '—'), + (0xFE59, '3', '('), + (0xFE5A, '3', ')'), + (0xFE5B, '3', '{'), + (0xFE5C, '3', '}'), + (0xFE5D, 'M', '〔'), + (0xFE5E, 'M', '〕'), + (0xFE5F, '3', '#'), + (0xFE60, '3', '&'), + (0xFE61, '3', '*'), + (0xFE62, '3', '+'), + (0xFE63, 'M', '-'), + (0xFE64, '3', '<'), + (0xFE65, '3', '>'), + (0xFE66, '3', '='), ] def _seg_50(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFE68, '3', u'\\'), - (0xFE69, '3', u'$'), - (0xFE6A, '3', u'%'), - (0xFE6B, '3', u'@'), + (0xFE67, 'X'), + (0xFE68, '3', '\\'), + (0xFE69, '3', '$'), + (0xFE6A, '3', '%'), + (0xFE6B, '3', '@'), (0xFE6C, 'X'), - (0xFE70, '3', u' ً'), - (0xFE71, 'M', u'ـً'), - (0xFE72, '3', u' ٌ'), + (0xFE70, '3', ' ً'), + (0xFE71, 'M', 'ـً'), + (0xFE72, '3', ' ٌ'), (0xFE73, 'V'), - (0xFE74, '3', u' ٍ'), + (0xFE74, '3', ' ٍ'), (0xFE75, 'X'), - (0xFE76, '3', u' َ'), - (0xFE77, 'M', u'ـَ'), - (0xFE78, '3', u' ُ'), - (0xFE79, 'M', u'ـُ'), - (0xFE7A, '3', u' ِ'), - (0xFE7B, 'M', u'ـِ'), - (0xFE7C, '3', u' ّ'), - (0xFE7D, 'M', u'ـّ'), - (0xFE7E, '3', u' ْ'), - (0xFE7F, 'M', u'ـْ'), - (0xFE80, 'M', u'ء'), - (0xFE81, 'M', u'آ'), - (0xFE83, 'M', u'أ'), - (0xFE85, 'M', u'ؤ'), - (0xFE87, 'M', u'إ'), - (0xFE89, 'M', u'ئ'), - (0xFE8D, 'M', u'ا'), - (0xFE8F, 'M', u'ب'), - (0xFE93, 'M', u'ة'), - (0xFE95, 'M', u'ت'), - (0xFE99, 'M', u'ث'), - (0xFE9D, 'M', u'ج'), - (0xFEA1, 'M', u'ح'), - (0xFEA5, 'M', u'خ'), - (0xFEA9, 'M', u'د'), - (0xFEAB, 'M', u'ذ'), - (0xFEAD, 'M', u'ر'), - (0xFEAF, 'M', u'ز'), - (0xFEB1, 'M', u'س'), - (0xFEB5, 'M', u'ش'), - (0xFEB9, 'M', u'ص'), - (0xFEBD, 'M', u'ض'), - (0xFEC1, 'M', u'ط'), - (0xFEC5, 'M', u'ظ'), - (0xFEC9, 'M', u'ع'), - (0xFECD, 'M', u'غ'), - (0xFED1, 'M', u'ف'), - (0xFED5, 'M', u'ق'), - (0xFED9, 'M', u'ك'), - (0xFEDD, 'M', u'ل'), - (0xFEE1, 'M', u'م'), - (0xFEE5, 'M', u'ن'), - (0xFEE9, 'M', u'ه'), - (0xFEED, 'M', u'و'), - (0xFEEF, 'M', u'ى'), - (0xFEF1, 'M', u'ي'), - (0xFEF5, 'M', u'لآ'), - (0xFEF7, 'M', u'لأ'), - (0xFEF9, 'M', u'لإ'), - (0xFEFB, 'M', u'لا'), + (0xFE76, '3', ' َ'), + (0xFE77, 'M', 'ـَ'), + (0xFE78, '3', ' ُ'), + (0xFE79, 'M', 'ـُ'), + (0xFE7A, '3', ' ِ'), + (0xFE7B, 'M', 'ـِ'), + (0xFE7C, '3', ' ّ'), + (0xFE7D, 'M', 'ـّ'), + (0xFE7E, '3', ' ْ'), + (0xFE7F, 'M', 'ـْ'), + (0xFE80, 'M', 'ء'), + (0xFE81, 'M', 'آ'), + (0xFE83, 'M', 'أ'), + (0xFE85, 'M', 'ؤ'), + (0xFE87, 'M', 'إ'), + (0xFE89, 'M', 'ئ'), + (0xFE8D, 'M', 'ا'), + (0xFE8F, 'M', 'ب'), + (0xFE93, 'M', 'ة'), + (0xFE95, 'M', 'ت'), + (0xFE99, 'M', 'ث'), + (0xFE9D, 'M', 'ج'), + (0xFEA1, 'M', 'ح'), + (0xFEA5, 'M', 'خ'), + (0xFEA9, 'M', 'د'), + (0xFEAB, 'M', 'ذ'), + (0xFEAD, 'M', 'ر'), + (0xFEAF, 'M', 'ز'), + (0xFEB1, 'M', 'س'), + (0xFEB5, 'M', 'ش'), + (0xFEB9, 'M', 'ص'), + (0xFEBD, 'M', 'ض'), + (0xFEC1, 'M', 'ط'), + (0xFEC5, 'M', 'ظ'), + (0xFEC9, 'M', 'ع'), + (0xFECD, 'M', 'غ'), + (0xFED1, 'M', 'ف'), + (0xFED5, 'M', 'ق'), + (0xFED9, 'M', 'ك'), + (0xFEDD, 'M', 'ل'), + (0xFEE1, 'M', 'م'), + (0xFEE5, 'M', 'ن'), + (0xFEE9, 'M', 'ه'), + (0xFEED, 'M', 'و'), + (0xFEEF, 'M', 'ى'), + (0xFEF1, 'M', 'ي'), + (0xFEF5, 'M', 'لآ'), + (0xFEF7, 'M', 'لأ'), + (0xFEF9, 'M', 'لإ'), + (0xFEFB, 'M', 'لا'), (0xFEFD, 'X'), (0xFEFF, 'I'), (0xFF00, 'X'), - (0xFF01, '3', u'!'), - (0xFF02, '3', u'"'), - (0xFF03, '3', u'#'), - (0xFF04, '3', u'$'), - (0xFF05, '3', u'%'), - (0xFF06, '3', u'&'), - (0xFF07, '3', u'\''), - (0xFF08, '3', u'('), - (0xFF09, '3', u')'), - (0xFF0A, '3', u'*'), - (0xFF0B, '3', u'+'), - (0xFF0C, '3', u','), - (0xFF0D, 'M', u'-'), - (0xFF0E, 'M', u'.'), - (0xFF0F, '3', u'/'), - (0xFF10, 'M', u'0'), - (0xFF11, 'M', u'1'), - (0xFF12, 'M', u'2'), - (0xFF13, 'M', u'3'), - (0xFF14, 'M', u'4'), - (0xFF15, 'M', u'5'), - (0xFF16, 'M', u'6'), - (0xFF17, 'M', u'7'), - (0xFF18, 'M', u'8'), - (0xFF19, 'M', u'9'), - (0xFF1A, '3', u':'), - (0xFF1B, '3', u';'), - (0xFF1C, '3', u'<'), - (0xFF1D, '3', u'='), - (0xFF1E, '3', u'>'), - (0xFF1F, '3', u'?'), - (0xFF20, '3', u'@'), - (0xFF21, 'M', u'a'), - (0xFF22, 'M', u'b'), - (0xFF23, 'M', u'c'), - (0xFF24, 'M', u'd'), + (0xFF01, '3', '!'), + (0xFF02, '3', '"'), + (0xFF03, '3', '#'), + (0xFF04, '3', '$'), + (0xFF05, '3', '%'), + (0xFF06, '3', '&'), + (0xFF07, '3', '\''), + (0xFF08, '3', '('), + (0xFF09, '3', ')'), + (0xFF0A, '3', '*'), + (0xFF0B, '3', '+'), + (0xFF0C, '3', ','), + (0xFF0D, 'M', '-'), + (0xFF0E, 'M', '.'), + (0xFF0F, '3', '/'), + (0xFF10, 'M', '0'), + (0xFF11, 'M', '1'), + (0xFF12, 'M', '2'), + (0xFF13, 'M', '3'), + (0xFF14, 'M', '4'), + (0xFF15, 'M', '5'), + (0xFF16, 'M', '6'), + (0xFF17, 'M', '7'), + (0xFF18, 'M', '8'), + (0xFF19, 'M', '9'), + (0xFF1A, '3', ':'), + (0xFF1B, '3', ';'), + (0xFF1C, '3', '<'), + (0xFF1D, '3', '='), + (0xFF1E, '3', '>'), + (0xFF1F, '3', '?'), + (0xFF20, '3', '@'), + (0xFF21, 'M', 'a'), + (0xFF22, 'M', 'b'), + (0xFF23, 'M', 'c'), ] def _seg_51(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFF25, 'M', u'e'), - (0xFF26, 'M', u'f'), - (0xFF27, 'M', u'g'), - (0xFF28, 'M', u'h'), - (0xFF29, 'M', u'i'), - (0xFF2A, 'M', u'j'), - (0xFF2B, 'M', u'k'), - (0xFF2C, 'M', u'l'), - (0xFF2D, 'M', u'm'), - (0xFF2E, 'M', u'n'), - (0xFF2F, 'M', u'o'), - (0xFF30, 'M', u'p'), - (0xFF31, 'M', u'q'), - (0xFF32, 'M', u'r'), - (0xFF33, 'M', u's'), - (0xFF34, 'M', u't'), - (0xFF35, 'M', u'u'), - (0xFF36, 'M', u'v'), - (0xFF37, 'M', u'w'), - (0xFF38, 'M', u'x'), - (0xFF39, 'M', u'y'), - (0xFF3A, 'M', u'z'), - (0xFF3B, '3', u'['), - (0xFF3C, '3', u'\\'), - (0xFF3D, '3', u']'), - (0xFF3E, '3', u'^'), - (0xFF3F, '3', u'_'), - (0xFF40, '3', u'`'), - (0xFF41, 'M', u'a'), - (0xFF42, 'M', u'b'), - (0xFF43, 'M', u'c'), - (0xFF44, 'M', u'd'), - (0xFF45, 'M', u'e'), - (0xFF46, 'M', u'f'), - (0xFF47, 'M', u'g'), - (0xFF48, 'M', u'h'), - (0xFF49, 'M', u'i'), - (0xFF4A, 'M', u'j'), - (0xFF4B, 'M', u'k'), - (0xFF4C, 'M', u'l'), - (0xFF4D, 'M', u'm'), - (0xFF4E, 'M', u'n'), - (0xFF4F, 'M', u'o'), - (0xFF50, 'M', u'p'), - (0xFF51, 'M', u'q'), - (0xFF52, 'M', u'r'), - (0xFF53, 'M', u's'), - (0xFF54, 'M', u't'), - (0xFF55, 'M', u'u'), - (0xFF56, 'M', u'v'), - (0xFF57, 'M', u'w'), - (0xFF58, 'M', u'x'), - (0xFF59, 'M', u'y'), - (0xFF5A, 'M', u'z'), - (0xFF5B, '3', u'{'), - (0xFF5C, '3', u'|'), - (0xFF5D, '3', u'}'), - (0xFF5E, '3', u'~'), - (0xFF5F, 'M', u'⦅'), - (0xFF60, 'M', u'⦆'), - (0xFF61, 'M', u'.'), - (0xFF62, 'M', u'「'), - (0xFF63, 'M', u'」'), - (0xFF64, 'M', u'、'), - (0xFF65, 'M', u'・'), - (0xFF66, 'M', u'ヲ'), - (0xFF67, 'M', u'ァ'), - (0xFF68, 'M', u'ィ'), - (0xFF69, 'M', u'ゥ'), - (0xFF6A, 'M', u'ェ'), - (0xFF6B, 'M', u'ォ'), - (0xFF6C, 'M', u'ャ'), - (0xFF6D, 'M', u'ュ'), - (0xFF6E, 'M', u'ョ'), - (0xFF6F, 'M', u'ッ'), - (0xFF70, 'M', u'ー'), - (0xFF71, 'M', u'ア'), - (0xFF72, 'M', u'イ'), - (0xFF73, 'M', u'ウ'), - (0xFF74, 'M', u'エ'), - (0xFF75, 'M', u'オ'), - (0xFF76, 'M', u'カ'), - (0xFF77, 'M', u'キ'), - (0xFF78, 'M', u'ク'), - (0xFF79, 'M', u'ケ'), - (0xFF7A, 'M', u'コ'), - (0xFF7B, 'M', u'サ'), - (0xFF7C, 'M', u'シ'), - (0xFF7D, 'M', u'ス'), - (0xFF7E, 'M', u'セ'), - (0xFF7F, 'M', u'ソ'), - (0xFF80, 'M', u'タ'), - (0xFF81, 'M', u'チ'), - (0xFF82, 'M', u'ツ'), - (0xFF83, 'M', u'テ'), - (0xFF84, 'M', u'ト'), - (0xFF85, 'M', u'ナ'), - (0xFF86, 'M', u'ニ'), - (0xFF87, 'M', u'ヌ'), - (0xFF88, 'M', u'ネ'), + (0xFF24, 'M', 'd'), + (0xFF25, 'M', 'e'), + (0xFF26, 'M', 'f'), + (0xFF27, 'M', 'g'), + (0xFF28, 'M', 'h'), + (0xFF29, 'M', 'i'), + (0xFF2A, 'M', 'j'), + (0xFF2B, 'M', 'k'), + (0xFF2C, 'M', 'l'), + (0xFF2D, 'M', 'm'), + (0xFF2E, 'M', 'n'), + (0xFF2F, 'M', 'o'), + (0xFF30, 'M', 'p'), + (0xFF31, 'M', 'q'), + (0xFF32, 'M', 'r'), + (0xFF33, 'M', 's'), + (0xFF34, 'M', 't'), + (0xFF35, 'M', 'u'), + (0xFF36, 'M', 'v'), + (0xFF37, 'M', 'w'), + (0xFF38, 'M', 'x'), + (0xFF39, 'M', 'y'), + (0xFF3A, 'M', 'z'), + (0xFF3B, '3', '['), + (0xFF3C, '3', '\\'), + (0xFF3D, '3', ']'), + (0xFF3E, '3', '^'), + (0xFF3F, '3', '_'), + (0xFF40, '3', '`'), + (0xFF41, 'M', 'a'), + (0xFF42, 'M', 'b'), + (0xFF43, 'M', 'c'), + (0xFF44, 'M', 'd'), + (0xFF45, 'M', 'e'), + (0xFF46, 'M', 'f'), + (0xFF47, 'M', 'g'), + (0xFF48, 'M', 'h'), + (0xFF49, 'M', 'i'), + (0xFF4A, 'M', 'j'), + (0xFF4B, 'M', 'k'), + (0xFF4C, 'M', 'l'), + (0xFF4D, 'M', 'm'), + (0xFF4E, 'M', 'n'), + (0xFF4F, 'M', 'o'), + (0xFF50, 'M', 'p'), + (0xFF51, 'M', 'q'), + (0xFF52, 'M', 'r'), + (0xFF53, 'M', 's'), + (0xFF54, 'M', 't'), + (0xFF55, 'M', 'u'), + (0xFF56, 'M', 'v'), + (0xFF57, 'M', 'w'), + (0xFF58, 'M', 'x'), + (0xFF59, 'M', 'y'), + (0xFF5A, 'M', 'z'), + (0xFF5B, '3', '{'), + (0xFF5C, '3', '|'), + (0xFF5D, '3', '}'), + (0xFF5E, '3', '~'), + (0xFF5F, 'M', '⦅'), + (0xFF60, 'M', '⦆'), + (0xFF61, 'M', '.'), + (0xFF62, 'M', '「'), + (0xFF63, 'M', '」'), + (0xFF64, 'M', '、'), + (0xFF65, 'M', '・'), + (0xFF66, 'M', 'ヲ'), + (0xFF67, 'M', 'ァ'), + (0xFF68, 'M', 'ィ'), + (0xFF69, 'M', 'ゥ'), + (0xFF6A, 'M', 'ェ'), + (0xFF6B, 'M', 'ォ'), + (0xFF6C, 'M', 'ャ'), + (0xFF6D, 'M', 'ュ'), + (0xFF6E, 'M', 'ョ'), + (0xFF6F, 'M', 'ッ'), + (0xFF70, 'M', 'ー'), + (0xFF71, 'M', 'ア'), + (0xFF72, 'M', 'イ'), + (0xFF73, 'M', 'ウ'), + (0xFF74, 'M', 'エ'), + (0xFF75, 'M', 'オ'), + (0xFF76, 'M', 'カ'), + (0xFF77, 'M', 'キ'), + (0xFF78, 'M', 'ク'), + (0xFF79, 'M', 'ケ'), + (0xFF7A, 'M', 'コ'), + (0xFF7B, 'M', 'サ'), + (0xFF7C, 'M', 'シ'), + (0xFF7D, 'M', 'ス'), + (0xFF7E, 'M', 'セ'), + (0xFF7F, 'M', 'ソ'), + (0xFF80, 'M', 'タ'), + (0xFF81, 'M', 'チ'), + (0xFF82, 'M', 'ツ'), + (0xFF83, 'M', 'テ'), + (0xFF84, 'M', 'ト'), + (0xFF85, 'M', 'ナ'), + (0xFF86, 'M', 'ニ'), + (0xFF87, 'M', 'ヌ'), ] def _seg_52(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0xFF89, 'M', u'ノ'), - (0xFF8A, 'M', u'ハ'), - (0xFF8B, 'M', u'ヒ'), - (0xFF8C, 'M', u'フ'), - (0xFF8D, 'M', u'ヘ'), - (0xFF8E, 'M', u'ホ'), - (0xFF8F, 'M', u'マ'), - (0xFF90, 'M', u'ミ'), - (0xFF91, 'M', u'ム'), - (0xFF92, 'M', u'メ'), - (0xFF93, 'M', u'モ'), - (0xFF94, 'M', u'ヤ'), - (0xFF95, 'M', u'ユ'), - (0xFF96, 'M', u'ヨ'), - (0xFF97, 'M', u'ラ'), - (0xFF98, 'M', u'リ'), - (0xFF99, 'M', u'ル'), - (0xFF9A, 'M', u'レ'), - (0xFF9B, 'M', u'ロ'), - (0xFF9C, 'M', u'ワ'), - (0xFF9D, 'M', u'ン'), - (0xFF9E, 'M', u'゙'), - (0xFF9F, 'M', u'゚'), + (0xFF88, 'M', 'ネ'), + (0xFF89, 'M', 'ノ'), + (0xFF8A, 'M', 'ハ'), + (0xFF8B, 'M', 'ヒ'), + (0xFF8C, 'M', 'フ'), + (0xFF8D, 'M', 'ヘ'), + (0xFF8E, 'M', 'ホ'), + (0xFF8F, 'M', 'マ'), + (0xFF90, 'M', 'ミ'), + (0xFF91, 'M', 'ム'), + (0xFF92, 'M', 'メ'), + (0xFF93, 'M', 'モ'), + (0xFF94, 'M', 'ヤ'), + (0xFF95, 'M', 'ユ'), + (0xFF96, 'M', 'ヨ'), + (0xFF97, 'M', 'ラ'), + (0xFF98, 'M', 'リ'), + (0xFF99, 'M', 'ル'), + (0xFF9A, 'M', 'レ'), + (0xFF9B, 'M', 'ロ'), + (0xFF9C, 'M', 'ワ'), + (0xFF9D, 'M', 'ン'), + (0xFF9E, 'M', '゙'), + (0xFF9F, 'M', '゚'), (0xFFA0, 'X'), - (0xFFA1, 'M', u'ᄀ'), - (0xFFA2, 'M', u'ᄁ'), - (0xFFA3, 'M', u'ᆪ'), - (0xFFA4, 'M', u'ᄂ'), - (0xFFA5, 'M', u'ᆬ'), - (0xFFA6, 'M', u'ᆭ'), - (0xFFA7, 'M', u'ᄃ'), - (0xFFA8, 'M', u'ᄄ'), - (0xFFA9, 'M', u'ᄅ'), - (0xFFAA, 'M', u'ᆰ'), - (0xFFAB, 'M', u'ᆱ'), - (0xFFAC, 'M', u'ᆲ'), - (0xFFAD, 'M', u'ᆳ'), - (0xFFAE, 'M', u'ᆴ'), - (0xFFAF, 'M', u'ᆵ'), - (0xFFB0, 'M', u'ᄚ'), - (0xFFB1, 'M', u'ᄆ'), - (0xFFB2, 'M', u'ᄇ'), - (0xFFB3, 'M', u'ᄈ'), - (0xFFB4, 'M', u'ᄡ'), - (0xFFB5, 'M', u'ᄉ'), - (0xFFB6, 'M', u'ᄊ'), - (0xFFB7, 'M', u'ᄋ'), - (0xFFB8, 'M', u'ᄌ'), - (0xFFB9, 'M', u'ᄍ'), - (0xFFBA, 'M', u'ᄎ'), - (0xFFBB, 'M', u'ᄏ'), - (0xFFBC, 'M', u'ᄐ'), - (0xFFBD, 'M', u'ᄑ'), - (0xFFBE, 'M', u'ᄒ'), + (0xFFA1, 'M', 'ᄀ'), + (0xFFA2, 'M', 'ᄁ'), + (0xFFA3, 'M', 'ᆪ'), + (0xFFA4, 'M', 'ᄂ'), + (0xFFA5, 'M', 'ᆬ'), + (0xFFA6, 'M', 'ᆭ'), + (0xFFA7, 'M', 'ᄃ'), + (0xFFA8, 'M', 'ᄄ'), + (0xFFA9, 'M', 'ᄅ'), + (0xFFAA, 'M', 'ᆰ'), + (0xFFAB, 'M', 'ᆱ'), + (0xFFAC, 'M', 'ᆲ'), + (0xFFAD, 'M', 'ᆳ'), + (0xFFAE, 'M', 'ᆴ'), + (0xFFAF, 'M', 'ᆵ'), + (0xFFB0, 'M', 'ᄚ'), + (0xFFB1, 'M', 'ᄆ'), + (0xFFB2, 'M', 'ᄇ'), + (0xFFB3, 'M', 'ᄈ'), + (0xFFB4, 'M', 'ᄡ'), + (0xFFB5, 'M', 'ᄉ'), + (0xFFB6, 'M', 'ᄊ'), + (0xFFB7, 'M', 'ᄋ'), + (0xFFB8, 'M', 'ᄌ'), + (0xFFB9, 'M', 'ᄍ'), + (0xFFBA, 'M', 'ᄎ'), + (0xFFBB, 'M', 'ᄏ'), + (0xFFBC, 'M', 'ᄐ'), + (0xFFBD, 'M', 'ᄑ'), + (0xFFBE, 'M', 'ᄒ'), (0xFFBF, 'X'), - (0xFFC2, 'M', u'ᅡ'), - (0xFFC3, 'M', u'ᅢ'), - (0xFFC4, 'M', u'ᅣ'), - (0xFFC5, 'M', u'ᅤ'), - (0xFFC6, 'M', u'ᅥ'), - (0xFFC7, 'M', u'ᅦ'), + (0xFFC2, 'M', 'ᅡ'), + (0xFFC3, 'M', 'ᅢ'), + (0xFFC4, 'M', 'ᅣ'), + (0xFFC5, 'M', 'ᅤ'), + (0xFFC6, 'M', 'ᅥ'), + (0xFFC7, 'M', 'ᅦ'), (0xFFC8, 'X'), - (0xFFCA, 'M', u'ᅧ'), - (0xFFCB, 'M', u'ᅨ'), - (0xFFCC, 'M', u'ᅩ'), - (0xFFCD, 'M', u'ᅪ'), - (0xFFCE, 'M', u'ᅫ'), - (0xFFCF, 'M', u'ᅬ'), + (0xFFCA, 'M', 'ᅧ'), + (0xFFCB, 'M', 'ᅨ'), + (0xFFCC, 'M', 'ᅩ'), + (0xFFCD, 'M', 'ᅪ'), + (0xFFCE, 'M', 'ᅫ'), + (0xFFCF, 'M', 'ᅬ'), (0xFFD0, 'X'), - (0xFFD2, 'M', u'ᅭ'), - (0xFFD3, 'M', u'ᅮ'), - (0xFFD4, 'M', u'ᅯ'), - (0xFFD5, 'M', u'ᅰ'), - (0xFFD6, 'M', u'ᅱ'), - (0xFFD7, 'M', u'ᅲ'), + (0xFFD2, 'M', 'ᅭ'), + (0xFFD3, 'M', 'ᅮ'), + (0xFFD4, 'M', 'ᅯ'), + (0xFFD5, 'M', 'ᅰ'), + (0xFFD6, 'M', 'ᅱ'), + (0xFFD7, 'M', 'ᅲ'), (0xFFD8, 'X'), - (0xFFDA, 'M', u'ᅳ'), - (0xFFDB, 'M', u'ᅴ'), - (0xFFDC, 'M', u'ᅵ'), + (0xFFDA, 'M', 'ᅳ'), + (0xFFDB, 'M', 'ᅴ'), + (0xFFDC, 'M', 'ᅵ'), (0xFFDD, 'X'), - (0xFFE0, 'M', u'¢'), - (0xFFE1, 'M', u'£'), - (0xFFE2, 'M', u'¬'), - (0xFFE3, '3', u' ̄'), - (0xFFE4, 'M', u'¦'), - (0xFFE5, 'M', u'¥'), - (0xFFE6, 'M', u'₩'), + (0xFFE0, 'M', '¢'), + (0xFFE1, 'M', '£'), + (0xFFE2, 'M', '¬'), + (0xFFE3, '3', ' ̄'), + (0xFFE4, 'M', '¦'), + (0xFFE5, 'M', '¥'), + (0xFFE6, 'M', '₩'), (0xFFE7, 'X'), - (0xFFE8, 'M', u'│'), - (0xFFE9, 'M', u'←'), - (0xFFEA, 'M', u'↑'), - (0xFFEB, 'M', u'→'), - (0xFFEC, 'M', u'↓'), - (0xFFED, 'M', u'■'), - (0xFFEE, 'M', u'○'), + (0xFFE8, 'M', '│'), + (0xFFE9, 'M', '←'), + (0xFFEA, 'M', '↑'), + (0xFFEB, 'M', '→'), + (0xFFEC, 'M', '↓'), + (0xFFED, 'M', '■'), + (0xFFEE, 'M', '○'), (0xFFEF, 'X'), (0x10000, 'V'), (0x1000C, 'X'), (0x1000D, 'V'), - (0x10027, 'X'), ] def _seg_53(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ + (0x10027, 'X'), (0x10028, 'V'), (0x1003B, 'X'), (0x1003C, 'V'), @@ -5536,7 +5592,7 @@ def _seg_53(): (0x10137, 'V'), (0x1018F, 'X'), (0x10190, 'V'), - (0x1019C, 'X'), + (0x1019D, 'X'), (0x101A0, 'V'), (0x101A1, 'X'), (0x101D0, 'V'), @@ -5559,90 +5615,91 @@ def _seg_53(): (0x103C4, 'X'), (0x103C8, 'V'), (0x103D6, 'X'), - (0x10400, 'M', u'𐐨'), - (0x10401, 'M', u'𐐩'), - (0x10402, 'M', u'𐐪'), - (0x10403, 'M', u'𐐫'), - (0x10404, 'M', u'𐐬'), - (0x10405, 'M', u'𐐭'), - (0x10406, 'M', u'𐐮'), - (0x10407, 'M', u'𐐯'), - (0x10408, 'M', u'𐐰'), - (0x10409, 'M', u'𐐱'), - (0x1040A, 'M', u'𐐲'), - (0x1040B, 'M', u'𐐳'), - (0x1040C, 'M', u'𐐴'), - (0x1040D, 'M', u'𐐵'), - (0x1040E, 'M', u'𐐶'), - (0x1040F, 'M', u'𐐷'), - (0x10410, 'M', u'𐐸'), - (0x10411, 'M', u'𐐹'), - (0x10412, 'M', u'𐐺'), - (0x10413, 'M', u'𐐻'), - (0x10414, 'M', u'𐐼'), - (0x10415, 'M', u'𐐽'), - (0x10416, 'M', u'𐐾'), - (0x10417, 'M', u'𐐿'), - (0x10418, 'M', u'𐑀'), - (0x10419, 'M', u'𐑁'), - (0x1041A, 'M', u'𐑂'), - (0x1041B, 'M', u'𐑃'), - (0x1041C, 'M', u'𐑄'), - (0x1041D, 'M', u'𐑅'), - (0x1041E, 'M', u'𐑆'), - (0x1041F, 'M', u'𐑇'), - (0x10420, 'M', u'𐑈'), - (0x10421, 'M', u'𐑉'), - (0x10422, 'M', u'𐑊'), - (0x10423, 'M', u'𐑋'), - (0x10424, 'M', u'𐑌'), - (0x10425, 'M', u'𐑍'), - (0x10426, 'M', u'𐑎'), - (0x10427, 'M', u'𐑏'), + (0x10400, 'M', '𐐨'), + (0x10401, 'M', '𐐩'), + (0x10402, 'M', '𐐪'), + (0x10403, 'M', '𐐫'), + (0x10404, 'M', '𐐬'), + (0x10405, 'M', '𐐭'), + (0x10406, 'M', '𐐮'), + (0x10407, 'M', '𐐯'), + (0x10408, 'M', '𐐰'), + (0x10409, 'M', '𐐱'), + (0x1040A, 'M', '𐐲'), + (0x1040B, 'M', '𐐳'), + (0x1040C, 'M', '𐐴'), + (0x1040D, 'M', '𐐵'), + (0x1040E, 'M', '𐐶'), + (0x1040F, 'M', '𐐷'), + (0x10410, 'M', '𐐸'), + (0x10411, 'M', '𐐹'), + (0x10412, 'M', '𐐺'), + (0x10413, 'M', '𐐻'), + (0x10414, 'M', '𐐼'), + (0x10415, 'M', '𐐽'), + (0x10416, 'M', '𐐾'), + (0x10417, 'M', '𐐿'), + (0x10418, 'M', '𐑀'), + (0x10419, 'M', '𐑁'), + (0x1041A, 'M', '𐑂'), + (0x1041B, 'M', '𐑃'), + (0x1041C, 'M', '𐑄'), + (0x1041D, 'M', '𐑅'), + (0x1041E, 'M', '𐑆'), + (0x1041F, 'M', '𐑇'), + (0x10420, 'M', '𐑈'), + (0x10421, 'M', '𐑉'), + (0x10422, 'M', '𐑊'), + (0x10423, 'M', '𐑋'), + (0x10424, 'M', '𐑌'), + (0x10425, 'M', '𐑍'), + (0x10426, 'M', '𐑎'), + (0x10427, 'M', '𐑏'), (0x10428, 'V'), (0x1049E, 'X'), (0x104A0, 'V'), (0x104AA, 'X'), - (0x104B0, 'M', u'𐓘'), - (0x104B1, 'M', u'𐓙'), - (0x104B2, 'M', u'𐓚'), - (0x104B3, 'M', u'𐓛'), - (0x104B4, 'M', u'𐓜'), - (0x104B5, 'M', u'𐓝'), - (0x104B6, 'M', u'𐓞'), - (0x104B7, 'M', u'𐓟'), - (0x104B8, 'M', u'𐓠'), - (0x104B9, 'M', u'𐓡'), - (0x104BA, 'M', u'𐓢'), - (0x104BB, 'M', u'𐓣'), - (0x104BC, 'M', u'𐓤'), - (0x104BD, 'M', u'𐓥'), - (0x104BE, 'M', u'𐓦'), - (0x104BF, 'M', u'𐓧'), + (0x104B0, 'M', '𐓘'), + (0x104B1, 'M', '𐓙'), + (0x104B2, 'M', '𐓚'), + (0x104B3, 'M', '𐓛'), + (0x104B4, 'M', '𐓜'), + (0x104B5, 'M', '𐓝'), + (0x104B6, 'M', '𐓞'), + (0x104B7, 'M', '𐓟'), + (0x104B8, 'M', '𐓠'), + (0x104B9, 'M', '𐓡'), + (0x104BA, 'M', '𐓢'), + (0x104BB, 'M', '𐓣'), + (0x104BC, 'M', '𐓤'), + (0x104BD, 'M', '𐓥'), + (0x104BE, 'M', '𐓦'), ] def _seg_54(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x104C0, 'M', u'𐓨'), - (0x104C1, 'M', u'𐓩'), - (0x104C2, 'M', u'𐓪'), - (0x104C3, 'M', u'𐓫'), - (0x104C4, 'M', u'𐓬'), - (0x104C5, 'M', u'𐓭'), - (0x104C6, 'M', u'𐓮'), - (0x104C7, 'M', u'𐓯'), - (0x104C8, 'M', u'𐓰'), - (0x104C9, 'M', u'𐓱'), - (0x104CA, 'M', u'𐓲'), - (0x104CB, 'M', u'𐓳'), - (0x104CC, 'M', u'𐓴'), - (0x104CD, 'M', u'𐓵'), - (0x104CE, 'M', u'𐓶'), - (0x104CF, 'M', u'𐓷'), - (0x104D0, 'M', u'𐓸'), - (0x104D1, 'M', u'𐓹'), - (0x104D2, 'M', u'𐓺'), - (0x104D3, 'M', u'𐓻'), + (0x104BF, 'M', '𐓧'), + (0x104C0, 'M', '𐓨'), + (0x104C1, 'M', '𐓩'), + (0x104C2, 'M', '𐓪'), + (0x104C3, 'M', '𐓫'), + (0x104C4, 'M', '𐓬'), + (0x104C5, 'M', '𐓭'), + (0x104C6, 'M', '𐓮'), + (0x104C7, 'M', '𐓯'), + (0x104C8, 'M', '𐓰'), + (0x104C9, 'M', '𐓱'), + (0x104CA, 'M', '𐓲'), + (0x104CB, 'M', '𐓳'), + (0x104CC, 'M', '𐓴'), + (0x104CD, 'M', '𐓵'), + (0x104CE, 'M', '𐓶'), + (0x104CF, 'M', '𐓷'), + (0x104D0, 'M', '𐓸'), + (0x104D1, 'M', '𐓹'), + (0x104D2, 'M', '𐓺'), + (0x104D3, 'M', '𐓻'), (0x104D4, 'X'), (0x104D8, 'V'), (0x104FC, 'X'), @@ -5722,63 +5779,64 @@ def _seg_54(): (0x10B9D, 'X'), (0x10BA9, 'V'), (0x10BB0, 'X'), - (0x10C00, 'V'), ] def _seg_55(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ + (0x10C00, 'V'), (0x10C49, 'X'), - (0x10C80, 'M', u'𐳀'), - (0x10C81, 'M', u'𐳁'), - (0x10C82, 'M', u'𐳂'), - (0x10C83, 'M', u'𐳃'), - (0x10C84, 'M', u'𐳄'), - (0x10C85, 'M', u'𐳅'), - (0x10C86, 'M', u'𐳆'), - (0x10C87, 'M', u'𐳇'), - (0x10C88, 'M', u'𐳈'), - (0x10C89, 'M', u'𐳉'), - (0x10C8A, 'M', u'𐳊'), - (0x10C8B, 'M', u'𐳋'), - (0x10C8C, 'M', u'𐳌'), - (0x10C8D, 'M', u'𐳍'), - (0x10C8E, 'M', u'𐳎'), - (0x10C8F, 'M', u'𐳏'), - (0x10C90, 'M', u'𐳐'), - (0x10C91, 'M', u'𐳑'), - (0x10C92, 'M', u'𐳒'), - (0x10C93, 'M', u'𐳓'), - (0x10C94, 'M', u'𐳔'), - (0x10C95, 'M', u'𐳕'), - (0x10C96, 'M', u'𐳖'), - (0x10C97, 'M', u'𐳗'), - (0x10C98, 'M', u'𐳘'), - (0x10C99, 'M', u'𐳙'), - (0x10C9A, 'M', u'𐳚'), - (0x10C9B, 'M', u'𐳛'), - (0x10C9C, 'M', u'𐳜'), - (0x10C9D, 'M', u'𐳝'), - (0x10C9E, 'M', u'𐳞'), - (0x10C9F, 'M', u'𐳟'), - (0x10CA0, 'M', u'𐳠'), - (0x10CA1, 'M', u'𐳡'), - (0x10CA2, 'M', u'𐳢'), - (0x10CA3, 'M', u'𐳣'), - (0x10CA4, 'M', u'𐳤'), - (0x10CA5, 'M', u'𐳥'), - (0x10CA6, 'M', u'𐳦'), - (0x10CA7, 'M', u'𐳧'), - (0x10CA8, 'M', u'𐳨'), - (0x10CA9, 'M', u'𐳩'), - (0x10CAA, 'M', u'𐳪'), - (0x10CAB, 'M', u'𐳫'), - (0x10CAC, 'M', u'𐳬'), - (0x10CAD, 'M', u'𐳭'), - (0x10CAE, 'M', u'𐳮'), - (0x10CAF, 'M', u'𐳯'), - (0x10CB0, 'M', u'𐳰'), - (0x10CB1, 'M', u'𐳱'), - (0x10CB2, 'M', u'𐳲'), + (0x10C80, 'M', '𐳀'), + (0x10C81, 'M', '𐳁'), + (0x10C82, 'M', '𐳂'), + (0x10C83, 'M', '𐳃'), + (0x10C84, 'M', '𐳄'), + (0x10C85, 'M', '𐳅'), + (0x10C86, 'M', '𐳆'), + (0x10C87, 'M', '𐳇'), + (0x10C88, 'M', '𐳈'), + (0x10C89, 'M', '𐳉'), + (0x10C8A, 'M', '𐳊'), + (0x10C8B, 'M', '𐳋'), + (0x10C8C, 'M', '𐳌'), + (0x10C8D, 'M', '𐳍'), + (0x10C8E, 'M', '𐳎'), + (0x10C8F, 'M', '𐳏'), + (0x10C90, 'M', '𐳐'), + (0x10C91, 'M', '𐳑'), + (0x10C92, 'M', '𐳒'), + (0x10C93, 'M', '𐳓'), + (0x10C94, 'M', '𐳔'), + (0x10C95, 'M', '𐳕'), + (0x10C96, 'M', '𐳖'), + (0x10C97, 'M', '𐳗'), + (0x10C98, 'M', '𐳘'), + (0x10C99, 'M', '𐳙'), + (0x10C9A, 'M', '𐳚'), + (0x10C9B, 'M', '𐳛'), + (0x10C9C, 'M', '𐳜'), + (0x10C9D, 'M', '𐳝'), + (0x10C9E, 'M', '𐳞'), + (0x10C9F, 'M', '𐳟'), + (0x10CA0, 'M', '𐳠'), + (0x10CA1, 'M', '𐳡'), + (0x10CA2, 'M', '𐳢'), + (0x10CA3, 'M', '𐳣'), + (0x10CA4, 'M', '𐳤'), + (0x10CA5, 'M', '𐳥'), + (0x10CA6, 'M', '𐳦'), + (0x10CA7, 'M', '𐳧'), + (0x10CA8, 'M', '𐳨'), + (0x10CA9, 'M', '𐳩'), + (0x10CAA, 'M', '𐳪'), + (0x10CAB, 'M', '𐳫'), + (0x10CAC, 'M', '𐳬'), + (0x10CAD, 'M', '𐳭'), + (0x10CAE, 'M', '𐳮'), + (0x10CAF, 'M', '𐳯'), + (0x10CB0, 'M', '𐳰'), + (0x10CB1, 'M', '𐳱'), + (0x10CB2, 'M', '𐳲'), (0x10CB3, 'X'), (0x10CC0, 'V'), (0x10CF3, 'X'), @@ -5788,10 +5846,18 @@ def _seg_55(): (0x10D3A, 'X'), (0x10E60, 'V'), (0x10E7F, 'X'), + (0x10E80, 'V'), + (0x10EAA, 'X'), + (0x10EAB, 'V'), + (0x10EAE, 'X'), + (0x10EB0, 'V'), + (0x10EB2, 'X'), (0x10F00, 'V'), (0x10F28, 'X'), (0x10F30, 'V'), (0x10F5A, 'X'), + (0x10FB0, 'V'), + (0x10FCC, 'X'), (0x10FE0, 'V'), (0x10FF7, 'X'), (0x11000, 'V'), @@ -5809,17 +5875,20 @@ def _seg_55(): (0x11100, 'V'), (0x11135, 'X'), (0x11136, 'V'), - (0x11147, 'X'), + (0x11148, 'X'), (0x11150, 'V'), (0x11177, 'X'), (0x11180, 'V'), - (0x111CE, 'X'), - (0x111D0, 'V'), (0x111E0, 'X'), (0x111E1, 'V'), (0x111F5, 'X'), (0x11200, 'V'), (0x11212, 'X'), + ] + +def _seg_56(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] + return [ (0x11213, 'V'), (0x1123F, 'X'), (0x11280, 'V'), @@ -5827,10 +5896,6 @@ def _seg_55(): (0x11288, 'V'), (0x11289, 'X'), (0x1128A, 'V'), - ] - -def _seg_56(): - return [ (0x1128E, 'X'), (0x1128F, 'V'), (0x1129E, 'X'), @@ -5871,11 +5936,9 @@ def _seg_56(): (0x11370, 'V'), (0x11375, 'X'), (0x11400, 'V'), - (0x1145A, 'X'), - (0x1145B, 'V'), (0x1145C, 'X'), (0x1145D, 'V'), - (0x11460, 'X'), + (0x11462, 'X'), (0x11480, 'V'), (0x114C8, 'X'), (0x114D0, 'V'), @@ -5902,46 +5965,61 @@ def _seg_56(): (0x11740, 'X'), (0x11800, 'V'), (0x1183C, 'X'), - (0x118A0, 'M', u'𑣀'), - (0x118A1, 'M', u'𑣁'), - (0x118A2, 'M', u'𑣂'), - (0x118A3, 'M', u'𑣃'), - (0x118A4, 'M', u'𑣄'), - (0x118A5, 'M', u'𑣅'), - (0x118A6, 'M', u'𑣆'), - (0x118A7, 'M', u'𑣇'), - (0x118A8, 'M', u'𑣈'), - (0x118A9, 'M', u'𑣉'), - (0x118AA, 'M', u'𑣊'), - (0x118AB, 'M', u'𑣋'), - (0x118AC, 'M', u'𑣌'), - (0x118AD, 'M', u'𑣍'), - (0x118AE, 'M', u'𑣎'), - (0x118AF, 'M', u'𑣏'), - (0x118B0, 'M', u'𑣐'), - (0x118B1, 'M', u'𑣑'), - (0x118B2, 'M', u'𑣒'), - (0x118B3, 'M', u'𑣓'), - (0x118B4, 'M', u'𑣔'), - (0x118B5, 'M', u'𑣕'), - (0x118B6, 'M', u'𑣖'), - (0x118B7, 'M', u'𑣗'), - (0x118B8, 'M', u'𑣘'), - (0x118B9, 'M', u'𑣙'), - (0x118BA, 'M', u'𑣚'), - (0x118BB, 'M', u'𑣛'), - (0x118BC, 'M', u'𑣜'), + (0x118A0, 'M', '𑣀'), + (0x118A1, 'M', '𑣁'), + (0x118A2, 'M', '𑣂'), + (0x118A3, 'M', '𑣃'), + (0x118A4, 'M', '𑣄'), + (0x118A5, 'M', '𑣅'), + (0x118A6, 'M', '𑣆'), + (0x118A7, 'M', '𑣇'), + (0x118A8, 'M', '𑣈'), + (0x118A9, 'M', '𑣉'), + (0x118AA, 'M', '𑣊'), + (0x118AB, 'M', '𑣋'), + (0x118AC, 'M', '𑣌'), + (0x118AD, 'M', '𑣍'), + (0x118AE, 'M', '𑣎'), + (0x118AF, 'M', '𑣏'), + (0x118B0, 'M', '𑣐'), + (0x118B1, 'M', '𑣑'), + (0x118B2, 'M', '𑣒'), + (0x118B3, 'M', '𑣓'), + (0x118B4, 'M', '𑣔'), + (0x118B5, 'M', '𑣕'), + (0x118B6, 'M', '𑣖'), + (0x118B7, 'M', '𑣗'), ] def _seg_57(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x118BD, 'M', u'𑣝'), - (0x118BE, 'M', u'𑣞'), - (0x118BF, 'M', u'𑣟'), + (0x118B8, 'M', '𑣘'), + (0x118B9, 'M', '𑣙'), + (0x118BA, 'M', '𑣚'), + (0x118BB, 'M', '𑣛'), + (0x118BC, 'M', '𑣜'), + (0x118BD, 'M', '𑣝'), + (0x118BE, 'M', '𑣞'), + (0x118BF, 'M', '𑣟'), (0x118C0, 'V'), (0x118F3, 'X'), (0x118FF, 'V'), - (0x11900, 'X'), + (0x11907, 'X'), + (0x11909, 'V'), + (0x1190A, 'X'), + (0x1190C, 'V'), + (0x11914, 'X'), + (0x11915, 'V'), + (0x11917, 'X'), + (0x11918, 'V'), + (0x11936, 'X'), + (0x11937, 'V'), + (0x11939, 'X'), + (0x1193B, 'V'), + (0x11947, 'X'), + (0x11950, 'V'), + (0x1195A, 'X'), (0x119A0, 'V'), (0x119A8, 'X'), (0x119AA, 'V'), @@ -5996,6 +6074,8 @@ def _seg_57(): (0x11DAA, 'X'), (0x11EE0, 'V'), (0x11EF9, 'X'), + (0x11FB0, 'V'), + (0x11FB1, 'X'), (0x11FC0, 'V'), (0x11FF2, 'X'), (0x11FFF, 'V'), @@ -6014,6 +6094,11 @@ def _seg_57(): (0x16A39, 'X'), (0x16A40, 'V'), (0x16A5F, 'X'), + ] + +def _seg_58(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] + return [ (0x16A60, 'V'), (0x16A6A, 'X'), (0x16A6E, 'V'), @@ -6032,42 +6117,38 @@ def _seg_57(): (0x16B78, 'X'), (0x16B7D, 'V'), (0x16B90, 'X'), - (0x16E40, 'M', u'𖹠'), - (0x16E41, 'M', u'𖹡'), - (0x16E42, 'M', u'𖹢'), - ] - -def _seg_58(): - return [ - (0x16E43, 'M', u'𖹣'), - (0x16E44, 'M', u'𖹤'), - (0x16E45, 'M', u'𖹥'), - (0x16E46, 'M', u'𖹦'), - (0x16E47, 'M', u'𖹧'), - (0x16E48, 'M', u'𖹨'), - (0x16E49, 'M', u'𖹩'), - (0x16E4A, 'M', u'𖹪'), - (0x16E4B, 'M', u'𖹫'), - (0x16E4C, 'M', u'𖹬'), - (0x16E4D, 'M', u'𖹭'), - (0x16E4E, 'M', u'𖹮'), - (0x16E4F, 'M', u'𖹯'), - (0x16E50, 'M', u'𖹰'), - (0x16E51, 'M', u'𖹱'), - (0x16E52, 'M', u'𖹲'), - (0x16E53, 'M', u'𖹳'), - (0x16E54, 'M', u'𖹴'), - (0x16E55, 'M', u'𖹵'), - (0x16E56, 'M', u'𖹶'), - (0x16E57, 'M', u'𖹷'), - (0x16E58, 'M', u'𖹸'), - (0x16E59, 'M', u'𖹹'), - (0x16E5A, 'M', u'𖹺'), - (0x16E5B, 'M', u'𖹻'), - (0x16E5C, 'M', u'𖹼'), - (0x16E5D, 'M', u'𖹽'), - (0x16E5E, 'M', u'𖹾'), - (0x16E5F, 'M', u'𖹿'), + (0x16E40, 'M', '𖹠'), + (0x16E41, 'M', '𖹡'), + (0x16E42, 'M', '𖹢'), + (0x16E43, 'M', '𖹣'), + (0x16E44, 'M', '𖹤'), + (0x16E45, 'M', '𖹥'), + (0x16E46, 'M', '𖹦'), + (0x16E47, 'M', '𖹧'), + (0x16E48, 'M', '𖹨'), + (0x16E49, 'M', '𖹩'), + (0x16E4A, 'M', '𖹪'), + (0x16E4B, 'M', '𖹫'), + (0x16E4C, 'M', '𖹬'), + (0x16E4D, 'M', '𖹭'), + (0x16E4E, 'M', '𖹮'), + (0x16E4F, 'M', '𖹯'), + (0x16E50, 'M', '𖹰'), + (0x16E51, 'M', '𖹱'), + (0x16E52, 'M', '𖹲'), + (0x16E53, 'M', '𖹳'), + (0x16E54, 'M', '𖹴'), + (0x16E55, 'M', '𖹵'), + (0x16E56, 'M', '𖹶'), + (0x16E57, 'M', '𖹷'), + (0x16E58, 'M', '𖹸'), + (0x16E59, 'M', '𖹹'), + (0x16E5A, 'M', '𖹺'), + (0x16E5B, 'M', '𖹻'), + (0x16E5C, 'M', '𖹼'), + (0x16E5D, 'M', '𖹽'), + (0x16E5E, 'M', '𖹾'), + (0x16E5F, 'M', '𖹿'), (0x16E60, 'V'), (0x16E9B, 'X'), (0x16F00, 'V'), @@ -6077,11 +6158,15 @@ def _seg_58(): (0x16F8F, 'V'), (0x16FA0, 'X'), (0x16FE0, 'V'), - (0x16FE4, 'X'), + (0x16FE5, 'X'), + (0x16FF0, 'V'), + (0x16FF2, 'X'), (0x17000, 'V'), (0x187F8, 'X'), (0x18800, 'V'), - (0x18AF3, 'X'), + (0x18CD6, 'X'), + (0x18D00, 'V'), + (0x18D09, 'X'), (0x1B000, 'V'), (0x1B11F, 'X'), (0x1B150, 'V'), @@ -6106,22 +6191,27 @@ def _seg_58(): (0x1D100, 'V'), (0x1D127, 'X'), (0x1D129, 'V'), - (0x1D15E, 'M', u'𝅗𝅥'), - (0x1D15F, 'M', u'𝅘𝅥'), - (0x1D160, 'M', u'𝅘𝅥𝅮'), - (0x1D161, 'M', u'𝅘𝅥𝅯'), - (0x1D162, 'M', u'𝅘𝅥𝅰'), - (0x1D163, 'M', u'𝅘𝅥𝅱'), - (0x1D164, 'M', u'𝅘𝅥𝅲'), + (0x1D15E, 'M', '𝅗𝅥'), + (0x1D15F, 'M', '𝅘𝅥'), + (0x1D160, 'M', '𝅘𝅥𝅮'), + (0x1D161, 'M', '𝅘𝅥𝅯'), + (0x1D162, 'M', '𝅘𝅥𝅰'), + (0x1D163, 'M', '𝅘𝅥𝅱'), + (0x1D164, 'M', '𝅘𝅥𝅲'), (0x1D165, 'V'), + ] + +def _seg_59(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] + return [ (0x1D173, 'X'), (0x1D17B, 'V'), - (0x1D1BB, 'M', u'𝆹𝅥'), - (0x1D1BC, 'M', u'𝆺𝅥'), - (0x1D1BD, 'M', u'𝆹𝅥𝅮'), - (0x1D1BE, 'M', u'𝆺𝅥𝅮'), - (0x1D1BF, 'M', u'𝆹𝅥𝅯'), - (0x1D1C0, 'M', u'𝆺𝅥𝅯'), + (0x1D1BB, 'M', '𝆹𝅥'), + (0x1D1BC, 'M', '𝆺𝅥'), + (0x1D1BD, 'M', '𝆹𝅥𝅮'), + (0x1D1BE, 'M', '𝆺𝅥𝅮'), + (0x1D1BF, 'M', '𝆹𝅥𝅯'), + (0x1D1C0, 'M', '𝆺𝅥𝅯'), (0x1D1C1, 'V'), (0x1D1E9, 'X'), (0x1D200, 'V'), @@ -6132,1060 +6222,1066 @@ def _seg_58(): (0x1D357, 'X'), (0x1D360, 'V'), (0x1D379, 'X'), - (0x1D400, 'M', u'a'), - (0x1D401, 'M', u'b'), - (0x1D402, 'M', u'c'), - (0x1D403, 'M', u'd'), - (0x1D404, 'M', u'e'), - (0x1D405, 'M', u'f'), - (0x1D406, 'M', u'g'), - ] - -def _seg_59(): - return [ - (0x1D407, 'M', u'h'), - (0x1D408, 'M', u'i'), - (0x1D409, 'M', u'j'), - (0x1D40A, 'M', u'k'), - (0x1D40B, 'M', u'l'), - (0x1D40C, 'M', u'm'), - (0x1D40D, 'M', u'n'), - (0x1D40E, 'M', u'o'), - (0x1D40F, 'M', u'p'), - (0x1D410, 'M', u'q'), - (0x1D411, 'M', u'r'), - (0x1D412, 'M', u's'), - (0x1D413, 'M', u't'), - (0x1D414, 'M', u'u'), - (0x1D415, 'M', u'v'), - (0x1D416, 'M', u'w'), - (0x1D417, 'M', u'x'), - (0x1D418, 'M', u'y'), - (0x1D419, 'M', u'z'), - (0x1D41A, 'M', u'a'), - (0x1D41B, 'M', u'b'), - (0x1D41C, 'M', u'c'), - (0x1D41D, 'M', u'd'), - (0x1D41E, 'M', u'e'), - (0x1D41F, 'M', u'f'), - (0x1D420, 'M', u'g'), - (0x1D421, 'M', u'h'), - (0x1D422, 'M', u'i'), - (0x1D423, 'M', u'j'), - (0x1D424, 'M', u'k'), - (0x1D425, 'M', u'l'), - (0x1D426, 'M', u'm'), - (0x1D427, 'M', u'n'), - (0x1D428, 'M', u'o'), - (0x1D429, 'M', u'p'), - (0x1D42A, 'M', u'q'), - (0x1D42B, 'M', u'r'), - (0x1D42C, 'M', u's'), - (0x1D42D, 'M', u't'), - (0x1D42E, 'M', u'u'), - (0x1D42F, 'M', u'v'), - (0x1D430, 'M', u'w'), - (0x1D431, 'M', u'x'), - (0x1D432, 'M', u'y'), - (0x1D433, 'M', u'z'), - (0x1D434, 'M', u'a'), - (0x1D435, 'M', u'b'), - (0x1D436, 'M', u'c'), - (0x1D437, 'M', u'd'), - (0x1D438, 'M', u'e'), - (0x1D439, 'M', u'f'), - (0x1D43A, 'M', u'g'), - (0x1D43B, 'M', u'h'), - (0x1D43C, 'M', u'i'), - (0x1D43D, 'M', u'j'), - (0x1D43E, 'M', u'k'), - (0x1D43F, 'M', u'l'), - (0x1D440, 'M', u'm'), - (0x1D441, 'M', u'n'), - (0x1D442, 'M', u'o'), - (0x1D443, 'M', u'p'), - (0x1D444, 'M', u'q'), - (0x1D445, 'M', u'r'), - (0x1D446, 'M', u's'), - (0x1D447, 'M', u't'), - (0x1D448, 'M', u'u'), - (0x1D449, 'M', u'v'), - (0x1D44A, 'M', u'w'), - (0x1D44B, 'M', u'x'), - (0x1D44C, 'M', u'y'), - (0x1D44D, 'M', u'z'), - (0x1D44E, 'M', u'a'), - (0x1D44F, 'M', u'b'), - (0x1D450, 'M', u'c'), - (0x1D451, 'M', u'd'), - (0x1D452, 'M', u'e'), - (0x1D453, 'M', u'f'), - (0x1D454, 'M', u'g'), - (0x1D455, 'X'), - (0x1D456, 'M', u'i'), - (0x1D457, 'M', u'j'), - (0x1D458, 'M', u'k'), - (0x1D459, 'M', u'l'), - (0x1D45A, 'M', u'm'), - (0x1D45B, 'M', u'n'), - (0x1D45C, 'M', u'o'), - (0x1D45D, 'M', u'p'), - (0x1D45E, 'M', u'q'), - (0x1D45F, 'M', u'r'), - (0x1D460, 'M', u's'), - (0x1D461, 'M', u't'), - (0x1D462, 'M', u'u'), - (0x1D463, 'M', u'v'), - (0x1D464, 'M', u'w'), - (0x1D465, 'M', u'x'), - (0x1D466, 'M', u'y'), - (0x1D467, 'M', u'z'), - (0x1D468, 'M', u'a'), - (0x1D469, 'M', u'b'), - (0x1D46A, 'M', u'c'), + (0x1D400, 'M', 'a'), + (0x1D401, 'M', 'b'), + (0x1D402, 'M', 'c'), + (0x1D403, 'M', 'd'), + (0x1D404, 'M', 'e'), + (0x1D405, 'M', 'f'), + (0x1D406, 'M', 'g'), + (0x1D407, 'M', 'h'), + (0x1D408, 'M', 'i'), + (0x1D409, 'M', 'j'), + (0x1D40A, 'M', 'k'), + (0x1D40B, 'M', 'l'), + (0x1D40C, 'M', 'm'), + (0x1D40D, 'M', 'n'), + (0x1D40E, 'M', 'o'), + (0x1D40F, 'M', 'p'), + (0x1D410, 'M', 'q'), + (0x1D411, 'M', 'r'), + (0x1D412, 'M', 's'), + (0x1D413, 'M', 't'), + (0x1D414, 'M', 'u'), + (0x1D415, 'M', 'v'), + (0x1D416, 'M', 'w'), + (0x1D417, 'M', 'x'), + (0x1D418, 'M', 'y'), + (0x1D419, 'M', 'z'), + (0x1D41A, 'M', 'a'), + (0x1D41B, 'M', 'b'), + (0x1D41C, 'M', 'c'), + (0x1D41D, 'M', 'd'), + (0x1D41E, 'M', 'e'), + (0x1D41F, 'M', 'f'), + (0x1D420, 'M', 'g'), + (0x1D421, 'M', 'h'), + (0x1D422, 'M', 'i'), + (0x1D423, 'M', 'j'), + (0x1D424, 'M', 'k'), + (0x1D425, 'M', 'l'), + (0x1D426, 'M', 'm'), + (0x1D427, 'M', 'n'), + (0x1D428, 'M', 'o'), + (0x1D429, 'M', 'p'), + (0x1D42A, 'M', 'q'), + (0x1D42B, 'M', 'r'), + (0x1D42C, 'M', 's'), + (0x1D42D, 'M', 't'), + (0x1D42E, 'M', 'u'), + (0x1D42F, 'M', 'v'), + (0x1D430, 'M', 'w'), + (0x1D431, 'M', 'x'), + (0x1D432, 'M', 'y'), + (0x1D433, 'M', 'z'), + (0x1D434, 'M', 'a'), + (0x1D435, 'M', 'b'), + (0x1D436, 'M', 'c'), + (0x1D437, 'M', 'd'), + (0x1D438, 'M', 'e'), + (0x1D439, 'M', 'f'), + (0x1D43A, 'M', 'g'), + (0x1D43B, 'M', 'h'), + (0x1D43C, 'M', 'i'), + (0x1D43D, 'M', 'j'), + (0x1D43E, 'M', 'k'), + (0x1D43F, 'M', 'l'), + (0x1D440, 'M', 'm'), + (0x1D441, 'M', 'n'), + (0x1D442, 'M', 'o'), + (0x1D443, 'M', 'p'), + (0x1D444, 'M', 'q'), + (0x1D445, 'M', 'r'), + (0x1D446, 'M', 's'), + (0x1D447, 'M', 't'), + (0x1D448, 'M', 'u'), + (0x1D449, 'M', 'v'), + (0x1D44A, 'M', 'w'), + (0x1D44B, 'M', 'x'), + (0x1D44C, 'M', 'y'), + (0x1D44D, 'M', 'z'), + (0x1D44E, 'M', 'a'), + (0x1D44F, 'M', 'b'), + (0x1D450, 'M', 'c'), + (0x1D451, 'M', 'd'), ] def _seg_60(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D46B, 'M', u'd'), - (0x1D46C, 'M', u'e'), - (0x1D46D, 'M', u'f'), - (0x1D46E, 'M', u'g'), - (0x1D46F, 'M', u'h'), - (0x1D470, 'M', u'i'), - (0x1D471, 'M', u'j'), - (0x1D472, 'M', u'k'), - (0x1D473, 'M', u'l'), - (0x1D474, 'M', u'm'), - (0x1D475, 'M', u'n'), - (0x1D476, 'M', u'o'), - (0x1D477, 'M', u'p'), - (0x1D478, 'M', u'q'), - (0x1D479, 'M', u'r'), - (0x1D47A, 'M', u's'), - (0x1D47B, 'M', u't'), - (0x1D47C, 'M', u'u'), - (0x1D47D, 'M', u'v'), - (0x1D47E, 'M', u'w'), - (0x1D47F, 'M', u'x'), - (0x1D480, 'M', u'y'), - (0x1D481, 'M', u'z'), - (0x1D482, 'M', u'a'), - (0x1D483, 'M', u'b'), - (0x1D484, 'M', u'c'), - (0x1D485, 'M', u'd'), - (0x1D486, 'M', u'e'), - (0x1D487, 'M', u'f'), - (0x1D488, 'M', u'g'), - (0x1D489, 'M', u'h'), - (0x1D48A, 'M', u'i'), - (0x1D48B, 'M', u'j'), - (0x1D48C, 'M', u'k'), - (0x1D48D, 'M', u'l'), - (0x1D48E, 'M', u'm'), - (0x1D48F, 'M', u'n'), - (0x1D490, 'M', u'o'), - (0x1D491, 'M', u'p'), - (0x1D492, 'M', u'q'), - (0x1D493, 'M', u'r'), - (0x1D494, 'M', u's'), - (0x1D495, 'M', u't'), - (0x1D496, 'M', u'u'), - (0x1D497, 'M', u'v'), - (0x1D498, 'M', u'w'), - (0x1D499, 'M', u'x'), - (0x1D49A, 'M', u'y'), - (0x1D49B, 'M', u'z'), - (0x1D49C, 'M', u'a'), + (0x1D452, 'M', 'e'), + (0x1D453, 'M', 'f'), + (0x1D454, 'M', 'g'), + (0x1D455, 'X'), + (0x1D456, 'M', 'i'), + (0x1D457, 'M', 'j'), + (0x1D458, 'M', 'k'), + (0x1D459, 'M', 'l'), + (0x1D45A, 'M', 'm'), + (0x1D45B, 'M', 'n'), + (0x1D45C, 'M', 'o'), + (0x1D45D, 'M', 'p'), + (0x1D45E, 'M', 'q'), + (0x1D45F, 'M', 'r'), + (0x1D460, 'M', 's'), + (0x1D461, 'M', 't'), + (0x1D462, 'M', 'u'), + (0x1D463, 'M', 'v'), + (0x1D464, 'M', 'w'), + (0x1D465, 'M', 'x'), + (0x1D466, 'M', 'y'), + (0x1D467, 'M', 'z'), + (0x1D468, 'M', 'a'), + (0x1D469, 'M', 'b'), + (0x1D46A, 'M', 'c'), + (0x1D46B, 'M', 'd'), + (0x1D46C, 'M', 'e'), + (0x1D46D, 'M', 'f'), + (0x1D46E, 'M', 'g'), + (0x1D46F, 'M', 'h'), + (0x1D470, 'M', 'i'), + (0x1D471, 'M', 'j'), + (0x1D472, 'M', 'k'), + (0x1D473, 'M', 'l'), + (0x1D474, 'M', 'm'), + (0x1D475, 'M', 'n'), + (0x1D476, 'M', 'o'), + (0x1D477, 'M', 'p'), + (0x1D478, 'M', 'q'), + (0x1D479, 'M', 'r'), + (0x1D47A, 'M', 's'), + (0x1D47B, 'M', 't'), + (0x1D47C, 'M', 'u'), + (0x1D47D, 'M', 'v'), + (0x1D47E, 'M', 'w'), + (0x1D47F, 'M', 'x'), + (0x1D480, 'M', 'y'), + (0x1D481, 'M', 'z'), + (0x1D482, 'M', 'a'), + (0x1D483, 'M', 'b'), + (0x1D484, 'M', 'c'), + (0x1D485, 'M', 'd'), + (0x1D486, 'M', 'e'), + (0x1D487, 'M', 'f'), + (0x1D488, 'M', 'g'), + (0x1D489, 'M', 'h'), + (0x1D48A, 'M', 'i'), + (0x1D48B, 'M', 'j'), + (0x1D48C, 'M', 'k'), + (0x1D48D, 'M', 'l'), + (0x1D48E, 'M', 'm'), + (0x1D48F, 'M', 'n'), + (0x1D490, 'M', 'o'), + (0x1D491, 'M', 'p'), + (0x1D492, 'M', 'q'), + (0x1D493, 'M', 'r'), + (0x1D494, 'M', 's'), + (0x1D495, 'M', 't'), + (0x1D496, 'M', 'u'), + (0x1D497, 'M', 'v'), + (0x1D498, 'M', 'w'), + (0x1D499, 'M', 'x'), + (0x1D49A, 'M', 'y'), + (0x1D49B, 'M', 'z'), + (0x1D49C, 'M', 'a'), (0x1D49D, 'X'), - (0x1D49E, 'M', u'c'), - (0x1D49F, 'M', u'd'), + (0x1D49E, 'M', 'c'), + (0x1D49F, 'M', 'd'), (0x1D4A0, 'X'), - (0x1D4A2, 'M', u'g'), + (0x1D4A2, 'M', 'g'), (0x1D4A3, 'X'), - (0x1D4A5, 'M', u'j'), - (0x1D4A6, 'M', u'k'), + (0x1D4A5, 'M', 'j'), + (0x1D4A6, 'M', 'k'), (0x1D4A7, 'X'), - (0x1D4A9, 'M', u'n'), - (0x1D4AA, 'M', u'o'), - (0x1D4AB, 'M', u'p'), - (0x1D4AC, 'M', u'q'), + (0x1D4A9, 'M', 'n'), + (0x1D4AA, 'M', 'o'), + (0x1D4AB, 'M', 'p'), + (0x1D4AC, 'M', 'q'), (0x1D4AD, 'X'), - (0x1D4AE, 'M', u's'), - (0x1D4AF, 'M', u't'), - (0x1D4B0, 'M', u'u'), - (0x1D4B1, 'M', u'v'), - (0x1D4B2, 'M', u'w'), - (0x1D4B3, 'M', u'x'), - (0x1D4B4, 'M', u'y'), - (0x1D4B5, 'M', u'z'), - (0x1D4B6, 'M', u'a'), - (0x1D4B7, 'M', u'b'), - (0x1D4B8, 'M', u'c'), - (0x1D4B9, 'M', u'd'), - (0x1D4BA, 'X'), - (0x1D4BB, 'M', u'f'), - (0x1D4BC, 'X'), - (0x1D4BD, 'M', u'h'), - (0x1D4BE, 'M', u'i'), - (0x1D4BF, 'M', u'j'), - (0x1D4C0, 'M', u'k'), - (0x1D4C1, 'M', u'l'), - (0x1D4C2, 'M', u'm'), - (0x1D4C3, 'M', u'n'), - (0x1D4C4, 'X'), - (0x1D4C5, 'M', u'p'), - (0x1D4C6, 'M', u'q'), - (0x1D4C7, 'M', u'r'), - (0x1D4C8, 'M', u's'), - (0x1D4C9, 'M', u't'), - (0x1D4CA, 'M', u'u'), - (0x1D4CB, 'M', u'v'), - (0x1D4CC, 'M', u'w'), - (0x1D4CD, 'M', u'x'), - (0x1D4CE, 'M', u'y'), - (0x1D4CF, 'M', u'z'), - (0x1D4D0, 'M', u'a'), - (0x1D4D1, 'M', u'b'), + (0x1D4AE, 'M', 's'), + (0x1D4AF, 'M', 't'), + (0x1D4B0, 'M', 'u'), + (0x1D4B1, 'M', 'v'), + (0x1D4B2, 'M', 'w'), + (0x1D4B3, 'M', 'x'), + (0x1D4B4, 'M', 'y'), + (0x1D4B5, 'M', 'z'), + (0x1D4B6, 'M', 'a'), + (0x1D4B7, 'M', 'b'), + (0x1D4B8, 'M', 'c'), ] def _seg_61(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D4D2, 'M', u'c'), - (0x1D4D3, 'M', u'd'), - (0x1D4D4, 'M', u'e'), - (0x1D4D5, 'M', u'f'), - (0x1D4D6, 'M', u'g'), - (0x1D4D7, 'M', u'h'), - (0x1D4D8, 'M', u'i'), - (0x1D4D9, 'M', u'j'), - (0x1D4DA, 'M', u'k'), - (0x1D4DB, 'M', u'l'), - (0x1D4DC, 'M', u'm'), - (0x1D4DD, 'M', u'n'), - (0x1D4DE, 'M', u'o'), - (0x1D4DF, 'M', u'p'), - (0x1D4E0, 'M', u'q'), - (0x1D4E1, 'M', u'r'), - (0x1D4E2, 'M', u's'), - (0x1D4E3, 'M', u't'), - (0x1D4E4, 'M', u'u'), - (0x1D4E5, 'M', u'v'), - (0x1D4E6, 'M', u'w'), - (0x1D4E7, 'M', u'x'), - (0x1D4E8, 'M', u'y'), - (0x1D4E9, 'M', u'z'), - (0x1D4EA, 'M', u'a'), - (0x1D4EB, 'M', u'b'), - (0x1D4EC, 'M', u'c'), - (0x1D4ED, 'M', u'd'), - (0x1D4EE, 'M', u'e'), - (0x1D4EF, 'M', u'f'), - (0x1D4F0, 'M', u'g'), - (0x1D4F1, 'M', u'h'), - (0x1D4F2, 'M', u'i'), - (0x1D4F3, 'M', u'j'), - (0x1D4F4, 'M', u'k'), - (0x1D4F5, 'M', u'l'), - (0x1D4F6, 'M', u'm'), - (0x1D4F7, 'M', u'n'), - (0x1D4F8, 'M', u'o'), - (0x1D4F9, 'M', u'p'), - (0x1D4FA, 'M', u'q'), - (0x1D4FB, 'M', u'r'), - (0x1D4FC, 'M', u's'), - (0x1D4FD, 'M', u't'), - (0x1D4FE, 'M', u'u'), - (0x1D4FF, 'M', u'v'), - (0x1D500, 'M', u'w'), - (0x1D501, 'M', u'x'), - (0x1D502, 'M', u'y'), - (0x1D503, 'M', u'z'), - (0x1D504, 'M', u'a'), - (0x1D505, 'M', u'b'), + (0x1D4B9, 'M', 'd'), + (0x1D4BA, 'X'), + (0x1D4BB, 'M', 'f'), + (0x1D4BC, 'X'), + (0x1D4BD, 'M', 'h'), + (0x1D4BE, 'M', 'i'), + (0x1D4BF, 'M', 'j'), + (0x1D4C0, 'M', 'k'), + (0x1D4C1, 'M', 'l'), + (0x1D4C2, 'M', 'm'), + (0x1D4C3, 'M', 'n'), + (0x1D4C4, 'X'), + (0x1D4C5, 'M', 'p'), + (0x1D4C6, 'M', 'q'), + (0x1D4C7, 'M', 'r'), + (0x1D4C8, 'M', 's'), + (0x1D4C9, 'M', 't'), + (0x1D4CA, 'M', 'u'), + (0x1D4CB, 'M', 'v'), + (0x1D4CC, 'M', 'w'), + (0x1D4CD, 'M', 'x'), + (0x1D4CE, 'M', 'y'), + (0x1D4CF, 'M', 'z'), + (0x1D4D0, 'M', 'a'), + (0x1D4D1, 'M', 'b'), + (0x1D4D2, 'M', 'c'), + (0x1D4D3, 'M', 'd'), + (0x1D4D4, 'M', 'e'), + (0x1D4D5, 'M', 'f'), + (0x1D4D6, 'M', 'g'), + (0x1D4D7, 'M', 'h'), + (0x1D4D8, 'M', 'i'), + (0x1D4D9, 'M', 'j'), + (0x1D4DA, 'M', 'k'), + (0x1D4DB, 'M', 'l'), + (0x1D4DC, 'M', 'm'), + (0x1D4DD, 'M', 'n'), + (0x1D4DE, 'M', 'o'), + (0x1D4DF, 'M', 'p'), + (0x1D4E0, 'M', 'q'), + (0x1D4E1, 'M', 'r'), + (0x1D4E2, 'M', 's'), + (0x1D4E3, 'M', 't'), + (0x1D4E4, 'M', 'u'), + (0x1D4E5, 'M', 'v'), + (0x1D4E6, 'M', 'w'), + (0x1D4E7, 'M', 'x'), + (0x1D4E8, 'M', 'y'), + (0x1D4E9, 'M', 'z'), + (0x1D4EA, 'M', 'a'), + (0x1D4EB, 'M', 'b'), + (0x1D4EC, 'M', 'c'), + (0x1D4ED, 'M', 'd'), + (0x1D4EE, 'M', 'e'), + (0x1D4EF, 'M', 'f'), + (0x1D4F0, 'M', 'g'), + (0x1D4F1, 'M', 'h'), + (0x1D4F2, 'M', 'i'), + (0x1D4F3, 'M', 'j'), + (0x1D4F4, 'M', 'k'), + (0x1D4F5, 'M', 'l'), + (0x1D4F6, 'M', 'm'), + (0x1D4F7, 'M', 'n'), + (0x1D4F8, 'M', 'o'), + (0x1D4F9, 'M', 'p'), + (0x1D4FA, 'M', 'q'), + (0x1D4FB, 'M', 'r'), + (0x1D4FC, 'M', 's'), + (0x1D4FD, 'M', 't'), + (0x1D4FE, 'M', 'u'), + (0x1D4FF, 'M', 'v'), + (0x1D500, 'M', 'w'), + (0x1D501, 'M', 'x'), + (0x1D502, 'M', 'y'), + (0x1D503, 'M', 'z'), + (0x1D504, 'M', 'a'), + (0x1D505, 'M', 'b'), (0x1D506, 'X'), - (0x1D507, 'M', u'd'), - (0x1D508, 'M', u'e'), - (0x1D509, 'M', u'f'), - (0x1D50A, 'M', u'g'), + (0x1D507, 'M', 'd'), + (0x1D508, 'M', 'e'), + (0x1D509, 'M', 'f'), + (0x1D50A, 'M', 'g'), (0x1D50B, 'X'), - (0x1D50D, 'M', u'j'), - (0x1D50E, 'M', u'k'), - (0x1D50F, 'M', u'l'), - (0x1D510, 'M', u'm'), - (0x1D511, 'M', u'n'), - (0x1D512, 'M', u'o'), - (0x1D513, 'M', u'p'), - (0x1D514, 'M', u'q'), + (0x1D50D, 'M', 'j'), + (0x1D50E, 'M', 'k'), + (0x1D50F, 'M', 'l'), + (0x1D510, 'M', 'm'), + (0x1D511, 'M', 'n'), + (0x1D512, 'M', 'o'), + (0x1D513, 'M', 'p'), + (0x1D514, 'M', 'q'), (0x1D515, 'X'), - (0x1D516, 'M', u's'), - (0x1D517, 'M', u't'), - (0x1D518, 'M', u'u'), - (0x1D519, 'M', u'v'), - (0x1D51A, 'M', u'w'), - (0x1D51B, 'M', u'x'), - (0x1D51C, 'M', u'y'), + (0x1D516, 'M', 's'), + (0x1D517, 'M', 't'), + (0x1D518, 'M', 'u'), + (0x1D519, 'M', 'v'), + (0x1D51A, 'M', 'w'), + (0x1D51B, 'M', 'x'), + (0x1D51C, 'M', 'y'), (0x1D51D, 'X'), - (0x1D51E, 'M', u'a'), - (0x1D51F, 'M', u'b'), - (0x1D520, 'M', u'c'), - (0x1D521, 'M', u'd'), - (0x1D522, 'M', u'e'), - (0x1D523, 'M', u'f'), - (0x1D524, 'M', u'g'), - (0x1D525, 'M', u'h'), - (0x1D526, 'M', u'i'), - (0x1D527, 'M', u'j'), - (0x1D528, 'M', u'k'), - (0x1D529, 'M', u'l'), - (0x1D52A, 'M', u'm'), - (0x1D52B, 'M', u'n'), - (0x1D52C, 'M', u'o'), - (0x1D52D, 'M', u'p'), - (0x1D52E, 'M', u'q'), - (0x1D52F, 'M', u'r'), - (0x1D530, 'M', u's'), - (0x1D531, 'M', u't'), - (0x1D532, 'M', u'u'), - (0x1D533, 'M', u'v'), - (0x1D534, 'M', u'w'), - (0x1D535, 'M', u'x'), - (0x1D536, 'M', u'y'), ] def _seg_62(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D537, 'M', u'z'), - (0x1D538, 'M', u'a'), - (0x1D539, 'M', u'b'), + (0x1D51E, 'M', 'a'), + (0x1D51F, 'M', 'b'), + (0x1D520, 'M', 'c'), + (0x1D521, 'M', 'd'), + (0x1D522, 'M', 'e'), + (0x1D523, 'M', 'f'), + (0x1D524, 'M', 'g'), + (0x1D525, 'M', 'h'), + (0x1D526, 'M', 'i'), + (0x1D527, 'M', 'j'), + (0x1D528, 'M', 'k'), + (0x1D529, 'M', 'l'), + (0x1D52A, 'M', 'm'), + (0x1D52B, 'M', 'n'), + (0x1D52C, 'M', 'o'), + (0x1D52D, 'M', 'p'), + (0x1D52E, 'M', 'q'), + (0x1D52F, 'M', 'r'), + (0x1D530, 'M', 's'), + (0x1D531, 'M', 't'), + (0x1D532, 'M', 'u'), + (0x1D533, 'M', 'v'), + (0x1D534, 'M', 'w'), + (0x1D535, 'M', 'x'), + (0x1D536, 'M', 'y'), + (0x1D537, 'M', 'z'), + (0x1D538, 'M', 'a'), + (0x1D539, 'M', 'b'), (0x1D53A, 'X'), - (0x1D53B, 'M', u'd'), - (0x1D53C, 'M', u'e'), - (0x1D53D, 'M', u'f'), - (0x1D53E, 'M', u'g'), + (0x1D53B, 'M', 'd'), + (0x1D53C, 'M', 'e'), + (0x1D53D, 'M', 'f'), + (0x1D53E, 'M', 'g'), (0x1D53F, 'X'), - (0x1D540, 'M', u'i'), - (0x1D541, 'M', u'j'), - (0x1D542, 'M', u'k'), - (0x1D543, 'M', u'l'), - (0x1D544, 'M', u'm'), + (0x1D540, 'M', 'i'), + (0x1D541, 'M', 'j'), + (0x1D542, 'M', 'k'), + (0x1D543, 'M', 'l'), + (0x1D544, 'M', 'm'), (0x1D545, 'X'), - (0x1D546, 'M', u'o'), + (0x1D546, 'M', 'o'), (0x1D547, 'X'), - (0x1D54A, 'M', u's'), - (0x1D54B, 'M', u't'), - (0x1D54C, 'M', u'u'), - (0x1D54D, 'M', u'v'), - (0x1D54E, 'M', u'w'), - (0x1D54F, 'M', u'x'), - (0x1D550, 'M', u'y'), + (0x1D54A, 'M', 's'), + (0x1D54B, 'M', 't'), + (0x1D54C, 'M', 'u'), + (0x1D54D, 'M', 'v'), + (0x1D54E, 'M', 'w'), + (0x1D54F, 'M', 'x'), + (0x1D550, 'M', 'y'), (0x1D551, 'X'), - (0x1D552, 'M', u'a'), - (0x1D553, 'M', u'b'), - (0x1D554, 'M', u'c'), - (0x1D555, 'M', u'd'), - (0x1D556, 'M', u'e'), - (0x1D557, 'M', u'f'), - (0x1D558, 'M', u'g'), - (0x1D559, 'M', u'h'), - (0x1D55A, 'M', u'i'), - (0x1D55B, 'M', u'j'), - (0x1D55C, 'M', u'k'), - (0x1D55D, 'M', u'l'), - (0x1D55E, 'M', u'm'), - (0x1D55F, 'M', u'n'), - (0x1D560, 'M', u'o'), - (0x1D561, 'M', u'p'), - (0x1D562, 'M', u'q'), - (0x1D563, 'M', u'r'), - (0x1D564, 'M', u's'), - (0x1D565, 'M', u't'), - (0x1D566, 'M', u'u'), - (0x1D567, 'M', u'v'), - (0x1D568, 'M', u'w'), - (0x1D569, 'M', u'x'), - (0x1D56A, 'M', u'y'), - (0x1D56B, 'M', u'z'), - (0x1D56C, 'M', u'a'), - (0x1D56D, 'M', u'b'), - (0x1D56E, 'M', u'c'), - (0x1D56F, 'M', u'd'), - (0x1D570, 'M', u'e'), - (0x1D571, 'M', u'f'), - (0x1D572, 'M', u'g'), - (0x1D573, 'M', u'h'), - (0x1D574, 'M', u'i'), - (0x1D575, 'M', u'j'), - (0x1D576, 'M', u'k'), - (0x1D577, 'M', u'l'), - (0x1D578, 'M', u'm'), - (0x1D579, 'M', u'n'), - (0x1D57A, 'M', u'o'), - (0x1D57B, 'M', u'p'), - (0x1D57C, 'M', u'q'), - (0x1D57D, 'M', u'r'), - (0x1D57E, 'M', u's'), - (0x1D57F, 'M', u't'), - (0x1D580, 'M', u'u'), - (0x1D581, 'M', u'v'), - (0x1D582, 'M', u'w'), - (0x1D583, 'M', u'x'), - (0x1D584, 'M', u'y'), - (0x1D585, 'M', u'z'), - (0x1D586, 'M', u'a'), - (0x1D587, 'M', u'b'), - (0x1D588, 'M', u'c'), - (0x1D589, 'M', u'd'), - (0x1D58A, 'M', u'e'), - (0x1D58B, 'M', u'f'), - (0x1D58C, 'M', u'g'), - (0x1D58D, 'M', u'h'), - (0x1D58E, 'M', u'i'), - (0x1D58F, 'M', u'j'), - (0x1D590, 'M', u'k'), - (0x1D591, 'M', u'l'), - (0x1D592, 'M', u'm'), - (0x1D593, 'M', u'n'), - (0x1D594, 'M', u'o'), - (0x1D595, 'M', u'p'), - (0x1D596, 'M', u'q'), - (0x1D597, 'M', u'r'), - (0x1D598, 'M', u's'), - (0x1D599, 'M', u't'), - (0x1D59A, 'M', u'u'), - (0x1D59B, 'M', u'v'), - (0x1D59C, 'M', u'w'), + (0x1D552, 'M', 'a'), + (0x1D553, 'M', 'b'), + (0x1D554, 'M', 'c'), + (0x1D555, 'M', 'd'), + (0x1D556, 'M', 'e'), + (0x1D557, 'M', 'f'), + (0x1D558, 'M', 'g'), + (0x1D559, 'M', 'h'), + (0x1D55A, 'M', 'i'), + (0x1D55B, 'M', 'j'), + (0x1D55C, 'M', 'k'), + (0x1D55D, 'M', 'l'), + (0x1D55E, 'M', 'm'), + (0x1D55F, 'M', 'n'), + (0x1D560, 'M', 'o'), + (0x1D561, 'M', 'p'), + (0x1D562, 'M', 'q'), + (0x1D563, 'M', 'r'), + (0x1D564, 'M', 's'), + (0x1D565, 'M', 't'), + (0x1D566, 'M', 'u'), + (0x1D567, 'M', 'v'), + (0x1D568, 'M', 'w'), + (0x1D569, 'M', 'x'), + (0x1D56A, 'M', 'y'), + (0x1D56B, 'M', 'z'), + (0x1D56C, 'M', 'a'), + (0x1D56D, 'M', 'b'), + (0x1D56E, 'M', 'c'), + (0x1D56F, 'M', 'd'), + (0x1D570, 'M', 'e'), + (0x1D571, 'M', 'f'), + (0x1D572, 'M', 'g'), + (0x1D573, 'M', 'h'), + (0x1D574, 'M', 'i'), + (0x1D575, 'M', 'j'), + (0x1D576, 'M', 'k'), + (0x1D577, 'M', 'l'), + (0x1D578, 'M', 'm'), + (0x1D579, 'M', 'n'), + (0x1D57A, 'M', 'o'), + (0x1D57B, 'M', 'p'), + (0x1D57C, 'M', 'q'), + (0x1D57D, 'M', 'r'), + (0x1D57E, 'M', 's'), + (0x1D57F, 'M', 't'), + (0x1D580, 'M', 'u'), + (0x1D581, 'M', 'v'), + (0x1D582, 'M', 'w'), + (0x1D583, 'M', 'x'), ] def _seg_63(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D59D, 'M', u'x'), - (0x1D59E, 'M', u'y'), - (0x1D59F, 'M', u'z'), - (0x1D5A0, 'M', u'a'), - (0x1D5A1, 'M', u'b'), - (0x1D5A2, 'M', u'c'), - (0x1D5A3, 'M', u'd'), - (0x1D5A4, 'M', u'e'), - (0x1D5A5, 'M', u'f'), - (0x1D5A6, 'M', u'g'), - (0x1D5A7, 'M', u'h'), - (0x1D5A8, 'M', u'i'), - (0x1D5A9, 'M', u'j'), - (0x1D5AA, 'M', u'k'), - (0x1D5AB, 'M', u'l'), - (0x1D5AC, 'M', u'm'), - (0x1D5AD, 'M', u'n'), - (0x1D5AE, 'M', u'o'), - (0x1D5AF, 'M', u'p'), - (0x1D5B0, 'M', u'q'), - (0x1D5B1, 'M', u'r'), - (0x1D5B2, 'M', u's'), - (0x1D5B3, 'M', u't'), - (0x1D5B4, 'M', u'u'), - (0x1D5B5, 'M', u'v'), - (0x1D5B6, 'M', u'w'), - (0x1D5B7, 'M', u'x'), - (0x1D5B8, 'M', u'y'), - (0x1D5B9, 'M', u'z'), - (0x1D5BA, 'M', u'a'), - (0x1D5BB, 'M', u'b'), - (0x1D5BC, 'M', u'c'), - (0x1D5BD, 'M', u'd'), - (0x1D5BE, 'M', u'e'), - (0x1D5BF, 'M', u'f'), - (0x1D5C0, 'M', u'g'), - (0x1D5C1, 'M', u'h'), - (0x1D5C2, 'M', u'i'), - (0x1D5C3, 'M', u'j'), - (0x1D5C4, 'M', u'k'), - (0x1D5C5, 'M', u'l'), - (0x1D5C6, 'M', u'm'), - (0x1D5C7, 'M', u'n'), - (0x1D5C8, 'M', u'o'), - (0x1D5C9, 'M', u'p'), - (0x1D5CA, 'M', u'q'), - (0x1D5CB, 'M', u'r'), - (0x1D5CC, 'M', u's'), - (0x1D5CD, 'M', u't'), - (0x1D5CE, 'M', u'u'), - (0x1D5CF, 'M', u'v'), - (0x1D5D0, 'M', u'w'), - (0x1D5D1, 'M', u'x'), - (0x1D5D2, 'M', u'y'), - (0x1D5D3, 'M', u'z'), - (0x1D5D4, 'M', u'a'), - (0x1D5D5, 'M', u'b'), - (0x1D5D6, 'M', u'c'), - (0x1D5D7, 'M', u'd'), - (0x1D5D8, 'M', u'e'), - (0x1D5D9, 'M', u'f'), - (0x1D5DA, 'M', u'g'), - (0x1D5DB, 'M', u'h'), - (0x1D5DC, 'M', u'i'), - (0x1D5DD, 'M', u'j'), - (0x1D5DE, 'M', u'k'), - (0x1D5DF, 'M', u'l'), - (0x1D5E0, 'M', u'm'), - (0x1D5E1, 'M', u'n'), - (0x1D5E2, 'M', u'o'), - (0x1D5E3, 'M', u'p'), - (0x1D5E4, 'M', u'q'), - (0x1D5E5, 'M', u'r'), - (0x1D5E6, 'M', u's'), - (0x1D5E7, 'M', u't'), - (0x1D5E8, 'M', u'u'), - (0x1D5E9, 'M', u'v'), - (0x1D5EA, 'M', u'w'), - (0x1D5EB, 'M', u'x'), - (0x1D5EC, 'M', u'y'), - (0x1D5ED, 'M', u'z'), - (0x1D5EE, 'M', u'a'), - (0x1D5EF, 'M', u'b'), - (0x1D5F0, 'M', u'c'), - (0x1D5F1, 'M', u'd'), - (0x1D5F2, 'M', u'e'), - (0x1D5F3, 'M', u'f'), - (0x1D5F4, 'M', u'g'), - (0x1D5F5, 'M', u'h'), - (0x1D5F6, 'M', u'i'), - (0x1D5F7, 'M', u'j'), - (0x1D5F8, 'M', u'k'), - (0x1D5F9, 'M', u'l'), - (0x1D5FA, 'M', u'm'), - (0x1D5FB, 'M', u'n'), - (0x1D5FC, 'M', u'o'), - (0x1D5FD, 'M', u'p'), - (0x1D5FE, 'M', u'q'), - (0x1D5FF, 'M', u'r'), - (0x1D600, 'M', u's'), + (0x1D584, 'M', 'y'), + (0x1D585, 'M', 'z'), + (0x1D586, 'M', 'a'), + (0x1D587, 'M', 'b'), + (0x1D588, 'M', 'c'), + (0x1D589, 'M', 'd'), + (0x1D58A, 'M', 'e'), + (0x1D58B, 'M', 'f'), + (0x1D58C, 'M', 'g'), + (0x1D58D, 'M', 'h'), + (0x1D58E, 'M', 'i'), + (0x1D58F, 'M', 'j'), + (0x1D590, 'M', 'k'), + (0x1D591, 'M', 'l'), + (0x1D592, 'M', 'm'), + (0x1D593, 'M', 'n'), + (0x1D594, 'M', 'o'), + (0x1D595, 'M', 'p'), + (0x1D596, 'M', 'q'), + (0x1D597, 'M', 'r'), + (0x1D598, 'M', 's'), + (0x1D599, 'M', 't'), + (0x1D59A, 'M', 'u'), + (0x1D59B, 'M', 'v'), + (0x1D59C, 'M', 'w'), + (0x1D59D, 'M', 'x'), + (0x1D59E, 'M', 'y'), + (0x1D59F, 'M', 'z'), + (0x1D5A0, 'M', 'a'), + (0x1D5A1, 'M', 'b'), + (0x1D5A2, 'M', 'c'), + (0x1D5A3, 'M', 'd'), + (0x1D5A4, 'M', 'e'), + (0x1D5A5, 'M', 'f'), + (0x1D5A6, 'M', 'g'), + (0x1D5A7, 'M', 'h'), + (0x1D5A8, 'M', 'i'), + (0x1D5A9, 'M', 'j'), + (0x1D5AA, 'M', 'k'), + (0x1D5AB, 'M', 'l'), + (0x1D5AC, 'M', 'm'), + (0x1D5AD, 'M', 'n'), + (0x1D5AE, 'M', 'o'), + (0x1D5AF, 'M', 'p'), + (0x1D5B0, 'M', 'q'), + (0x1D5B1, 'M', 'r'), + (0x1D5B2, 'M', 's'), + (0x1D5B3, 'M', 't'), + (0x1D5B4, 'M', 'u'), + (0x1D5B5, 'M', 'v'), + (0x1D5B6, 'M', 'w'), + (0x1D5B7, 'M', 'x'), + (0x1D5B8, 'M', 'y'), + (0x1D5B9, 'M', 'z'), + (0x1D5BA, 'M', 'a'), + (0x1D5BB, 'M', 'b'), + (0x1D5BC, 'M', 'c'), + (0x1D5BD, 'M', 'd'), + (0x1D5BE, 'M', 'e'), + (0x1D5BF, 'M', 'f'), + (0x1D5C0, 'M', 'g'), + (0x1D5C1, 'M', 'h'), + (0x1D5C2, 'M', 'i'), + (0x1D5C3, 'M', 'j'), + (0x1D5C4, 'M', 'k'), + (0x1D5C5, 'M', 'l'), + (0x1D5C6, 'M', 'm'), + (0x1D5C7, 'M', 'n'), + (0x1D5C8, 'M', 'o'), + (0x1D5C9, 'M', 'p'), + (0x1D5CA, 'M', 'q'), + (0x1D5CB, 'M', 'r'), + (0x1D5CC, 'M', 's'), + (0x1D5CD, 'M', 't'), + (0x1D5CE, 'M', 'u'), + (0x1D5CF, 'M', 'v'), + (0x1D5D0, 'M', 'w'), + (0x1D5D1, 'M', 'x'), + (0x1D5D2, 'M', 'y'), + (0x1D5D3, 'M', 'z'), + (0x1D5D4, 'M', 'a'), + (0x1D5D5, 'M', 'b'), + (0x1D5D6, 'M', 'c'), + (0x1D5D7, 'M', 'd'), + (0x1D5D8, 'M', 'e'), + (0x1D5D9, 'M', 'f'), + (0x1D5DA, 'M', 'g'), + (0x1D5DB, 'M', 'h'), + (0x1D5DC, 'M', 'i'), + (0x1D5DD, 'M', 'j'), + (0x1D5DE, 'M', 'k'), + (0x1D5DF, 'M', 'l'), + (0x1D5E0, 'M', 'm'), + (0x1D5E1, 'M', 'n'), + (0x1D5E2, 'M', 'o'), + (0x1D5E3, 'M', 'p'), + (0x1D5E4, 'M', 'q'), + (0x1D5E5, 'M', 'r'), + (0x1D5E6, 'M', 's'), + (0x1D5E7, 'M', 't'), ] def _seg_64(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D601, 'M', u't'), - (0x1D602, 'M', u'u'), - (0x1D603, 'M', u'v'), - (0x1D604, 'M', u'w'), - (0x1D605, 'M', u'x'), - (0x1D606, 'M', u'y'), - (0x1D607, 'M', u'z'), - (0x1D608, 'M', u'a'), - (0x1D609, 'M', u'b'), - (0x1D60A, 'M', u'c'), - (0x1D60B, 'M', u'd'), - (0x1D60C, 'M', u'e'), - (0x1D60D, 'M', u'f'), - (0x1D60E, 'M', u'g'), - (0x1D60F, 'M', u'h'), - (0x1D610, 'M', u'i'), - (0x1D611, 'M', u'j'), - (0x1D612, 'M', u'k'), - (0x1D613, 'M', u'l'), - (0x1D614, 'M', u'm'), - (0x1D615, 'M', u'n'), - (0x1D616, 'M', u'o'), - (0x1D617, 'M', u'p'), - (0x1D618, 'M', u'q'), - (0x1D619, 'M', u'r'), - (0x1D61A, 'M', u's'), - (0x1D61B, 'M', u't'), - (0x1D61C, 'M', u'u'), - (0x1D61D, 'M', u'v'), - (0x1D61E, 'M', u'w'), - (0x1D61F, 'M', u'x'), - (0x1D620, 'M', u'y'), - (0x1D621, 'M', u'z'), - (0x1D622, 'M', u'a'), - (0x1D623, 'M', u'b'), - (0x1D624, 'M', u'c'), - (0x1D625, 'M', u'd'), - (0x1D626, 'M', u'e'), - (0x1D627, 'M', u'f'), - (0x1D628, 'M', u'g'), - (0x1D629, 'M', u'h'), - (0x1D62A, 'M', u'i'), - (0x1D62B, 'M', u'j'), - (0x1D62C, 'M', u'k'), - (0x1D62D, 'M', u'l'), - (0x1D62E, 'M', u'm'), - (0x1D62F, 'M', u'n'), - (0x1D630, 'M', u'o'), - (0x1D631, 'M', u'p'), - (0x1D632, 'M', u'q'), - (0x1D633, 'M', u'r'), - (0x1D634, 'M', u's'), - (0x1D635, 'M', u't'), - (0x1D636, 'M', u'u'), - (0x1D637, 'M', u'v'), - (0x1D638, 'M', u'w'), - (0x1D639, 'M', u'x'), - (0x1D63A, 'M', u'y'), - (0x1D63B, 'M', u'z'), - (0x1D63C, 'M', u'a'), - (0x1D63D, 'M', u'b'), - (0x1D63E, 'M', u'c'), - (0x1D63F, 'M', u'd'), - (0x1D640, 'M', u'e'), - (0x1D641, 'M', u'f'), - (0x1D642, 'M', u'g'), - (0x1D643, 'M', u'h'), - (0x1D644, 'M', u'i'), - (0x1D645, 'M', u'j'), - (0x1D646, 'M', u'k'), - (0x1D647, 'M', u'l'), - (0x1D648, 'M', u'm'), - (0x1D649, 'M', u'n'), - (0x1D64A, 'M', u'o'), - (0x1D64B, 'M', u'p'), - (0x1D64C, 'M', u'q'), - (0x1D64D, 'M', u'r'), - (0x1D64E, 'M', u's'), - (0x1D64F, 'M', u't'), - (0x1D650, 'M', u'u'), - (0x1D651, 'M', u'v'), - (0x1D652, 'M', u'w'), - (0x1D653, 'M', u'x'), - (0x1D654, 'M', u'y'), - (0x1D655, 'M', u'z'), - (0x1D656, 'M', u'a'), - (0x1D657, 'M', u'b'), - (0x1D658, 'M', u'c'), - (0x1D659, 'M', u'd'), - (0x1D65A, 'M', u'e'), - (0x1D65B, 'M', u'f'), - (0x1D65C, 'M', u'g'), - (0x1D65D, 'M', u'h'), - (0x1D65E, 'M', u'i'), - (0x1D65F, 'M', u'j'), - (0x1D660, 'M', u'k'), - (0x1D661, 'M', u'l'), - (0x1D662, 'M', u'm'), - (0x1D663, 'M', u'n'), - (0x1D664, 'M', u'o'), + (0x1D5E8, 'M', 'u'), + (0x1D5E9, 'M', 'v'), + (0x1D5EA, 'M', 'w'), + (0x1D5EB, 'M', 'x'), + (0x1D5EC, 'M', 'y'), + (0x1D5ED, 'M', 'z'), + (0x1D5EE, 'M', 'a'), + (0x1D5EF, 'M', 'b'), + (0x1D5F0, 'M', 'c'), + (0x1D5F1, 'M', 'd'), + (0x1D5F2, 'M', 'e'), + (0x1D5F3, 'M', 'f'), + (0x1D5F4, 'M', 'g'), + (0x1D5F5, 'M', 'h'), + (0x1D5F6, 'M', 'i'), + (0x1D5F7, 'M', 'j'), + (0x1D5F8, 'M', 'k'), + (0x1D5F9, 'M', 'l'), + (0x1D5FA, 'M', 'm'), + (0x1D5FB, 'M', 'n'), + (0x1D5FC, 'M', 'o'), + (0x1D5FD, 'M', 'p'), + (0x1D5FE, 'M', 'q'), + (0x1D5FF, 'M', 'r'), + (0x1D600, 'M', 's'), + (0x1D601, 'M', 't'), + (0x1D602, 'M', 'u'), + (0x1D603, 'M', 'v'), + (0x1D604, 'M', 'w'), + (0x1D605, 'M', 'x'), + (0x1D606, 'M', 'y'), + (0x1D607, 'M', 'z'), + (0x1D608, 'M', 'a'), + (0x1D609, 'M', 'b'), + (0x1D60A, 'M', 'c'), + (0x1D60B, 'M', 'd'), + (0x1D60C, 'M', 'e'), + (0x1D60D, 'M', 'f'), + (0x1D60E, 'M', 'g'), + (0x1D60F, 'M', 'h'), + (0x1D610, 'M', 'i'), + (0x1D611, 'M', 'j'), + (0x1D612, 'M', 'k'), + (0x1D613, 'M', 'l'), + (0x1D614, 'M', 'm'), + (0x1D615, 'M', 'n'), + (0x1D616, 'M', 'o'), + (0x1D617, 'M', 'p'), + (0x1D618, 'M', 'q'), + (0x1D619, 'M', 'r'), + (0x1D61A, 'M', 's'), + (0x1D61B, 'M', 't'), + (0x1D61C, 'M', 'u'), + (0x1D61D, 'M', 'v'), + (0x1D61E, 'M', 'w'), + (0x1D61F, 'M', 'x'), + (0x1D620, 'M', 'y'), + (0x1D621, 'M', 'z'), + (0x1D622, 'M', 'a'), + (0x1D623, 'M', 'b'), + (0x1D624, 'M', 'c'), + (0x1D625, 'M', 'd'), + (0x1D626, 'M', 'e'), + (0x1D627, 'M', 'f'), + (0x1D628, 'M', 'g'), + (0x1D629, 'M', 'h'), + (0x1D62A, 'M', 'i'), + (0x1D62B, 'M', 'j'), + (0x1D62C, 'M', 'k'), + (0x1D62D, 'M', 'l'), + (0x1D62E, 'M', 'm'), + (0x1D62F, 'M', 'n'), + (0x1D630, 'M', 'o'), + (0x1D631, 'M', 'p'), + (0x1D632, 'M', 'q'), + (0x1D633, 'M', 'r'), + (0x1D634, 'M', 's'), + (0x1D635, 'M', 't'), + (0x1D636, 'M', 'u'), + (0x1D637, 'M', 'v'), + (0x1D638, 'M', 'w'), + (0x1D639, 'M', 'x'), + (0x1D63A, 'M', 'y'), + (0x1D63B, 'M', 'z'), + (0x1D63C, 'M', 'a'), + (0x1D63D, 'M', 'b'), + (0x1D63E, 'M', 'c'), + (0x1D63F, 'M', 'd'), + (0x1D640, 'M', 'e'), + (0x1D641, 'M', 'f'), + (0x1D642, 'M', 'g'), + (0x1D643, 'M', 'h'), + (0x1D644, 'M', 'i'), + (0x1D645, 'M', 'j'), + (0x1D646, 'M', 'k'), + (0x1D647, 'M', 'l'), + (0x1D648, 'M', 'm'), + (0x1D649, 'M', 'n'), + (0x1D64A, 'M', 'o'), + (0x1D64B, 'M', 'p'), ] def _seg_65(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D665, 'M', u'p'), - (0x1D666, 'M', u'q'), - (0x1D667, 'M', u'r'), - (0x1D668, 'M', u's'), - (0x1D669, 'M', u't'), - (0x1D66A, 'M', u'u'), - (0x1D66B, 'M', u'v'), - (0x1D66C, 'M', u'w'), - (0x1D66D, 'M', u'x'), - (0x1D66E, 'M', u'y'), - (0x1D66F, 'M', u'z'), - (0x1D670, 'M', u'a'), - (0x1D671, 'M', u'b'), - (0x1D672, 'M', u'c'), - (0x1D673, 'M', u'd'), - (0x1D674, 'M', u'e'), - (0x1D675, 'M', u'f'), - (0x1D676, 'M', u'g'), - (0x1D677, 'M', u'h'), - (0x1D678, 'M', u'i'), - (0x1D679, 'M', u'j'), - (0x1D67A, 'M', u'k'), - (0x1D67B, 'M', u'l'), - (0x1D67C, 'M', u'm'), - (0x1D67D, 'M', u'n'), - (0x1D67E, 'M', u'o'), - (0x1D67F, 'M', u'p'), - (0x1D680, 'M', u'q'), - (0x1D681, 'M', u'r'), - (0x1D682, 'M', u's'), - (0x1D683, 'M', u't'), - (0x1D684, 'M', u'u'), - (0x1D685, 'M', u'v'), - (0x1D686, 'M', u'w'), - (0x1D687, 'M', u'x'), - (0x1D688, 'M', u'y'), - (0x1D689, 'M', u'z'), - (0x1D68A, 'M', u'a'), - (0x1D68B, 'M', u'b'), - (0x1D68C, 'M', u'c'), - (0x1D68D, 'M', u'd'), - (0x1D68E, 'M', u'e'), - (0x1D68F, 'M', u'f'), - (0x1D690, 'M', u'g'), - (0x1D691, 'M', u'h'), - (0x1D692, 'M', u'i'), - (0x1D693, 'M', u'j'), - (0x1D694, 'M', u'k'), - (0x1D695, 'M', u'l'), - (0x1D696, 'M', u'm'), - (0x1D697, 'M', u'n'), - (0x1D698, 'M', u'o'), - (0x1D699, 'M', u'p'), - (0x1D69A, 'M', u'q'), - (0x1D69B, 'M', u'r'), - (0x1D69C, 'M', u's'), - (0x1D69D, 'M', u't'), - (0x1D69E, 'M', u'u'), - (0x1D69F, 'M', u'v'), - (0x1D6A0, 'M', u'w'), - (0x1D6A1, 'M', u'x'), - (0x1D6A2, 'M', u'y'), - (0x1D6A3, 'M', u'z'), - (0x1D6A4, 'M', u'ı'), - (0x1D6A5, 'M', u'ȷ'), + (0x1D64C, 'M', 'q'), + (0x1D64D, 'M', 'r'), + (0x1D64E, 'M', 's'), + (0x1D64F, 'M', 't'), + (0x1D650, 'M', 'u'), + (0x1D651, 'M', 'v'), + (0x1D652, 'M', 'w'), + (0x1D653, 'M', 'x'), + (0x1D654, 'M', 'y'), + (0x1D655, 'M', 'z'), + (0x1D656, 'M', 'a'), + (0x1D657, 'M', 'b'), + (0x1D658, 'M', 'c'), + (0x1D659, 'M', 'd'), + (0x1D65A, 'M', 'e'), + (0x1D65B, 'M', 'f'), + (0x1D65C, 'M', 'g'), + (0x1D65D, 'M', 'h'), + (0x1D65E, 'M', 'i'), + (0x1D65F, 'M', 'j'), + (0x1D660, 'M', 'k'), + (0x1D661, 'M', 'l'), + (0x1D662, 'M', 'm'), + (0x1D663, 'M', 'n'), + (0x1D664, 'M', 'o'), + (0x1D665, 'M', 'p'), + (0x1D666, 'M', 'q'), + (0x1D667, 'M', 'r'), + (0x1D668, 'M', 's'), + (0x1D669, 'M', 't'), + (0x1D66A, 'M', 'u'), + (0x1D66B, 'M', 'v'), + (0x1D66C, 'M', 'w'), + (0x1D66D, 'M', 'x'), + (0x1D66E, 'M', 'y'), + (0x1D66F, 'M', 'z'), + (0x1D670, 'M', 'a'), + (0x1D671, 'M', 'b'), + (0x1D672, 'M', 'c'), + (0x1D673, 'M', 'd'), + (0x1D674, 'M', 'e'), + (0x1D675, 'M', 'f'), + (0x1D676, 'M', 'g'), + (0x1D677, 'M', 'h'), + (0x1D678, 'M', 'i'), + (0x1D679, 'M', 'j'), + (0x1D67A, 'M', 'k'), + (0x1D67B, 'M', 'l'), + (0x1D67C, 'M', 'm'), + (0x1D67D, 'M', 'n'), + (0x1D67E, 'M', 'o'), + (0x1D67F, 'M', 'p'), + (0x1D680, 'M', 'q'), + (0x1D681, 'M', 'r'), + (0x1D682, 'M', 's'), + (0x1D683, 'M', 't'), + (0x1D684, 'M', 'u'), + (0x1D685, 'M', 'v'), + (0x1D686, 'M', 'w'), + (0x1D687, 'M', 'x'), + (0x1D688, 'M', 'y'), + (0x1D689, 'M', 'z'), + (0x1D68A, 'M', 'a'), + (0x1D68B, 'M', 'b'), + (0x1D68C, 'M', 'c'), + (0x1D68D, 'M', 'd'), + (0x1D68E, 'M', 'e'), + (0x1D68F, 'M', 'f'), + (0x1D690, 'M', 'g'), + (0x1D691, 'M', 'h'), + (0x1D692, 'M', 'i'), + (0x1D693, 'M', 'j'), + (0x1D694, 'M', 'k'), + (0x1D695, 'M', 'l'), + (0x1D696, 'M', 'm'), + (0x1D697, 'M', 'n'), + (0x1D698, 'M', 'o'), + (0x1D699, 'M', 'p'), + (0x1D69A, 'M', 'q'), + (0x1D69B, 'M', 'r'), + (0x1D69C, 'M', 's'), + (0x1D69D, 'M', 't'), + (0x1D69E, 'M', 'u'), + (0x1D69F, 'M', 'v'), + (0x1D6A0, 'M', 'w'), + (0x1D6A1, 'M', 'x'), + (0x1D6A2, 'M', 'y'), + (0x1D6A3, 'M', 'z'), + (0x1D6A4, 'M', 'ı'), + (0x1D6A5, 'M', 'ȷ'), (0x1D6A6, 'X'), - (0x1D6A8, 'M', u'α'), - (0x1D6A9, 'M', u'β'), - (0x1D6AA, 'M', u'γ'), - (0x1D6AB, 'M', u'δ'), - (0x1D6AC, 'M', u'ε'), - (0x1D6AD, 'M', u'ζ'), - (0x1D6AE, 'M', u'η'), - (0x1D6AF, 'M', u'θ'), - (0x1D6B0, 'M', u'ι'), - (0x1D6B1, 'M', u'κ'), - (0x1D6B2, 'M', u'λ'), - (0x1D6B3, 'M', u'μ'), - (0x1D6B4, 'M', u'ν'), - (0x1D6B5, 'M', u'ξ'), - (0x1D6B6, 'M', u'ο'), - (0x1D6B7, 'M', u'π'), - (0x1D6B8, 'M', u'ρ'), - (0x1D6B9, 'M', u'θ'), - (0x1D6BA, 'M', u'σ'), - (0x1D6BB, 'M', u'τ'), - (0x1D6BC, 'M', u'υ'), - (0x1D6BD, 'M', u'φ'), - (0x1D6BE, 'M', u'χ'), - (0x1D6BF, 'M', u'ψ'), - (0x1D6C0, 'M', u'ω'), - (0x1D6C1, 'M', u'∇'), - (0x1D6C2, 'M', u'α'), - (0x1D6C3, 'M', u'β'), - (0x1D6C4, 'M', u'γ'), - (0x1D6C5, 'M', u'δ'), - (0x1D6C6, 'M', u'ε'), - (0x1D6C7, 'M', u'ζ'), - (0x1D6C8, 'M', u'η'), - (0x1D6C9, 'M', u'θ'), + (0x1D6A8, 'M', 'α'), + (0x1D6A9, 'M', 'β'), + (0x1D6AA, 'M', 'γ'), + (0x1D6AB, 'M', 'δ'), + (0x1D6AC, 'M', 'ε'), + (0x1D6AD, 'M', 'ζ'), + (0x1D6AE, 'M', 'η'), + (0x1D6AF, 'M', 'θ'), + (0x1D6B0, 'M', 'ι'), ] def _seg_66(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D6CA, 'M', u'ι'), - (0x1D6CB, 'M', u'κ'), - (0x1D6CC, 'M', u'λ'), - (0x1D6CD, 'M', u'μ'), - (0x1D6CE, 'M', u'ν'), - (0x1D6CF, 'M', u'ξ'), - (0x1D6D0, 'M', u'ο'), - (0x1D6D1, 'M', u'π'), - (0x1D6D2, 'M', u'ρ'), - (0x1D6D3, 'M', u'σ'), - (0x1D6D5, 'M', u'τ'), - (0x1D6D6, 'M', u'υ'), - (0x1D6D7, 'M', u'φ'), - (0x1D6D8, 'M', u'χ'), - (0x1D6D9, 'M', u'ψ'), - (0x1D6DA, 'M', u'ω'), - (0x1D6DB, 'M', u'∂'), - (0x1D6DC, 'M', u'ε'), - (0x1D6DD, 'M', u'θ'), - (0x1D6DE, 'M', u'κ'), - (0x1D6DF, 'M', u'φ'), - (0x1D6E0, 'M', u'ρ'), - (0x1D6E1, 'M', u'π'), - (0x1D6E2, 'M', u'α'), - (0x1D6E3, 'M', u'β'), - (0x1D6E4, 'M', u'γ'), - (0x1D6E5, 'M', u'δ'), - (0x1D6E6, 'M', u'ε'), - (0x1D6E7, 'M', u'ζ'), - (0x1D6E8, 'M', u'η'), - (0x1D6E9, 'M', u'θ'), - (0x1D6EA, 'M', u'ι'), - (0x1D6EB, 'M', u'κ'), - (0x1D6EC, 'M', u'λ'), - (0x1D6ED, 'M', u'μ'), - (0x1D6EE, 'M', u'ν'), - (0x1D6EF, 'M', u'ξ'), - (0x1D6F0, 'M', u'ο'), - (0x1D6F1, 'M', u'π'), - (0x1D6F2, 'M', u'ρ'), - (0x1D6F3, 'M', u'θ'), - (0x1D6F4, 'M', u'σ'), - (0x1D6F5, 'M', u'τ'), - (0x1D6F6, 'M', u'υ'), - (0x1D6F7, 'M', u'φ'), - (0x1D6F8, 'M', u'χ'), - (0x1D6F9, 'M', u'ψ'), - (0x1D6FA, 'M', u'ω'), - (0x1D6FB, 'M', u'∇'), - (0x1D6FC, 'M', u'α'), - (0x1D6FD, 'M', u'β'), - (0x1D6FE, 'M', u'γ'), - (0x1D6FF, 'M', u'δ'), - (0x1D700, 'M', u'ε'), - (0x1D701, 'M', u'ζ'), - (0x1D702, 'M', u'η'), - (0x1D703, 'M', u'θ'), - (0x1D704, 'M', u'ι'), - (0x1D705, 'M', u'κ'), - (0x1D706, 'M', u'λ'), - (0x1D707, 'M', u'μ'), - (0x1D708, 'M', u'ν'), - (0x1D709, 'M', u'ξ'), - (0x1D70A, 'M', u'ο'), - (0x1D70B, 'M', u'π'), - (0x1D70C, 'M', u'ρ'), - (0x1D70D, 'M', u'σ'), - (0x1D70F, 'M', u'τ'), - (0x1D710, 'M', u'υ'), - (0x1D711, 'M', u'φ'), - (0x1D712, 'M', u'χ'), - (0x1D713, 'M', u'ψ'), - (0x1D714, 'M', u'ω'), - (0x1D715, 'M', u'∂'), - (0x1D716, 'M', u'ε'), - (0x1D717, 'M', u'θ'), - (0x1D718, 'M', u'κ'), - (0x1D719, 'M', u'φ'), - (0x1D71A, 'M', u'ρ'), - (0x1D71B, 'M', u'π'), - (0x1D71C, 'M', u'α'), - (0x1D71D, 'M', u'β'), - (0x1D71E, 'M', u'γ'), - (0x1D71F, 'M', u'δ'), - (0x1D720, 'M', u'ε'), - (0x1D721, 'M', u'ζ'), - (0x1D722, 'M', u'η'), - (0x1D723, 'M', u'θ'), - (0x1D724, 'M', u'ι'), - (0x1D725, 'M', u'κ'), - (0x1D726, 'M', u'λ'), - (0x1D727, 'M', u'μ'), - (0x1D728, 'M', u'ν'), - (0x1D729, 'M', u'ξ'), - (0x1D72A, 'M', u'ο'), - (0x1D72B, 'M', u'π'), - (0x1D72C, 'M', u'ρ'), - (0x1D72D, 'M', u'θ'), - (0x1D72E, 'M', u'σ'), - (0x1D72F, 'M', u'τ'), + (0x1D6B1, 'M', 'κ'), + (0x1D6B2, 'M', 'λ'), + (0x1D6B3, 'M', 'μ'), + (0x1D6B4, 'M', 'ν'), + (0x1D6B5, 'M', 'ξ'), + (0x1D6B6, 'M', 'ο'), + (0x1D6B7, 'M', 'π'), + (0x1D6B8, 'M', 'ρ'), + (0x1D6B9, 'M', 'θ'), + (0x1D6BA, 'M', 'σ'), + (0x1D6BB, 'M', 'τ'), + (0x1D6BC, 'M', 'υ'), + (0x1D6BD, 'M', 'φ'), + (0x1D6BE, 'M', 'χ'), + (0x1D6BF, 'M', 'ψ'), + (0x1D6C0, 'M', 'ω'), + (0x1D6C1, 'M', '∇'), + (0x1D6C2, 'M', 'α'), + (0x1D6C3, 'M', 'β'), + (0x1D6C4, 'M', 'γ'), + (0x1D6C5, 'M', 'δ'), + (0x1D6C6, 'M', 'ε'), + (0x1D6C7, 'M', 'ζ'), + (0x1D6C8, 'M', 'η'), + (0x1D6C9, 'M', 'θ'), + (0x1D6CA, 'M', 'ι'), + (0x1D6CB, 'M', 'κ'), + (0x1D6CC, 'M', 'λ'), + (0x1D6CD, 'M', 'μ'), + (0x1D6CE, 'M', 'ν'), + (0x1D6CF, 'M', 'ξ'), + (0x1D6D0, 'M', 'ο'), + (0x1D6D1, 'M', 'π'), + (0x1D6D2, 'M', 'ρ'), + (0x1D6D3, 'M', 'σ'), + (0x1D6D5, 'M', 'τ'), + (0x1D6D6, 'M', 'υ'), + (0x1D6D7, 'M', 'φ'), + (0x1D6D8, 'M', 'χ'), + (0x1D6D9, 'M', 'ψ'), + (0x1D6DA, 'M', 'ω'), + (0x1D6DB, 'M', '∂'), + (0x1D6DC, 'M', 'ε'), + (0x1D6DD, 'M', 'θ'), + (0x1D6DE, 'M', 'κ'), + (0x1D6DF, 'M', 'φ'), + (0x1D6E0, 'M', 'ρ'), + (0x1D6E1, 'M', 'π'), + (0x1D6E2, 'M', 'α'), + (0x1D6E3, 'M', 'β'), + (0x1D6E4, 'M', 'γ'), + (0x1D6E5, 'M', 'δ'), + (0x1D6E6, 'M', 'ε'), + (0x1D6E7, 'M', 'ζ'), + (0x1D6E8, 'M', 'η'), + (0x1D6E9, 'M', 'θ'), + (0x1D6EA, 'M', 'ι'), + (0x1D6EB, 'M', 'κ'), + (0x1D6EC, 'M', 'λ'), + (0x1D6ED, 'M', 'μ'), + (0x1D6EE, 'M', 'ν'), + (0x1D6EF, 'M', 'ξ'), + (0x1D6F0, 'M', 'ο'), + (0x1D6F1, 'M', 'π'), + (0x1D6F2, 'M', 'ρ'), + (0x1D6F3, 'M', 'θ'), + (0x1D6F4, 'M', 'σ'), + (0x1D6F5, 'M', 'τ'), + (0x1D6F6, 'M', 'υ'), + (0x1D6F7, 'M', 'φ'), + (0x1D6F8, 'M', 'χ'), + (0x1D6F9, 'M', 'ψ'), + (0x1D6FA, 'M', 'ω'), + (0x1D6FB, 'M', '∇'), + (0x1D6FC, 'M', 'α'), + (0x1D6FD, 'M', 'β'), + (0x1D6FE, 'M', 'γ'), + (0x1D6FF, 'M', 'δ'), + (0x1D700, 'M', 'ε'), + (0x1D701, 'M', 'ζ'), + (0x1D702, 'M', 'η'), + (0x1D703, 'M', 'θ'), + (0x1D704, 'M', 'ι'), + (0x1D705, 'M', 'κ'), + (0x1D706, 'M', 'λ'), + (0x1D707, 'M', 'μ'), + (0x1D708, 'M', 'ν'), + (0x1D709, 'M', 'ξ'), + (0x1D70A, 'M', 'ο'), + (0x1D70B, 'M', 'π'), + (0x1D70C, 'M', 'ρ'), + (0x1D70D, 'M', 'σ'), + (0x1D70F, 'M', 'τ'), + (0x1D710, 'M', 'υ'), + (0x1D711, 'M', 'φ'), + (0x1D712, 'M', 'χ'), + (0x1D713, 'M', 'ψ'), + (0x1D714, 'M', 'ω'), + (0x1D715, 'M', '∂'), + (0x1D716, 'M', 'ε'), ] def _seg_67(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D730, 'M', u'υ'), - (0x1D731, 'M', u'φ'), - (0x1D732, 'M', u'χ'), - (0x1D733, 'M', u'ψ'), - (0x1D734, 'M', u'ω'), - (0x1D735, 'M', u'∇'), - (0x1D736, 'M', u'α'), - (0x1D737, 'M', u'β'), - (0x1D738, 'M', u'γ'), - (0x1D739, 'M', u'δ'), - (0x1D73A, 'M', u'ε'), - (0x1D73B, 'M', u'ζ'), - (0x1D73C, 'M', u'η'), - (0x1D73D, 'M', u'θ'), - (0x1D73E, 'M', u'ι'), - (0x1D73F, 'M', u'κ'), - (0x1D740, 'M', u'λ'), - (0x1D741, 'M', u'μ'), - (0x1D742, 'M', u'ν'), - (0x1D743, 'M', u'ξ'), - (0x1D744, 'M', u'ο'), - (0x1D745, 'M', u'π'), - (0x1D746, 'M', u'ρ'), - (0x1D747, 'M', u'σ'), - (0x1D749, 'M', u'τ'), - (0x1D74A, 'M', u'υ'), - (0x1D74B, 'M', u'φ'), - (0x1D74C, 'M', u'χ'), - (0x1D74D, 'M', u'ψ'), - (0x1D74E, 'M', u'ω'), - (0x1D74F, 'M', u'∂'), - (0x1D750, 'M', u'ε'), - (0x1D751, 'M', u'θ'), - (0x1D752, 'M', u'κ'), - (0x1D753, 'M', u'φ'), - (0x1D754, 'M', u'ρ'), - (0x1D755, 'M', u'π'), - (0x1D756, 'M', u'α'), - (0x1D757, 'M', u'β'), - (0x1D758, 'M', u'γ'), - (0x1D759, 'M', u'δ'), - (0x1D75A, 'M', u'ε'), - (0x1D75B, 'M', u'ζ'), - (0x1D75C, 'M', u'η'), - (0x1D75D, 'M', u'θ'), - (0x1D75E, 'M', u'ι'), - (0x1D75F, 'M', u'κ'), - (0x1D760, 'M', u'λ'), - (0x1D761, 'M', u'μ'), - (0x1D762, 'M', u'ν'), - (0x1D763, 'M', u'ξ'), - (0x1D764, 'M', u'ο'), - (0x1D765, 'M', u'π'), - (0x1D766, 'M', u'ρ'), - (0x1D767, 'M', u'θ'), - (0x1D768, 'M', u'σ'), - (0x1D769, 'M', u'τ'), - (0x1D76A, 'M', u'υ'), - (0x1D76B, 'M', u'φ'), - (0x1D76C, 'M', u'χ'), - (0x1D76D, 'M', u'ψ'), - (0x1D76E, 'M', u'ω'), - (0x1D76F, 'M', u'∇'), - (0x1D770, 'M', u'α'), - (0x1D771, 'M', u'β'), - (0x1D772, 'M', u'γ'), - (0x1D773, 'M', u'δ'), - (0x1D774, 'M', u'ε'), - (0x1D775, 'M', u'ζ'), - (0x1D776, 'M', u'η'), - (0x1D777, 'M', u'θ'), - (0x1D778, 'M', u'ι'), - (0x1D779, 'M', u'κ'), - (0x1D77A, 'M', u'λ'), - (0x1D77B, 'M', u'μ'), - (0x1D77C, 'M', u'ν'), - (0x1D77D, 'M', u'ξ'), - (0x1D77E, 'M', u'ο'), - (0x1D77F, 'M', u'π'), - (0x1D780, 'M', u'ρ'), - (0x1D781, 'M', u'σ'), - (0x1D783, 'M', u'τ'), - (0x1D784, 'M', u'υ'), - (0x1D785, 'M', u'φ'), - (0x1D786, 'M', u'χ'), - (0x1D787, 'M', u'ψ'), - (0x1D788, 'M', u'ω'), - (0x1D789, 'M', u'∂'), - (0x1D78A, 'M', u'ε'), - (0x1D78B, 'M', u'θ'), - (0x1D78C, 'M', u'κ'), - (0x1D78D, 'M', u'φ'), - (0x1D78E, 'M', u'ρ'), - (0x1D78F, 'M', u'π'), - (0x1D790, 'M', u'α'), - (0x1D791, 'M', u'β'), - (0x1D792, 'M', u'γ'), - (0x1D793, 'M', u'δ'), - (0x1D794, 'M', u'ε'), - (0x1D795, 'M', u'ζ'), + (0x1D717, 'M', 'θ'), + (0x1D718, 'M', 'κ'), + (0x1D719, 'M', 'φ'), + (0x1D71A, 'M', 'ρ'), + (0x1D71B, 'M', 'π'), + (0x1D71C, 'M', 'α'), + (0x1D71D, 'M', 'β'), + (0x1D71E, 'M', 'γ'), + (0x1D71F, 'M', 'δ'), + (0x1D720, 'M', 'ε'), + (0x1D721, 'M', 'ζ'), + (0x1D722, 'M', 'η'), + (0x1D723, 'M', 'θ'), + (0x1D724, 'M', 'ι'), + (0x1D725, 'M', 'κ'), + (0x1D726, 'M', 'λ'), + (0x1D727, 'M', 'μ'), + (0x1D728, 'M', 'ν'), + (0x1D729, 'M', 'ξ'), + (0x1D72A, 'M', 'ο'), + (0x1D72B, 'M', 'π'), + (0x1D72C, 'M', 'ρ'), + (0x1D72D, 'M', 'θ'), + (0x1D72E, 'M', 'σ'), + (0x1D72F, 'M', 'τ'), + (0x1D730, 'M', 'υ'), + (0x1D731, 'M', 'φ'), + (0x1D732, 'M', 'χ'), + (0x1D733, 'M', 'ψ'), + (0x1D734, 'M', 'ω'), + (0x1D735, 'M', '∇'), + (0x1D736, 'M', 'α'), + (0x1D737, 'M', 'β'), + (0x1D738, 'M', 'γ'), + (0x1D739, 'M', 'δ'), + (0x1D73A, 'M', 'ε'), + (0x1D73B, 'M', 'ζ'), + (0x1D73C, 'M', 'η'), + (0x1D73D, 'M', 'θ'), + (0x1D73E, 'M', 'ι'), + (0x1D73F, 'M', 'κ'), + (0x1D740, 'M', 'λ'), + (0x1D741, 'M', 'μ'), + (0x1D742, 'M', 'ν'), + (0x1D743, 'M', 'ξ'), + (0x1D744, 'M', 'ο'), + (0x1D745, 'M', 'π'), + (0x1D746, 'M', 'ρ'), + (0x1D747, 'M', 'σ'), + (0x1D749, 'M', 'τ'), + (0x1D74A, 'M', 'υ'), + (0x1D74B, 'M', 'φ'), + (0x1D74C, 'M', 'χ'), + (0x1D74D, 'M', 'ψ'), + (0x1D74E, 'M', 'ω'), + (0x1D74F, 'M', '∂'), + (0x1D750, 'M', 'ε'), + (0x1D751, 'M', 'θ'), + (0x1D752, 'M', 'κ'), + (0x1D753, 'M', 'φ'), + (0x1D754, 'M', 'ρ'), + (0x1D755, 'M', 'π'), + (0x1D756, 'M', 'α'), + (0x1D757, 'M', 'β'), + (0x1D758, 'M', 'γ'), + (0x1D759, 'M', 'δ'), + (0x1D75A, 'M', 'ε'), + (0x1D75B, 'M', 'ζ'), + (0x1D75C, 'M', 'η'), + (0x1D75D, 'M', 'θ'), + (0x1D75E, 'M', 'ι'), + (0x1D75F, 'M', 'κ'), + (0x1D760, 'M', 'λ'), + (0x1D761, 'M', 'μ'), + (0x1D762, 'M', 'ν'), + (0x1D763, 'M', 'ξ'), + (0x1D764, 'M', 'ο'), + (0x1D765, 'M', 'π'), + (0x1D766, 'M', 'ρ'), + (0x1D767, 'M', 'θ'), + (0x1D768, 'M', 'σ'), + (0x1D769, 'M', 'τ'), + (0x1D76A, 'M', 'υ'), + (0x1D76B, 'M', 'φ'), + (0x1D76C, 'M', 'χ'), + (0x1D76D, 'M', 'ψ'), + (0x1D76E, 'M', 'ω'), + (0x1D76F, 'M', '∇'), + (0x1D770, 'M', 'α'), + (0x1D771, 'M', 'β'), + (0x1D772, 'M', 'γ'), + (0x1D773, 'M', 'δ'), + (0x1D774, 'M', 'ε'), + (0x1D775, 'M', 'ζ'), + (0x1D776, 'M', 'η'), + (0x1D777, 'M', 'θ'), + (0x1D778, 'M', 'ι'), + (0x1D779, 'M', 'κ'), + (0x1D77A, 'M', 'λ'), + (0x1D77B, 'M', 'μ'), ] def _seg_68(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D796, 'M', u'η'), - (0x1D797, 'M', u'θ'), - (0x1D798, 'M', u'ι'), - (0x1D799, 'M', u'κ'), - (0x1D79A, 'M', u'λ'), - (0x1D79B, 'M', u'μ'), - (0x1D79C, 'M', u'ν'), - (0x1D79D, 'M', u'ξ'), - (0x1D79E, 'M', u'ο'), - (0x1D79F, 'M', u'π'), - (0x1D7A0, 'M', u'ρ'), - (0x1D7A1, 'M', u'θ'), - (0x1D7A2, 'M', u'σ'), - (0x1D7A3, 'M', u'τ'), - (0x1D7A4, 'M', u'υ'), - (0x1D7A5, 'M', u'φ'), - (0x1D7A6, 'M', u'χ'), - (0x1D7A7, 'M', u'ψ'), - (0x1D7A8, 'M', u'ω'), - (0x1D7A9, 'M', u'∇'), - (0x1D7AA, 'M', u'α'), - (0x1D7AB, 'M', u'β'), - (0x1D7AC, 'M', u'γ'), - (0x1D7AD, 'M', u'δ'), - (0x1D7AE, 'M', u'ε'), - (0x1D7AF, 'M', u'ζ'), - (0x1D7B0, 'M', u'η'), - (0x1D7B1, 'M', u'θ'), - (0x1D7B2, 'M', u'ι'), - (0x1D7B3, 'M', u'κ'), - (0x1D7B4, 'M', u'λ'), - (0x1D7B5, 'M', u'μ'), - (0x1D7B6, 'M', u'ν'), - (0x1D7B7, 'M', u'ξ'), - (0x1D7B8, 'M', u'ο'), - (0x1D7B9, 'M', u'π'), - (0x1D7BA, 'M', u'ρ'), - (0x1D7BB, 'M', u'σ'), - (0x1D7BD, 'M', u'τ'), - (0x1D7BE, 'M', u'υ'), - (0x1D7BF, 'M', u'φ'), - (0x1D7C0, 'M', u'χ'), - (0x1D7C1, 'M', u'ψ'), - (0x1D7C2, 'M', u'ω'), - (0x1D7C3, 'M', u'∂'), - (0x1D7C4, 'M', u'ε'), - (0x1D7C5, 'M', u'θ'), - (0x1D7C6, 'M', u'κ'), - (0x1D7C7, 'M', u'φ'), - (0x1D7C8, 'M', u'ρ'), - (0x1D7C9, 'M', u'π'), - (0x1D7CA, 'M', u'ϝ'), + (0x1D77C, 'M', 'ν'), + (0x1D77D, 'M', 'ξ'), + (0x1D77E, 'M', 'ο'), + (0x1D77F, 'M', 'π'), + (0x1D780, 'M', 'ρ'), + (0x1D781, 'M', 'σ'), + (0x1D783, 'M', 'τ'), + (0x1D784, 'M', 'υ'), + (0x1D785, 'M', 'φ'), + (0x1D786, 'M', 'χ'), + (0x1D787, 'M', 'ψ'), + (0x1D788, 'M', 'ω'), + (0x1D789, 'M', '∂'), + (0x1D78A, 'M', 'ε'), + (0x1D78B, 'M', 'θ'), + (0x1D78C, 'M', 'κ'), + (0x1D78D, 'M', 'φ'), + (0x1D78E, 'M', 'ρ'), + (0x1D78F, 'M', 'π'), + (0x1D790, 'M', 'α'), + (0x1D791, 'M', 'β'), + (0x1D792, 'M', 'γ'), + (0x1D793, 'M', 'δ'), + (0x1D794, 'M', 'ε'), + (0x1D795, 'M', 'ζ'), + (0x1D796, 'M', 'η'), + (0x1D797, 'M', 'θ'), + (0x1D798, 'M', 'ι'), + (0x1D799, 'M', 'κ'), + (0x1D79A, 'M', 'λ'), + (0x1D79B, 'M', 'μ'), + (0x1D79C, 'M', 'ν'), + (0x1D79D, 'M', 'ξ'), + (0x1D79E, 'M', 'ο'), + (0x1D79F, 'M', 'π'), + (0x1D7A0, 'M', 'ρ'), + (0x1D7A1, 'M', 'θ'), + (0x1D7A2, 'M', 'σ'), + (0x1D7A3, 'M', 'τ'), + (0x1D7A4, 'M', 'υ'), + (0x1D7A5, 'M', 'φ'), + (0x1D7A6, 'M', 'χ'), + (0x1D7A7, 'M', 'ψ'), + (0x1D7A8, 'M', 'ω'), + (0x1D7A9, 'M', '∇'), + (0x1D7AA, 'M', 'α'), + (0x1D7AB, 'M', 'β'), + (0x1D7AC, 'M', 'γ'), + (0x1D7AD, 'M', 'δ'), + (0x1D7AE, 'M', 'ε'), + (0x1D7AF, 'M', 'ζ'), + (0x1D7B0, 'M', 'η'), + (0x1D7B1, 'M', 'θ'), + (0x1D7B2, 'M', 'ι'), + (0x1D7B3, 'M', 'κ'), + (0x1D7B4, 'M', 'λ'), + (0x1D7B5, 'M', 'μ'), + (0x1D7B6, 'M', 'ν'), + (0x1D7B7, 'M', 'ξ'), + (0x1D7B8, 'M', 'ο'), + (0x1D7B9, 'M', 'π'), + (0x1D7BA, 'M', 'ρ'), + (0x1D7BB, 'M', 'σ'), + (0x1D7BD, 'M', 'τ'), + (0x1D7BE, 'M', 'υ'), + (0x1D7BF, 'M', 'φ'), + (0x1D7C0, 'M', 'χ'), + (0x1D7C1, 'M', 'ψ'), + (0x1D7C2, 'M', 'ω'), + (0x1D7C3, 'M', '∂'), + (0x1D7C4, 'M', 'ε'), + (0x1D7C5, 'M', 'θ'), + (0x1D7C6, 'M', 'κ'), + (0x1D7C7, 'M', 'φ'), + (0x1D7C8, 'M', 'ρ'), + (0x1D7C9, 'M', 'π'), + (0x1D7CA, 'M', 'ϝ'), (0x1D7CC, 'X'), - (0x1D7CE, 'M', u'0'), - (0x1D7CF, 'M', u'1'), - (0x1D7D0, 'M', u'2'), - (0x1D7D1, 'M', u'3'), - (0x1D7D2, 'M', u'4'), - (0x1D7D3, 'M', u'5'), - (0x1D7D4, 'M', u'6'), - (0x1D7D5, 'M', u'7'), - (0x1D7D6, 'M', u'8'), - (0x1D7D7, 'M', u'9'), - (0x1D7D8, 'M', u'0'), - (0x1D7D9, 'M', u'1'), - (0x1D7DA, 'M', u'2'), - (0x1D7DB, 'M', u'3'), - (0x1D7DC, 'M', u'4'), - (0x1D7DD, 'M', u'5'), - (0x1D7DE, 'M', u'6'), - (0x1D7DF, 'M', u'7'), - (0x1D7E0, 'M', u'8'), - (0x1D7E1, 'M', u'9'), - (0x1D7E2, 'M', u'0'), - (0x1D7E3, 'M', u'1'), - (0x1D7E4, 'M', u'2'), - (0x1D7E5, 'M', u'3'), - (0x1D7E6, 'M', u'4'), - (0x1D7E7, 'M', u'5'), - (0x1D7E8, 'M', u'6'), - (0x1D7E9, 'M', u'7'), - (0x1D7EA, 'M', u'8'), - (0x1D7EB, 'M', u'9'), - (0x1D7EC, 'M', u'0'), - (0x1D7ED, 'M', u'1'), - (0x1D7EE, 'M', u'2'), - (0x1D7EF, 'M', u'3'), - (0x1D7F0, 'M', u'4'), - (0x1D7F1, 'M', u'5'), - (0x1D7F2, 'M', u'6'), - (0x1D7F3, 'M', u'7'), - (0x1D7F4, 'M', u'8'), - (0x1D7F5, 'M', u'9'), - (0x1D7F6, 'M', u'0'), - (0x1D7F7, 'M', u'1'), - (0x1D7F8, 'M', u'2'), - (0x1D7F9, 'M', u'3'), - (0x1D7FA, 'M', u'4'), - (0x1D7FB, 'M', u'5'), - (0x1D7FC, 'M', u'6'), + (0x1D7CE, 'M', '0'), + (0x1D7CF, 'M', '1'), + (0x1D7D0, 'M', '2'), + (0x1D7D1, 'M', '3'), + (0x1D7D2, 'M', '4'), + (0x1D7D3, 'M', '5'), + (0x1D7D4, 'M', '6'), + (0x1D7D5, 'M', '7'), + (0x1D7D6, 'M', '8'), + (0x1D7D7, 'M', '9'), + (0x1D7D8, 'M', '0'), + (0x1D7D9, 'M', '1'), + (0x1D7DA, 'M', '2'), + (0x1D7DB, 'M', '3'), + (0x1D7DC, 'M', '4'), + (0x1D7DD, 'M', '5'), + (0x1D7DE, 'M', '6'), + (0x1D7DF, 'M', '7'), + (0x1D7E0, 'M', '8'), + (0x1D7E1, 'M', '9'), + (0x1D7E2, 'M', '0'), + (0x1D7E3, 'M', '1'), ] def _seg_69(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1D7FD, 'M', u'7'), - (0x1D7FE, 'M', u'8'), - (0x1D7FF, 'M', u'9'), + (0x1D7E4, 'M', '2'), + (0x1D7E5, 'M', '3'), + (0x1D7E6, 'M', '4'), + (0x1D7E7, 'M', '5'), + (0x1D7E8, 'M', '6'), + (0x1D7E9, 'M', '7'), + (0x1D7EA, 'M', '8'), + (0x1D7EB, 'M', '9'), + (0x1D7EC, 'M', '0'), + (0x1D7ED, 'M', '1'), + (0x1D7EE, 'M', '2'), + (0x1D7EF, 'M', '3'), + (0x1D7F0, 'M', '4'), + (0x1D7F1, 'M', '5'), + (0x1D7F2, 'M', '6'), + (0x1D7F3, 'M', '7'), + (0x1D7F4, 'M', '8'), + (0x1D7F5, 'M', '9'), + (0x1D7F6, 'M', '0'), + (0x1D7F7, 'M', '1'), + (0x1D7F8, 'M', '2'), + (0x1D7F9, 'M', '3'), + (0x1D7FA, 'M', '4'), + (0x1D7FB, 'M', '5'), + (0x1D7FC, 'M', '6'), + (0x1D7FD, 'M', '7'), + (0x1D7FE, 'M', '8'), + (0x1D7FF, 'M', '9'), (0x1D800, 'V'), (0x1DA8C, 'X'), (0x1DA9B, 'V'), @@ -7218,231 +7314,233 @@ def _seg_69(): (0x1E8C5, 'X'), (0x1E8C7, 'V'), (0x1E8D7, 'X'), - (0x1E900, 'M', u'𞤢'), - (0x1E901, 'M', u'𞤣'), - (0x1E902, 'M', u'𞤤'), - (0x1E903, 'M', u'𞤥'), - (0x1E904, 'M', u'𞤦'), - (0x1E905, 'M', u'𞤧'), - (0x1E906, 'M', u'𞤨'), - (0x1E907, 'M', u'𞤩'), - (0x1E908, 'M', u'𞤪'), - (0x1E909, 'M', u'𞤫'), - (0x1E90A, 'M', u'𞤬'), - (0x1E90B, 'M', u'𞤭'), - (0x1E90C, 'M', u'𞤮'), - (0x1E90D, 'M', u'𞤯'), - (0x1E90E, 'M', u'𞤰'), - (0x1E90F, 'M', u'𞤱'), - (0x1E910, 'M', u'𞤲'), - (0x1E911, 'M', u'𞤳'), - (0x1E912, 'M', u'𞤴'), - (0x1E913, 'M', u'𞤵'), - (0x1E914, 'M', u'𞤶'), - (0x1E915, 'M', u'𞤷'), - (0x1E916, 'M', u'𞤸'), - (0x1E917, 'M', u'𞤹'), - (0x1E918, 'M', u'𞤺'), - (0x1E919, 'M', u'𞤻'), - (0x1E91A, 'M', u'𞤼'), - (0x1E91B, 'M', u'𞤽'), - (0x1E91C, 'M', u'𞤾'), - (0x1E91D, 'M', u'𞤿'), - (0x1E91E, 'M', u'𞥀'), - (0x1E91F, 'M', u'𞥁'), - (0x1E920, 'M', u'𞥂'), - (0x1E921, 'M', u'𞥃'), + (0x1E900, 'M', '𞤢'), + (0x1E901, 'M', '𞤣'), + (0x1E902, 'M', '𞤤'), + (0x1E903, 'M', '𞤥'), + (0x1E904, 'M', '𞤦'), + (0x1E905, 'M', '𞤧'), + (0x1E906, 'M', '𞤨'), + (0x1E907, 'M', '𞤩'), + (0x1E908, 'M', '𞤪'), + (0x1E909, 'M', '𞤫'), + (0x1E90A, 'M', '𞤬'), + (0x1E90B, 'M', '𞤭'), + (0x1E90C, 'M', '𞤮'), + (0x1E90D, 'M', '𞤯'), + (0x1E90E, 'M', '𞤰'), + (0x1E90F, 'M', '𞤱'), + (0x1E910, 'M', '𞤲'), + (0x1E911, 'M', '𞤳'), + (0x1E912, 'M', '𞤴'), + (0x1E913, 'M', '𞤵'), + (0x1E914, 'M', '𞤶'), + (0x1E915, 'M', '𞤷'), + (0x1E916, 'M', '𞤸'), + (0x1E917, 'M', '𞤹'), + (0x1E918, 'M', '𞤺'), + (0x1E919, 'M', '𞤻'), + (0x1E91A, 'M', '𞤼'), + (0x1E91B, 'M', '𞤽'), + (0x1E91C, 'M', '𞤾'), + (0x1E91D, 'M', '𞤿'), + (0x1E91E, 'M', '𞥀'), + (0x1E91F, 'M', '𞥁'), + (0x1E920, 'M', '𞥂'), + (0x1E921, 'M', '𞥃'), (0x1E922, 'V'), (0x1E94C, 'X'), (0x1E950, 'V'), (0x1E95A, 'X'), (0x1E95E, 'V'), (0x1E960, 'X'), + ] + +def _seg_70(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] + return [ (0x1EC71, 'V'), (0x1ECB5, 'X'), (0x1ED01, 'V'), (0x1ED3E, 'X'), - (0x1EE00, 'M', u'ا'), - (0x1EE01, 'M', u'ب'), - (0x1EE02, 'M', u'ج'), - (0x1EE03, 'M', u'د'), + (0x1EE00, 'M', 'ا'), + (0x1EE01, 'M', 'ب'), + (0x1EE02, 'M', 'ج'), + (0x1EE03, 'M', 'د'), (0x1EE04, 'X'), - (0x1EE05, 'M', u'و'), - (0x1EE06, 'M', u'ز'), - (0x1EE07, 'M', u'ح'), - (0x1EE08, 'M', u'ط'), - (0x1EE09, 'M', u'ي'), - (0x1EE0A, 'M', u'ك'), - (0x1EE0B, 'M', u'ل'), - (0x1EE0C, 'M', u'م'), - (0x1EE0D, 'M', u'ن'), - (0x1EE0E, 'M', u'س'), - (0x1EE0F, 'M', u'ع'), - (0x1EE10, 'M', u'ف'), - (0x1EE11, 'M', u'ص'), - (0x1EE12, 'M', u'ق'), - (0x1EE13, 'M', u'ر'), - (0x1EE14, 'M', u'ش'), - ] - -def _seg_70(): - return [ - (0x1EE15, 'M', u'ت'), - (0x1EE16, 'M', u'ث'), - (0x1EE17, 'M', u'خ'), - (0x1EE18, 'M', u'ذ'), - (0x1EE19, 'M', u'ض'), - (0x1EE1A, 'M', u'ظ'), - (0x1EE1B, 'M', u'غ'), - (0x1EE1C, 'M', u'ٮ'), - (0x1EE1D, 'M', u'ں'), - (0x1EE1E, 'M', u'ڡ'), - (0x1EE1F, 'M', u'ٯ'), + (0x1EE05, 'M', 'و'), + (0x1EE06, 'M', 'ز'), + (0x1EE07, 'M', 'ح'), + (0x1EE08, 'M', 'ط'), + (0x1EE09, 'M', 'ي'), + (0x1EE0A, 'M', 'ك'), + (0x1EE0B, 'M', 'ل'), + (0x1EE0C, 'M', 'م'), + (0x1EE0D, 'M', 'ن'), + (0x1EE0E, 'M', 'س'), + (0x1EE0F, 'M', 'ع'), + (0x1EE10, 'M', 'ف'), + (0x1EE11, 'M', 'ص'), + (0x1EE12, 'M', 'ق'), + (0x1EE13, 'M', 'ر'), + (0x1EE14, 'M', 'ش'), + (0x1EE15, 'M', 'ت'), + (0x1EE16, 'M', 'ث'), + (0x1EE17, 'M', 'خ'), + (0x1EE18, 'M', 'ذ'), + (0x1EE19, 'M', 'ض'), + (0x1EE1A, 'M', 'ظ'), + (0x1EE1B, 'M', 'غ'), + (0x1EE1C, 'M', 'ٮ'), + (0x1EE1D, 'M', 'ں'), + (0x1EE1E, 'M', 'ڡ'), + (0x1EE1F, 'M', 'ٯ'), (0x1EE20, 'X'), - (0x1EE21, 'M', u'ب'), - (0x1EE22, 'M', u'ج'), + (0x1EE21, 'M', 'ب'), + (0x1EE22, 'M', 'ج'), (0x1EE23, 'X'), - (0x1EE24, 'M', u'ه'), + (0x1EE24, 'M', 'ه'), (0x1EE25, 'X'), - (0x1EE27, 'M', u'ح'), + (0x1EE27, 'M', 'ح'), (0x1EE28, 'X'), - (0x1EE29, 'M', u'ي'), - (0x1EE2A, 'M', u'ك'), - (0x1EE2B, 'M', u'ل'), - (0x1EE2C, 'M', u'م'), - (0x1EE2D, 'M', u'ن'), - (0x1EE2E, 'M', u'س'), - (0x1EE2F, 'M', u'ع'), - (0x1EE30, 'M', u'ف'), - (0x1EE31, 'M', u'ص'), - (0x1EE32, 'M', u'ق'), + (0x1EE29, 'M', 'ي'), + (0x1EE2A, 'M', 'ك'), + (0x1EE2B, 'M', 'ل'), + (0x1EE2C, 'M', 'م'), + (0x1EE2D, 'M', 'ن'), + (0x1EE2E, 'M', 'س'), + (0x1EE2F, 'M', 'ع'), + (0x1EE30, 'M', 'ف'), + (0x1EE31, 'M', 'ص'), + (0x1EE32, 'M', 'ق'), (0x1EE33, 'X'), - (0x1EE34, 'M', u'ش'), - (0x1EE35, 'M', u'ت'), - (0x1EE36, 'M', u'ث'), - (0x1EE37, 'M', u'خ'), + (0x1EE34, 'M', 'ش'), + (0x1EE35, 'M', 'ت'), + (0x1EE36, 'M', 'ث'), + (0x1EE37, 'M', 'خ'), (0x1EE38, 'X'), - (0x1EE39, 'M', u'ض'), + (0x1EE39, 'M', 'ض'), (0x1EE3A, 'X'), - (0x1EE3B, 'M', u'غ'), + (0x1EE3B, 'M', 'غ'), (0x1EE3C, 'X'), - (0x1EE42, 'M', u'ج'), + (0x1EE42, 'M', 'ج'), (0x1EE43, 'X'), - (0x1EE47, 'M', u'ح'), + (0x1EE47, 'M', 'ح'), (0x1EE48, 'X'), - (0x1EE49, 'M', u'ي'), + (0x1EE49, 'M', 'ي'), (0x1EE4A, 'X'), - (0x1EE4B, 'M', u'ل'), + (0x1EE4B, 'M', 'ل'), (0x1EE4C, 'X'), - (0x1EE4D, 'M', u'ن'), - (0x1EE4E, 'M', u'س'), - (0x1EE4F, 'M', u'ع'), + (0x1EE4D, 'M', 'ن'), + (0x1EE4E, 'M', 'س'), + (0x1EE4F, 'M', 'ع'), (0x1EE50, 'X'), - (0x1EE51, 'M', u'ص'), - (0x1EE52, 'M', u'ق'), + (0x1EE51, 'M', 'ص'), + (0x1EE52, 'M', 'ق'), (0x1EE53, 'X'), - (0x1EE54, 'M', u'ش'), + (0x1EE54, 'M', 'ش'), (0x1EE55, 'X'), - (0x1EE57, 'M', u'خ'), + (0x1EE57, 'M', 'خ'), (0x1EE58, 'X'), - (0x1EE59, 'M', u'ض'), + (0x1EE59, 'M', 'ض'), (0x1EE5A, 'X'), - (0x1EE5B, 'M', u'غ'), + (0x1EE5B, 'M', 'غ'), (0x1EE5C, 'X'), - (0x1EE5D, 'M', u'ں'), + (0x1EE5D, 'M', 'ں'), (0x1EE5E, 'X'), - (0x1EE5F, 'M', u'ٯ'), + (0x1EE5F, 'M', 'ٯ'), (0x1EE60, 'X'), - (0x1EE61, 'M', u'ب'), - (0x1EE62, 'M', u'ج'), + (0x1EE61, 'M', 'ب'), + (0x1EE62, 'M', 'ج'), (0x1EE63, 'X'), - (0x1EE64, 'M', u'ه'), + (0x1EE64, 'M', 'ه'), (0x1EE65, 'X'), - (0x1EE67, 'M', u'ح'), - (0x1EE68, 'M', u'ط'), - (0x1EE69, 'M', u'ي'), - (0x1EE6A, 'M', u'ك'), + (0x1EE67, 'M', 'ح'), + (0x1EE68, 'M', 'ط'), + (0x1EE69, 'M', 'ي'), + (0x1EE6A, 'M', 'ك'), + ] + +def _seg_71(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] + return [ (0x1EE6B, 'X'), - (0x1EE6C, 'M', u'م'), - (0x1EE6D, 'M', u'ن'), - (0x1EE6E, 'M', u'س'), - (0x1EE6F, 'M', u'ع'), - (0x1EE70, 'M', u'ف'), - (0x1EE71, 'M', u'ص'), - (0x1EE72, 'M', u'ق'), + (0x1EE6C, 'M', 'م'), + (0x1EE6D, 'M', 'ن'), + (0x1EE6E, 'M', 'س'), + (0x1EE6F, 'M', 'ع'), + (0x1EE70, 'M', 'ف'), + (0x1EE71, 'M', 'ص'), + (0x1EE72, 'M', 'ق'), (0x1EE73, 'X'), - (0x1EE74, 'M', u'ش'), - (0x1EE75, 'M', u'ت'), - (0x1EE76, 'M', u'ث'), - (0x1EE77, 'M', u'خ'), + (0x1EE74, 'M', 'ش'), + (0x1EE75, 'M', 'ت'), + (0x1EE76, 'M', 'ث'), + (0x1EE77, 'M', 'خ'), (0x1EE78, 'X'), - (0x1EE79, 'M', u'ض'), - (0x1EE7A, 'M', u'ظ'), - (0x1EE7B, 'M', u'غ'), - (0x1EE7C, 'M', u'ٮ'), + (0x1EE79, 'M', 'ض'), + (0x1EE7A, 'M', 'ظ'), + (0x1EE7B, 'M', 'غ'), + (0x1EE7C, 'M', 'ٮ'), (0x1EE7D, 'X'), - (0x1EE7E, 'M', u'ڡ'), + (0x1EE7E, 'M', 'ڡ'), (0x1EE7F, 'X'), - (0x1EE80, 'M', u'ا'), - (0x1EE81, 'M', u'ب'), - (0x1EE82, 'M', u'ج'), - (0x1EE83, 'M', u'د'), - ] - -def _seg_71(): - return [ - (0x1EE84, 'M', u'ه'), - (0x1EE85, 'M', u'و'), - (0x1EE86, 'M', u'ز'), - (0x1EE87, 'M', u'ح'), - (0x1EE88, 'M', u'ط'), - (0x1EE89, 'M', u'ي'), + (0x1EE80, 'M', 'ا'), + (0x1EE81, 'M', 'ب'), + (0x1EE82, 'M', 'ج'), + (0x1EE83, 'M', 'د'), + (0x1EE84, 'M', 'ه'), + (0x1EE85, 'M', 'و'), + (0x1EE86, 'M', 'ز'), + (0x1EE87, 'M', 'ح'), + (0x1EE88, 'M', 'ط'), + (0x1EE89, 'M', 'ي'), (0x1EE8A, 'X'), - (0x1EE8B, 'M', u'ل'), - (0x1EE8C, 'M', u'م'), - (0x1EE8D, 'M', u'ن'), - (0x1EE8E, 'M', u'س'), - (0x1EE8F, 'M', u'ع'), - (0x1EE90, 'M', u'ف'), - (0x1EE91, 'M', u'ص'), - (0x1EE92, 'M', u'ق'), - (0x1EE93, 'M', u'ر'), - (0x1EE94, 'M', u'ش'), - (0x1EE95, 'M', u'ت'), - (0x1EE96, 'M', u'ث'), - (0x1EE97, 'M', u'خ'), - (0x1EE98, 'M', u'ذ'), - (0x1EE99, 'M', u'ض'), - (0x1EE9A, 'M', u'ظ'), - (0x1EE9B, 'M', u'غ'), + (0x1EE8B, 'M', 'ل'), + (0x1EE8C, 'M', 'م'), + (0x1EE8D, 'M', 'ن'), + (0x1EE8E, 'M', 'س'), + (0x1EE8F, 'M', 'ع'), + (0x1EE90, 'M', 'ف'), + (0x1EE91, 'M', 'ص'), + (0x1EE92, 'M', 'ق'), + (0x1EE93, 'M', 'ر'), + (0x1EE94, 'M', 'ش'), + (0x1EE95, 'M', 'ت'), + (0x1EE96, 'M', 'ث'), + (0x1EE97, 'M', 'خ'), + (0x1EE98, 'M', 'ذ'), + (0x1EE99, 'M', 'ض'), + (0x1EE9A, 'M', 'ظ'), + (0x1EE9B, 'M', 'غ'), (0x1EE9C, 'X'), - (0x1EEA1, 'M', u'ب'), - (0x1EEA2, 'M', u'ج'), - (0x1EEA3, 'M', u'د'), + (0x1EEA1, 'M', 'ب'), + (0x1EEA2, 'M', 'ج'), + (0x1EEA3, 'M', 'د'), (0x1EEA4, 'X'), - (0x1EEA5, 'M', u'و'), - (0x1EEA6, 'M', u'ز'), - (0x1EEA7, 'M', u'ح'), - (0x1EEA8, 'M', u'ط'), - (0x1EEA9, 'M', u'ي'), + (0x1EEA5, 'M', 'و'), + (0x1EEA6, 'M', 'ز'), + (0x1EEA7, 'M', 'ح'), + (0x1EEA8, 'M', 'ط'), + (0x1EEA9, 'M', 'ي'), (0x1EEAA, 'X'), - (0x1EEAB, 'M', u'ل'), - (0x1EEAC, 'M', u'م'), - (0x1EEAD, 'M', u'ن'), - (0x1EEAE, 'M', u'س'), - (0x1EEAF, 'M', u'ع'), - (0x1EEB0, 'M', u'ف'), - (0x1EEB1, 'M', u'ص'), - (0x1EEB2, 'M', u'ق'), - (0x1EEB3, 'M', u'ر'), - (0x1EEB4, 'M', u'ش'), - (0x1EEB5, 'M', u'ت'), - (0x1EEB6, 'M', u'ث'), - (0x1EEB7, 'M', u'خ'), - (0x1EEB8, 'M', u'ذ'), - (0x1EEB9, 'M', u'ض'), - (0x1EEBA, 'M', u'ظ'), - (0x1EEBB, 'M', u'غ'), + (0x1EEAB, 'M', 'ل'), + (0x1EEAC, 'M', 'م'), + (0x1EEAD, 'M', 'ن'), + (0x1EEAE, 'M', 'س'), + (0x1EEAF, 'M', 'ع'), + (0x1EEB0, 'M', 'ف'), + (0x1EEB1, 'M', 'ص'), + (0x1EEB2, 'M', 'ق'), + (0x1EEB3, 'M', 'ر'), + (0x1EEB4, 'M', 'ش'), + (0x1EEB5, 'M', 'ت'), + (0x1EEB6, 'M', 'ث'), + (0x1EEB7, 'M', 'خ'), + (0x1EEB8, 'M', 'ذ'), + (0x1EEB9, 'M', 'ض'), + (0x1EEBA, 'M', 'ظ'), + (0x1EEBB, 'M', 'غ'), (0x1EEBC, 'X'), (0x1EEF0, 'V'), (0x1EEF2, 'X'), @@ -7458,170 +7556,170 @@ def _seg_71(): (0x1F0D0, 'X'), (0x1F0D1, 'V'), (0x1F0F6, 'X'), - (0x1F101, '3', u'0,'), - (0x1F102, '3', u'1,'), - (0x1F103, '3', u'2,'), - (0x1F104, '3', u'3,'), - (0x1F105, '3', u'4,'), - (0x1F106, '3', u'5,'), - (0x1F107, '3', u'6,'), - (0x1F108, '3', u'7,'), - (0x1F109, '3', u'8,'), - (0x1F10A, '3', u'9,'), - (0x1F10B, 'V'), - (0x1F10D, 'X'), - (0x1F110, '3', u'(a)'), - (0x1F111, '3', u'(b)'), - (0x1F112, '3', u'(c)'), - (0x1F113, '3', u'(d)'), - (0x1F114, '3', u'(e)'), - (0x1F115, '3', u'(f)'), - (0x1F116, '3', u'(g)'), - (0x1F117, '3', u'(h)'), - (0x1F118, '3', u'(i)'), - (0x1F119, '3', u'(j)'), - (0x1F11A, '3', u'(k)'), - (0x1F11B, '3', u'(l)'), - (0x1F11C, '3', u'(m)'), - (0x1F11D, '3', u'(n)'), - (0x1F11E, '3', u'(o)'), - (0x1F11F, '3', u'(p)'), - (0x1F120, '3', u'(q)'), - (0x1F121, '3', u'(r)'), - (0x1F122, '3', u'(s)'), - (0x1F123, '3', u'(t)'), - (0x1F124, '3', u'(u)'), + (0x1F101, '3', '0,'), + (0x1F102, '3', '1,'), + (0x1F103, '3', '2,'), + (0x1F104, '3', '3,'), + (0x1F105, '3', '4,'), + (0x1F106, '3', '5,'), + (0x1F107, '3', '6,'), + (0x1F108, '3', '7,'), ] def _seg_72(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1F125, '3', u'(v)'), - (0x1F126, '3', u'(w)'), - (0x1F127, '3', u'(x)'), - (0x1F128, '3', u'(y)'), - (0x1F129, '3', u'(z)'), - (0x1F12A, 'M', u'〔s〕'), - (0x1F12B, 'M', u'c'), - (0x1F12C, 'M', u'r'), - (0x1F12D, 'M', u'cd'), - (0x1F12E, 'M', u'wz'), + (0x1F109, '3', '8,'), + (0x1F10A, '3', '9,'), + (0x1F10B, 'V'), + (0x1F110, '3', '(a)'), + (0x1F111, '3', '(b)'), + (0x1F112, '3', '(c)'), + (0x1F113, '3', '(d)'), + (0x1F114, '3', '(e)'), + (0x1F115, '3', '(f)'), + (0x1F116, '3', '(g)'), + (0x1F117, '3', '(h)'), + (0x1F118, '3', '(i)'), + (0x1F119, '3', '(j)'), + (0x1F11A, '3', '(k)'), + (0x1F11B, '3', '(l)'), + (0x1F11C, '3', '(m)'), + (0x1F11D, '3', '(n)'), + (0x1F11E, '3', '(o)'), + (0x1F11F, '3', '(p)'), + (0x1F120, '3', '(q)'), + (0x1F121, '3', '(r)'), + (0x1F122, '3', '(s)'), + (0x1F123, '3', '(t)'), + (0x1F124, '3', '(u)'), + (0x1F125, '3', '(v)'), + (0x1F126, '3', '(w)'), + (0x1F127, '3', '(x)'), + (0x1F128, '3', '(y)'), + (0x1F129, '3', '(z)'), + (0x1F12A, 'M', '〔s〕'), + (0x1F12B, 'M', 'c'), + (0x1F12C, 'M', 'r'), + (0x1F12D, 'M', 'cd'), + (0x1F12E, 'M', 'wz'), (0x1F12F, 'V'), - (0x1F130, 'M', u'a'), - (0x1F131, 'M', u'b'), - (0x1F132, 'M', u'c'), - (0x1F133, 'M', u'd'), - (0x1F134, 'M', u'e'), - (0x1F135, 'M', u'f'), - (0x1F136, 'M', u'g'), - (0x1F137, 'M', u'h'), - (0x1F138, 'M', u'i'), - (0x1F139, 'M', u'j'), - (0x1F13A, 'M', u'k'), - (0x1F13B, 'M', u'l'), - (0x1F13C, 'M', u'm'), - (0x1F13D, 'M', u'n'), - (0x1F13E, 'M', u'o'), - (0x1F13F, 'M', u'p'), - (0x1F140, 'M', u'q'), - (0x1F141, 'M', u'r'), - (0x1F142, 'M', u's'), - (0x1F143, 'M', u't'), - (0x1F144, 'M', u'u'), - (0x1F145, 'M', u'v'), - (0x1F146, 'M', u'w'), - (0x1F147, 'M', u'x'), - (0x1F148, 'M', u'y'), - (0x1F149, 'M', u'z'), - (0x1F14A, 'M', u'hv'), - (0x1F14B, 'M', u'mv'), - (0x1F14C, 'M', u'sd'), - (0x1F14D, 'M', u'ss'), - (0x1F14E, 'M', u'ppv'), - (0x1F14F, 'M', u'wc'), + (0x1F130, 'M', 'a'), + (0x1F131, 'M', 'b'), + (0x1F132, 'M', 'c'), + (0x1F133, 'M', 'd'), + (0x1F134, 'M', 'e'), + (0x1F135, 'M', 'f'), + (0x1F136, 'M', 'g'), + (0x1F137, 'M', 'h'), + (0x1F138, 'M', 'i'), + (0x1F139, 'M', 'j'), + (0x1F13A, 'M', 'k'), + (0x1F13B, 'M', 'l'), + (0x1F13C, 'M', 'm'), + (0x1F13D, 'M', 'n'), + (0x1F13E, 'M', 'o'), + (0x1F13F, 'M', 'p'), + (0x1F140, 'M', 'q'), + (0x1F141, 'M', 'r'), + (0x1F142, 'M', 's'), + (0x1F143, 'M', 't'), + (0x1F144, 'M', 'u'), + (0x1F145, 'M', 'v'), + (0x1F146, 'M', 'w'), + (0x1F147, 'M', 'x'), + (0x1F148, 'M', 'y'), + (0x1F149, 'M', 'z'), + (0x1F14A, 'M', 'hv'), + (0x1F14B, 'M', 'mv'), + (0x1F14C, 'M', 'sd'), + (0x1F14D, 'M', 'ss'), + (0x1F14E, 'M', 'ppv'), + (0x1F14F, 'M', 'wc'), (0x1F150, 'V'), - (0x1F16A, 'M', u'mc'), - (0x1F16B, 'M', u'md'), - (0x1F16C, 'M', u'mr'), - (0x1F16D, 'X'), - (0x1F170, 'V'), - (0x1F190, 'M', u'dj'), + (0x1F16A, 'M', 'mc'), + (0x1F16B, 'M', 'md'), + (0x1F16C, 'M', 'mr'), + (0x1F16D, 'V'), + (0x1F190, 'M', 'dj'), (0x1F191, 'V'), - (0x1F1AD, 'X'), + (0x1F1AE, 'X'), (0x1F1E6, 'V'), - (0x1F200, 'M', u'ほか'), - (0x1F201, 'M', u'ココ'), - (0x1F202, 'M', u'サ'), + (0x1F200, 'M', 'ほか'), + (0x1F201, 'M', 'ココ'), + (0x1F202, 'M', 'サ'), (0x1F203, 'X'), - (0x1F210, 'M', u'手'), - (0x1F211, 'M', u'字'), - (0x1F212, 'M', u'双'), - (0x1F213, 'M', u'デ'), - (0x1F214, 'M', u'二'), - (0x1F215, 'M', u'多'), - (0x1F216, 'M', u'解'), - (0x1F217, 'M', u'天'), - (0x1F218, 'M', u'交'), - (0x1F219, 'M', u'映'), - (0x1F21A, 'M', u'無'), - (0x1F21B, 'M', u'料'), - (0x1F21C, 'M', u'前'), - (0x1F21D, 'M', u'後'), - (0x1F21E, 'M', u'再'), - (0x1F21F, 'M', u'新'), - (0x1F220, 'M', u'初'), - (0x1F221, 'M', u'終'), - (0x1F222, 'M', u'生'), - (0x1F223, 'M', u'販'), - (0x1F224, 'M', u'声'), - (0x1F225, 'M', u'吹'), - (0x1F226, 'M', u'演'), - (0x1F227, 'M', u'投'), - (0x1F228, 'M', u'捕'), - (0x1F229, 'M', u'一'), - (0x1F22A, 'M', u'三'), - (0x1F22B, 'M', u'遊'), - (0x1F22C, 'M', u'左'), - (0x1F22D, 'M', u'中'), - (0x1F22E, 'M', u'右'), - (0x1F22F, 'M', u'指'), - (0x1F230, 'M', u'走'), - (0x1F231, 'M', u'打'), - (0x1F232, 'M', u'禁'), - (0x1F233, 'M', u'空'), - (0x1F234, 'M', u'合'), - (0x1F235, 'M', u'満'), - (0x1F236, 'M', u'有'), - (0x1F237, 'M', u'月'), - (0x1F238, 'M', u'申'), - (0x1F239, 'M', u'割'), - (0x1F23A, 'M', u'営'), + (0x1F210, 'M', '手'), + (0x1F211, 'M', '字'), + (0x1F212, 'M', '双'), + (0x1F213, 'M', 'デ'), + (0x1F214, 'M', '二'), + (0x1F215, 'M', '多'), + (0x1F216, 'M', '解'), + (0x1F217, 'M', '天'), + (0x1F218, 'M', '交'), + (0x1F219, 'M', '映'), + (0x1F21A, 'M', '無'), + (0x1F21B, 'M', '料'), + (0x1F21C, 'M', '前'), + (0x1F21D, 'M', '後'), + (0x1F21E, 'M', '再'), + (0x1F21F, 'M', '新'), + (0x1F220, 'M', '初'), + (0x1F221, 'M', '終'), + (0x1F222, 'M', '生'), + (0x1F223, 'M', '販'), ] def _seg_73(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x1F23B, 'M', u'配'), + (0x1F224, 'M', '声'), + (0x1F225, 'M', '吹'), + (0x1F226, 'M', '演'), + (0x1F227, 'M', '投'), + (0x1F228, 'M', '捕'), + (0x1F229, 'M', '一'), + (0x1F22A, 'M', '三'), + (0x1F22B, 'M', '遊'), + (0x1F22C, 'M', '左'), + (0x1F22D, 'M', '中'), + (0x1F22E, 'M', '右'), + (0x1F22F, 'M', '指'), + (0x1F230, 'M', '走'), + (0x1F231, 'M', '打'), + (0x1F232, 'M', '禁'), + (0x1F233, 'M', '空'), + (0x1F234, 'M', '合'), + (0x1F235, 'M', '満'), + (0x1F236, 'M', '有'), + (0x1F237, 'M', '月'), + (0x1F238, 'M', '申'), + (0x1F239, 'M', '割'), + (0x1F23A, 'M', '営'), + (0x1F23B, 'M', '配'), (0x1F23C, 'X'), - (0x1F240, 'M', u'〔本〕'), - (0x1F241, 'M', u'〔三〕'), - (0x1F242, 'M', u'〔二〕'), - (0x1F243, 'M', u'〔安〕'), - (0x1F244, 'M', u'〔点〕'), - (0x1F245, 'M', u'〔打〕'), - (0x1F246, 'M', u'〔盗〕'), - (0x1F247, 'M', u'〔勝〕'), - (0x1F248, 'M', u'〔敗〕'), + (0x1F240, 'M', '〔本〕'), + (0x1F241, 'M', '〔三〕'), + (0x1F242, 'M', '〔二〕'), + (0x1F243, 'M', '〔安〕'), + (0x1F244, 'M', '〔点〕'), + (0x1F245, 'M', '〔打〕'), + (0x1F246, 'M', '〔盗〕'), + (0x1F247, 'M', '〔勝〕'), + (0x1F248, 'M', '〔敗〕'), (0x1F249, 'X'), - (0x1F250, 'M', u'得'), - (0x1F251, 'M', u'可'), + (0x1F250, 'M', '得'), + (0x1F251, 'M', '可'), (0x1F252, 'X'), (0x1F260, 'V'), (0x1F266, 'X'), (0x1F300, 'V'), - (0x1F6D6, 'X'), + (0x1F6D8, 'X'), (0x1F6E0, 'V'), (0x1F6ED, 'X'), (0x1F6F0, 'V'), - (0x1F6FB, 'X'), + (0x1F6FD, 'X'), (0x1F700, 'V'), (0x1F774, 'X'), (0x1F780, 'V'), @@ -7638,32 +7736,52 @@ def _seg_73(): (0x1F888, 'X'), (0x1F890, 'V'), (0x1F8AE, 'X'), + (0x1F8B0, 'V'), + (0x1F8B2, 'X'), (0x1F900, 'V'), - (0x1F90C, 'X'), - (0x1F90D, 'V'), - (0x1F972, 'X'), - (0x1F973, 'V'), - (0x1F977, 'X'), + (0x1F979, 'X'), (0x1F97A, 'V'), - (0x1F9A3, 'X'), - (0x1F9A5, 'V'), - (0x1F9AB, 'X'), - (0x1F9AE, 'V'), - (0x1F9CB, 'X'), + (0x1F9CC, 'X'), (0x1F9CD, 'V'), (0x1FA54, 'X'), (0x1FA60, 'V'), (0x1FA6E, 'X'), (0x1FA70, 'V'), - (0x1FA74, 'X'), + (0x1FA75, 'X'), (0x1FA78, 'V'), (0x1FA7B, 'X'), (0x1FA80, 'V'), - (0x1FA83, 'X'), + (0x1FA87, 'X'), (0x1FA90, 'V'), - (0x1FA96, 'X'), + (0x1FAA9, 'X'), + (0x1FAB0, 'V'), + (0x1FAB7, 'X'), + (0x1FAC0, 'V'), + (0x1FAC3, 'X'), + (0x1FAD0, 'V'), + (0x1FAD7, 'X'), + (0x1FB00, 'V'), + (0x1FB93, 'X'), + (0x1FB94, 'V'), + (0x1FBCB, 'X'), + (0x1FBF0, 'M', '0'), + (0x1FBF1, 'M', '1'), + (0x1FBF2, 'M', '2'), + (0x1FBF3, 'M', '3'), + (0x1FBF4, 'M', '4'), + (0x1FBF5, 'M', '5'), + (0x1FBF6, 'M', '6'), + (0x1FBF7, 'M', '7'), + (0x1FBF8, 'M', '8'), + (0x1FBF9, 'M', '9'), + ] + +def _seg_74(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] + return [ + (0x1FBFA, 'X'), (0x20000, 'V'), - (0x2A6D7, 'X'), + (0x2A6DE, 'X'), (0x2A700, 'V'), (0x2B735, 'X'), (0x2B740, 'V'), @@ -7672,563 +7790,566 @@ def _seg_73(): (0x2CEA2, 'X'), (0x2CEB0, 'V'), (0x2EBE1, 'X'), - (0x2F800, 'M', u'丽'), - (0x2F801, 'M', u'丸'), - (0x2F802, 'M', u'乁'), - (0x2F803, 'M', u'𠄢'), - (0x2F804, 'M', u'你'), - (0x2F805, 'M', u'侮'), - (0x2F806, 'M', u'侻'), - (0x2F807, 'M', u'倂'), - (0x2F808, 'M', u'偺'), - (0x2F809, 'M', u'備'), - (0x2F80A, 'M', u'僧'), - (0x2F80B, 'M', u'像'), - (0x2F80C, 'M', u'㒞'), - (0x2F80D, 'M', u'𠘺'), - (0x2F80E, 'M', u'免'), - (0x2F80F, 'M', u'兔'), - (0x2F810, 'M', u'兤'), - (0x2F811, 'M', u'具'), - (0x2F812, 'M', u'𠔜'), - (0x2F813, 'M', u'㒹'), - (0x2F814, 'M', u'內'), - (0x2F815, 'M', u'再'), - (0x2F816, 'M', u'𠕋'), - (0x2F817, 'M', u'冗'), - (0x2F818, 'M', u'冤'), - (0x2F819, 'M', u'仌'), - (0x2F81A, 'M', u'冬'), + (0x2F800, 'M', '丽'), + (0x2F801, 'M', '丸'), + (0x2F802, 'M', '乁'), + (0x2F803, 'M', '𠄢'), + (0x2F804, 'M', '你'), + (0x2F805, 'M', '侮'), + (0x2F806, 'M', '侻'), + (0x2F807, 'M', '倂'), + (0x2F808, 'M', '偺'), + (0x2F809, 'M', '備'), + (0x2F80A, 'M', '僧'), + (0x2F80B, 'M', '像'), + (0x2F80C, 'M', '㒞'), + (0x2F80D, 'M', '𠘺'), + (0x2F80E, 'M', '免'), + (0x2F80F, 'M', '兔'), + (0x2F810, 'M', '兤'), + (0x2F811, 'M', '具'), + (0x2F812, 'M', '𠔜'), + (0x2F813, 'M', '㒹'), + (0x2F814, 'M', '內'), + (0x2F815, 'M', '再'), + (0x2F816, 'M', '𠕋'), + (0x2F817, 'M', '冗'), + (0x2F818, 'M', '冤'), + (0x2F819, 'M', '仌'), + (0x2F81A, 'M', '冬'), + (0x2F81B, 'M', '况'), + (0x2F81C, 'M', '𩇟'), + (0x2F81D, 'M', '凵'), + (0x2F81E, 'M', '刃'), + (0x2F81F, 'M', '㓟'), + (0x2F820, 'M', '刻'), + (0x2F821, 'M', '剆'), + (0x2F822, 'M', '割'), + (0x2F823, 'M', '剷'), + (0x2F824, 'M', '㔕'), + (0x2F825, 'M', '勇'), + (0x2F826, 'M', '勉'), + (0x2F827, 'M', '勤'), + (0x2F828, 'M', '勺'), + (0x2F829, 'M', '包'), + (0x2F82A, 'M', '匆'), + (0x2F82B, 'M', '北'), + (0x2F82C, 'M', '卉'), + (0x2F82D, 'M', '卑'), + (0x2F82E, 'M', '博'), + (0x2F82F, 'M', '即'), + (0x2F830, 'M', '卽'), + (0x2F831, 'M', '卿'), + (0x2F834, 'M', '𠨬'), + (0x2F835, 'M', '灰'), + (0x2F836, 'M', '及'), + (0x2F837, 'M', '叟'), + (0x2F838, 'M', '𠭣'), + (0x2F839, 'M', '叫'), + (0x2F83A, 'M', '叱'), + (0x2F83B, 'M', '吆'), + (0x2F83C, 'M', '咞'), + (0x2F83D, 'M', '吸'), + (0x2F83E, 'M', '呈'), + (0x2F83F, 'M', '周'), + (0x2F840, 'M', '咢'), + (0x2F841, 'M', '哶'), + (0x2F842, 'M', '唐'), + (0x2F843, 'M', '啓'), + (0x2F844, 'M', '啣'), + (0x2F845, 'M', '善'), + (0x2F847, 'M', '喙'), + (0x2F848, 'M', '喫'), + (0x2F849, 'M', '喳'), + (0x2F84A, 'M', '嗂'), + (0x2F84B, 'M', '圖'), + (0x2F84C, 'M', '嘆'), + (0x2F84D, 'M', '圗'), + (0x2F84E, 'M', '噑'), + (0x2F84F, 'M', '噴'), + (0x2F850, 'M', '切'), + (0x2F851, 'M', '壮'), + (0x2F852, 'M', '城'), + (0x2F853, 'M', '埴'), + (0x2F854, 'M', '堍'), + (0x2F855, 'M', '型'), + (0x2F856, 'M', '堲'), + (0x2F857, 'M', '報'), + (0x2F858, 'M', '墬'), + (0x2F859, 'M', '𡓤'), + (0x2F85A, 'M', '売'), + (0x2F85B, 'M', '壷'), ] -def _seg_74(): +def _seg_75(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2F81B, 'M', u'况'), - (0x2F81C, 'M', u'𩇟'), - (0x2F81D, 'M', u'凵'), - (0x2F81E, 'M', u'刃'), - (0x2F81F, 'M', u'㓟'), - (0x2F820, 'M', u'刻'), - (0x2F821, 'M', u'剆'), - (0x2F822, 'M', u'割'), - (0x2F823, 'M', u'剷'), - (0x2F824, 'M', u'㔕'), - (0x2F825, 'M', u'勇'), - (0x2F826, 'M', u'勉'), - (0x2F827, 'M', u'勤'), - (0x2F828, 'M', u'勺'), - (0x2F829, 'M', u'包'), - (0x2F82A, 'M', u'匆'), - (0x2F82B, 'M', u'北'), - (0x2F82C, 'M', u'卉'), - (0x2F82D, 'M', u'卑'), - (0x2F82E, 'M', u'博'), - (0x2F82F, 'M', u'即'), - (0x2F830, 'M', u'卽'), - (0x2F831, 'M', u'卿'), - (0x2F834, 'M', u'𠨬'), - (0x2F835, 'M', u'灰'), - (0x2F836, 'M', u'及'), - (0x2F837, 'M', u'叟'), - (0x2F838, 'M', u'𠭣'), - (0x2F839, 'M', u'叫'), - (0x2F83A, 'M', u'叱'), - (0x2F83B, 'M', u'吆'), - (0x2F83C, 'M', u'咞'), - (0x2F83D, 'M', u'吸'), - (0x2F83E, 'M', u'呈'), - (0x2F83F, 'M', u'周'), - (0x2F840, 'M', u'咢'), - (0x2F841, 'M', u'哶'), - (0x2F842, 'M', u'唐'), - (0x2F843, 'M', u'啓'), - (0x2F844, 'M', u'啣'), - (0x2F845, 'M', u'善'), - (0x2F847, 'M', u'喙'), - (0x2F848, 'M', u'喫'), - (0x2F849, 'M', u'喳'), - (0x2F84A, 'M', u'嗂'), - (0x2F84B, 'M', u'圖'), - (0x2F84C, 'M', u'嘆'), - (0x2F84D, 'M', u'圗'), - (0x2F84E, 'M', u'噑'), - (0x2F84F, 'M', u'噴'), - (0x2F850, 'M', u'切'), - (0x2F851, 'M', u'壮'), - (0x2F852, 'M', u'城'), - (0x2F853, 'M', u'埴'), - (0x2F854, 'M', u'堍'), - (0x2F855, 'M', u'型'), - (0x2F856, 'M', u'堲'), - (0x2F857, 'M', u'報'), - (0x2F858, 'M', u'墬'), - (0x2F859, 'M', u'𡓤'), - (0x2F85A, 'M', u'売'), - (0x2F85B, 'M', u'壷'), - (0x2F85C, 'M', u'夆'), - (0x2F85D, 'M', u'多'), - (0x2F85E, 'M', u'夢'), - (0x2F85F, 'M', u'奢'), - (0x2F860, 'M', u'𡚨'), - (0x2F861, 'M', u'𡛪'), - (0x2F862, 'M', u'姬'), - (0x2F863, 'M', u'娛'), - (0x2F864, 'M', u'娧'), - (0x2F865, 'M', u'姘'), - (0x2F866, 'M', u'婦'), - (0x2F867, 'M', u'㛮'), + (0x2F85C, 'M', '夆'), + (0x2F85D, 'M', '多'), + (0x2F85E, 'M', '夢'), + (0x2F85F, 'M', '奢'), + (0x2F860, 'M', '𡚨'), + (0x2F861, 'M', '𡛪'), + (0x2F862, 'M', '姬'), + (0x2F863, 'M', '娛'), + (0x2F864, 'M', '娧'), + (0x2F865, 'M', '姘'), + (0x2F866, 'M', '婦'), + (0x2F867, 'M', '㛮'), (0x2F868, 'X'), - (0x2F869, 'M', u'嬈'), - (0x2F86A, 'M', u'嬾'), - (0x2F86C, 'M', u'𡧈'), - (0x2F86D, 'M', u'寃'), - (0x2F86E, 'M', u'寘'), - (0x2F86F, 'M', u'寧'), - (0x2F870, 'M', u'寳'), - (0x2F871, 'M', u'𡬘'), - (0x2F872, 'M', u'寿'), - (0x2F873, 'M', u'将'), + (0x2F869, 'M', '嬈'), + (0x2F86A, 'M', '嬾'), + (0x2F86C, 'M', '𡧈'), + (0x2F86D, 'M', '寃'), + (0x2F86E, 'M', '寘'), + (0x2F86F, 'M', '寧'), + (0x2F870, 'M', '寳'), + (0x2F871, 'M', '𡬘'), + (0x2F872, 'M', '寿'), + (0x2F873, 'M', '将'), (0x2F874, 'X'), - (0x2F875, 'M', u'尢'), - (0x2F876, 'M', u'㞁'), - (0x2F877, 'M', u'屠'), - (0x2F878, 'M', u'屮'), - (0x2F879, 'M', u'峀'), - (0x2F87A, 'M', u'岍'), - (0x2F87B, 'M', u'𡷤'), - (0x2F87C, 'M', u'嵃'), - (0x2F87D, 'M', u'𡷦'), - (0x2F87E, 'M', u'嵮'), - (0x2F87F, 'M', u'嵫'), - (0x2F880, 'M', u'嵼'), - (0x2F881, 'M', u'巡'), - (0x2F882, 'M', u'巢'), - ] - -def _seg_75(): - return [ - (0x2F883, 'M', u'㠯'), - (0x2F884, 'M', u'巽'), - (0x2F885, 'M', u'帨'), - (0x2F886, 'M', u'帽'), - (0x2F887, 'M', u'幩'), - (0x2F888, 'M', u'㡢'), - (0x2F889, 'M', u'𢆃'), - (0x2F88A, 'M', u'㡼'), - (0x2F88B, 'M', u'庰'), - (0x2F88C, 'M', u'庳'), - (0x2F88D, 'M', u'庶'), - (0x2F88E, 'M', u'廊'), - (0x2F88F, 'M', u'𪎒'), - (0x2F890, 'M', u'廾'), - (0x2F891, 'M', u'𢌱'), - (0x2F893, 'M', u'舁'), - (0x2F894, 'M', u'弢'), - (0x2F896, 'M', u'㣇'), - (0x2F897, 'M', u'𣊸'), - (0x2F898, 'M', u'𦇚'), - (0x2F899, 'M', u'形'), - (0x2F89A, 'M', u'彫'), - (0x2F89B, 'M', u'㣣'), - (0x2F89C, 'M', u'徚'), - (0x2F89D, 'M', u'忍'), - (0x2F89E, 'M', u'志'), - (0x2F89F, 'M', u'忹'), - (0x2F8A0, 'M', u'悁'), - (0x2F8A1, 'M', u'㤺'), - (0x2F8A2, 'M', u'㤜'), - (0x2F8A3, 'M', u'悔'), - (0x2F8A4, 'M', u'𢛔'), - (0x2F8A5, 'M', u'惇'), - (0x2F8A6, 'M', u'慈'), - (0x2F8A7, 'M', u'慌'), - (0x2F8A8, 'M', u'慎'), - (0x2F8A9, 'M', u'慌'), - (0x2F8AA, 'M', u'慺'), - (0x2F8AB, 'M', u'憎'), - (0x2F8AC, 'M', u'憲'), - (0x2F8AD, 'M', u'憤'), - (0x2F8AE, 'M', u'憯'), - (0x2F8AF, 'M', u'懞'), - (0x2F8B0, 'M', u'懲'), - (0x2F8B1, 'M', u'懶'), - (0x2F8B2, 'M', u'成'), - (0x2F8B3, 'M', u'戛'), - (0x2F8B4, 'M', u'扝'), - (0x2F8B5, 'M', u'抱'), - (0x2F8B6, 'M', u'拔'), - (0x2F8B7, 'M', u'捐'), - (0x2F8B8, 'M', u'𢬌'), - (0x2F8B9, 'M', u'挽'), - (0x2F8BA, 'M', u'拼'), - (0x2F8BB, 'M', u'捨'), - (0x2F8BC, 'M', u'掃'), - (0x2F8BD, 'M', u'揤'), - (0x2F8BE, 'M', u'𢯱'), - (0x2F8BF, 'M', u'搢'), - (0x2F8C0, 'M', u'揅'), - (0x2F8C1, 'M', u'掩'), - (0x2F8C2, 'M', u'㨮'), - (0x2F8C3, 'M', u'摩'), - (0x2F8C4, 'M', u'摾'), - (0x2F8C5, 'M', u'撝'), - (0x2F8C6, 'M', u'摷'), - (0x2F8C7, 'M', u'㩬'), - (0x2F8C8, 'M', u'敏'), - (0x2F8C9, 'M', u'敬'), - (0x2F8CA, 'M', u'𣀊'), - (0x2F8CB, 'M', u'旣'), - (0x2F8CC, 'M', u'書'), - (0x2F8CD, 'M', u'晉'), - (0x2F8CE, 'M', u'㬙'), - (0x2F8CF, 'M', u'暑'), - (0x2F8D0, 'M', u'㬈'), - (0x2F8D1, 'M', u'㫤'), - (0x2F8D2, 'M', u'冒'), - (0x2F8D3, 'M', u'冕'), - (0x2F8D4, 'M', u'最'), - (0x2F8D5, 'M', u'暜'), - (0x2F8D6, 'M', u'肭'), - (0x2F8D7, 'M', u'䏙'), - (0x2F8D8, 'M', u'朗'), - (0x2F8D9, 'M', u'望'), - (0x2F8DA, 'M', u'朡'), - (0x2F8DB, 'M', u'杞'), - (0x2F8DC, 'M', u'杓'), - (0x2F8DD, 'M', u'𣏃'), - (0x2F8DE, 'M', u'㭉'), - (0x2F8DF, 'M', u'柺'), - (0x2F8E0, 'M', u'枅'), - (0x2F8E1, 'M', u'桒'), - (0x2F8E2, 'M', u'梅'), - (0x2F8E3, 'M', u'𣑭'), - (0x2F8E4, 'M', u'梎'), - (0x2F8E5, 'M', u'栟'), - (0x2F8E6, 'M', u'椔'), - (0x2F8E7, 'M', u'㮝'), - (0x2F8E8, 'M', u'楂'), + (0x2F875, 'M', '尢'), + (0x2F876, 'M', '㞁'), + (0x2F877, 'M', '屠'), + (0x2F878, 'M', '屮'), + (0x2F879, 'M', '峀'), + (0x2F87A, 'M', '岍'), + (0x2F87B, 'M', '𡷤'), + (0x2F87C, 'M', '嵃'), + (0x2F87D, 'M', '𡷦'), + (0x2F87E, 'M', '嵮'), + (0x2F87F, 'M', '嵫'), + (0x2F880, 'M', '嵼'), + (0x2F881, 'M', '巡'), + (0x2F882, 'M', '巢'), + (0x2F883, 'M', '㠯'), + (0x2F884, 'M', '巽'), + (0x2F885, 'M', '帨'), + (0x2F886, 'M', '帽'), + (0x2F887, 'M', '幩'), + (0x2F888, 'M', '㡢'), + (0x2F889, 'M', '𢆃'), + (0x2F88A, 'M', '㡼'), + (0x2F88B, 'M', '庰'), + (0x2F88C, 'M', '庳'), + (0x2F88D, 'M', '庶'), + (0x2F88E, 'M', '廊'), + (0x2F88F, 'M', '𪎒'), + (0x2F890, 'M', '廾'), + (0x2F891, 'M', '𢌱'), + (0x2F893, 'M', '舁'), + (0x2F894, 'M', '弢'), + (0x2F896, 'M', '㣇'), + (0x2F897, 'M', '𣊸'), + (0x2F898, 'M', '𦇚'), + (0x2F899, 'M', '形'), + (0x2F89A, 'M', '彫'), + (0x2F89B, 'M', '㣣'), + (0x2F89C, 'M', '徚'), + (0x2F89D, 'M', '忍'), + (0x2F89E, 'M', '志'), + (0x2F89F, 'M', '忹'), + (0x2F8A0, 'M', '悁'), + (0x2F8A1, 'M', '㤺'), + (0x2F8A2, 'M', '㤜'), + (0x2F8A3, 'M', '悔'), + (0x2F8A4, 'M', '𢛔'), + (0x2F8A5, 'M', '惇'), + (0x2F8A6, 'M', '慈'), + (0x2F8A7, 'M', '慌'), + (0x2F8A8, 'M', '慎'), + (0x2F8A9, 'M', '慌'), + (0x2F8AA, 'M', '慺'), + (0x2F8AB, 'M', '憎'), + (0x2F8AC, 'M', '憲'), + (0x2F8AD, 'M', '憤'), + (0x2F8AE, 'M', '憯'), + (0x2F8AF, 'M', '懞'), + (0x2F8B0, 'M', '懲'), + (0x2F8B1, 'M', '懶'), + (0x2F8B2, 'M', '成'), + (0x2F8B3, 'M', '戛'), + (0x2F8B4, 'M', '扝'), + (0x2F8B5, 'M', '抱'), + (0x2F8B6, 'M', '拔'), + (0x2F8B7, 'M', '捐'), + (0x2F8B8, 'M', '𢬌'), + (0x2F8B9, 'M', '挽'), + (0x2F8BA, 'M', '拼'), + (0x2F8BB, 'M', '捨'), + (0x2F8BC, 'M', '掃'), + (0x2F8BD, 'M', '揤'), + (0x2F8BE, 'M', '𢯱'), + (0x2F8BF, 'M', '搢'), + (0x2F8C0, 'M', '揅'), + (0x2F8C1, 'M', '掩'), + (0x2F8C2, 'M', '㨮'), ] def _seg_76(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2F8E9, 'M', u'榣'), - (0x2F8EA, 'M', u'槪'), - (0x2F8EB, 'M', u'檨'), - (0x2F8EC, 'M', u'𣚣'), - (0x2F8ED, 'M', u'櫛'), - (0x2F8EE, 'M', u'㰘'), - (0x2F8EF, 'M', u'次'), - (0x2F8F0, 'M', u'𣢧'), - (0x2F8F1, 'M', u'歔'), - (0x2F8F2, 'M', u'㱎'), - (0x2F8F3, 'M', u'歲'), - (0x2F8F4, 'M', u'殟'), - (0x2F8F5, 'M', u'殺'), - (0x2F8F6, 'M', u'殻'), - (0x2F8F7, 'M', u'𣪍'), - (0x2F8F8, 'M', u'𡴋'), - (0x2F8F9, 'M', u'𣫺'), - (0x2F8FA, 'M', u'汎'), - (0x2F8FB, 'M', u'𣲼'), - (0x2F8FC, 'M', u'沿'), - (0x2F8FD, 'M', u'泍'), - (0x2F8FE, 'M', u'汧'), - (0x2F8FF, 'M', u'洖'), - (0x2F900, 'M', u'派'), - (0x2F901, 'M', u'海'), - (0x2F902, 'M', u'流'), - (0x2F903, 'M', u'浩'), - (0x2F904, 'M', u'浸'), - (0x2F905, 'M', u'涅'), - (0x2F906, 'M', u'𣴞'), - (0x2F907, 'M', u'洴'), - (0x2F908, 'M', u'港'), - (0x2F909, 'M', u'湮'), - (0x2F90A, 'M', u'㴳'), - (0x2F90B, 'M', u'滋'), - (0x2F90C, 'M', u'滇'), - (0x2F90D, 'M', u'𣻑'), - (0x2F90E, 'M', u'淹'), - (0x2F90F, 'M', u'潮'), - (0x2F910, 'M', u'𣽞'), - (0x2F911, 'M', u'𣾎'), - (0x2F912, 'M', u'濆'), - (0x2F913, 'M', u'瀹'), - (0x2F914, 'M', u'瀞'), - (0x2F915, 'M', u'瀛'), - (0x2F916, 'M', u'㶖'), - (0x2F917, 'M', u'灊'), - (0x2F918, 'M', u'災'), - (0x2F919, 'M', u'灷'), - (0x2F91A, 'M', u'炭'), - (0x2F91B, 'M', u'𠔥'), - (0x2F91C, 'M', u'煅'), - (0x2F91D, 'M', u'𤉣'), - (0x2F91E, 'M', u'熜'), + (0x2F8C3, 'M', '摩'), + (0x2F8C4, 'M', '摾'), + (0x2F8C5, 'M', '撝'), + (0x2F8C6, 'M', '摷'), + (0x2F8C7, 'M', '㩬'), + (0x2F8C8, 'M', '敏'), + (0x2F8C9, 'M', '敬'), + (0x2F8CA, 'M', '𣀊'), + (0x2F8CB, 'M', '旣'), + (0x2F8CC, 'M', '書'), + (0x2F8CD, 'M', '晉'), + (0x2F8CE, 'M', '㬙'), + (0x2F8CF, 'M', '暑'), + (0x2F8D0, 'M', '㬈'), + (0x2F8D1, 'M', '㫤'), + (0x2F8D2, 'M', '冒'), + (0x2F8D3, 'M', '冕'), + (0x2F8D4, 'M', '最'), + (0x2F8D5, 'M', '暜'), + (0x2F8D6, 'M', '肭'), + (0x2F8D7, 'M', '䏙'), + (0x2F8D8, 'M', '朗'), + (0x2F8D9, 'M', '望'), + (0x2F8DA, 'M', '朡'), + (0x2F8DB, 'M', '杞'), + (0x2F8DC, 'M', '杓'), + (0x2F8DD, 'M', '𣏃'), + (0x2F8DE, 'M', '㭉'), + (0x2F8DF, 'M', '柺'), + (0x2F8E0, 'M', '枅'), + (0x2F8E1, 'M', '桒'), + (0x2F8E2, 'M', '梅'), + (0x2F8E3, 'M', '𣑭'), + (0x2F8E4, 'M', '梎'), + (0x2F8E5, 'M', '栟'), + (0x2F8E6, 'M', '椔'), + (0x2F8E7, 'M', '㮝'), + (0x2F8E8, 'M', '楂'), + (0x2F8E9, 'M', '榣'), + (0x2F8EA, 'M', '槪'), + (0x2F8EB, 'M', '檨'), + (0x2F8EC, 'M', '𣚣'), + (0x2F8ED, 'M', '櫛'), + (0x2F8EE, 'M', '㰘'), + (0x2F8EF, 'M', '次'), + (0x2F8F0, 'M', '𣢧'), + (0x2F8F1, 'M', '歔'), + (0x2F8F2, 'M', '㱎'), + (0x2F8F3, 'M', '歲'), + (0x2F8F4, 'M', '殟'), + (0x2F8F5, 'M', '殺'), + (0x2F8F6, 'M', '殻'), + (0x2F8F7, 'M', '𣪍'), + (0x2F8F8, 'M', '𡴋'), + (0x2F8F9, 'M', '𣫺'), + (0x2F8FA, 'M', '汎'), + (0x2F8FB, 'M', '𣲼'), + (0x2F8FC, 'M', '沿'), + (0x2F8FD, 'M', '泍'), + (0x2F8FE, 'M', '汧'), + (0x2F8FF, 'M', '洖'), + (0x2F900, 'M', '派'), + (0x2F901, 'M', '海'), + (0x2F902, 'M', '流'), + (0x2F903, 'M', '浩'), + (0x2F904, 'M', '浸'), + (0x2F905, 'M', '涅'), + (0x2F906, 'M', '𣴞'), + (0x2F907, 'M', '洴'), + (0x2F908, 'M', '港'), + (0x2F909, 'M', '湮'), + (0x2F90A, 'M', '㴳'), + (0x2F90B, 'M', '滋'), + (0x2F90C, 'M', '滇'), + (0x2F90D, 'M', '𣻑'), + (0x2F90E, 'M', '淹'), + (0x2F90F, 'M', '潮'), + (0x2F910, 'M', '𣽞'), + (0x2F911, 'M', '𣾎'), + (0x2F912, 'M', '濆'), + (0x2F913, 'M', '瀹'), + (0x2F914, 'M', '瀞'), + (0x2F915, 'M', '瀛'), + (0x2F916, 'M', '㶖'), + (0x2F917, 'M', '灊'), + (0x2F918, 'M', '災'), + (0x2F919, 'M', '灷'), + (0x2F91A, 'M', '炭'), + (0x2F91B, 'M', '𠔥'), + (0x2F91C, 'M', '煅'), + (0x2F91D, 'M', '𤉣'), + (0x2F91E, 'M', '熜'), (0x2F91F, 'X'), - (0x2F920, 'M', u'爨'), - (0x2F921, 'M', u'爵'), - (0x2F922, 'M', u'牐'), - (0x2F923, 'M', u'𤘈'), - (0x2F924, 'M', u'犀'), - (0x2F925, 'M', u'犕'), - (0x2F926, 'M', u'𤜵'), - (0x2F927, 'M', u'𤠔'), - (0x2F928, 'M', u'獺'), - (0x2F929, 'M', u'王'), - (0x2F92A, 'M', u'㺬'), - (0x2F92B, 'M', u'玥'), - (0x2F92C, 'M', u'㺸'), - (0x2F92E, 'M', u'瑇'), - (0x2F92F, 'M', u'瑜'), - (0x2F930, 'M', u'瑱'), - (0x2F931, 'M', u'璅'), - (0x2F932, 'M', u'瓊'), - (0x2F933, 'M', u'㼛'), - (0x2F934, 'M', u'甤'), - (0x2F935, 'M', u'𤰶'), - (0x2F936, 'M', u'甾'), - (0x2F937, 'M', u'𤲒'), - (0x2F938, 'M', u'異'), - (0x2F939, 'M', u'𢆟'), - (0x2F93A, 'M', u'瘐'), - (0x2F93B, 'M', u'𤾡'), - (0x2F93C, 'M', u'𤾸'), - (0x2F93D, 'M', u'𥁄'), - (0x2F93E, 'M', u'㿼'), - (0x2F93F, 'M', u'䀈'), - (0x2F940, 'M', u'直'), - (0x2F941, 'M', u'𥃳'), - (0x2F942, 'M', u'𥃲'), - (0x2F943, 'M', u'𥄙'), - (0x2F944, 'M', u'𥄳'), - (0x2F945, 'M', u'眞'), - (0x2F946, 'M', u'真'), - (0x2F948, 'M', u'睊'), - (0x2F949, 'M', u'䀹'), - (0x2F94A, 'M', u'瞋'), - (0x2F94B, 'M', u'䁆'), - (0x2F94C, 'M', u'䂖'), - (0x2F94D, 'M', u'𥐝'), - (0x2F94E, 'M', u'硎'), + (0x2F920, 'M', '爨'), + (0x2F921, 'M', '爵'), + (0x2F922, 'M', '牐'), + (0x2F923, 'M', '𤘈'), + (0x2F924, 'M', '犀'), + (0x2F925, 'M', '犕'), + (0x2F926, 'M', '𤜵'), ] def _seg_77(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2F94F, 'M', u'碌'), - (0x2F950, 'M', u'磌'), - (0x2F951, 'M', u'䃣'), - (0x2F952, 'M', u'𥘦'), - (0x2F953, 'M', u'祖'), - (0x2F954, 'M', u'𥚚'), - (0x2F955, 'M', u'𥛅'), - (0x2F956, 'M', u'福'), - (0x2F957, 'M', u'秫'), - (0x2F958, 'M', u'䄯'), - (0x2F959, 'M', u'穀'), - (0x2F95A, 'M', u'穊'), - (0x2F95B, 'M', u'穏'), - (0x2F95C, 'M', u'𥥼'), - (0x2F95D, 'M', u'𥪧'), + (0x2F927, 'M', '𤠔'), + (0x2F928, 'M', '獺'), + (0x2F929, 'M', '王'), + (0x2F92A, 'M', '㺬'), + (0x2F92B, 'M', '玥'), + (0x2F92C, 'M', '㺸'), + (0x2F92E, 'M', '瑇'), + (0x2F92F, 'M', '瑜'), + (0x2F930, 'M', '瑱'), + (0x2F931, 'M', '璅'), + (0x2F932, 'M', '瓊'), + (0x2F933, 'M', '㼛'), + (0x2F934, 'M', '甤'), + (0x2F935, 'M', '𤰶'), + (0x2F936, 'M', '甾'), + (0x2F937, 'M', '𤲒'), + (0x2F938, 'M', '異'), + (0x2F939, 'M', '𢆟'), + (0x2F93A, 'M', '瘐'), + (0x2F93B, 'M', '𤾡'), + (0x2F93C, 'M', '𤾸'), + (0x2F93D, 'M', '𥁄'), + (0x2F93E, 'M', '㿼'), + (0x2F93F, 'M', '䀈'), + (0x2F940, 'M', '直'), + (0x2F941, 'M', '𥃳'), + (0x2F942, 'M', '𥃲'), + (0x2F943, 'M', '𥄙'), + (0x2F944, 'M', '𥄳'), + (0x2F945, 'M', '眞'), + (0x2F946, 'M', '真'), + (0x2F948, 'M', '睊'), + (0x2F949, 'M', '䀹'), + (0x2F94A, 'M', '瞋'), + (0x2F94B, 'M', '䁆'), + (0x2F94C, 'M', '䂖'), + (0x2F94D, 'M', '𥐝'), + (0x2F94E, 'M', '硎'), + (0x2F94F, 'M', '碌'), + (0x2F950, 'M', '磌'), + (0x2F951, 'M', '䃣'), + (0x2F952, 'M', '𥘦'), + (0x2F953, 'M', '祖'), + (0x2F954, 'M', '𥚚'), + (0x2F955, 'M', '𥛅'), + (0x2F956, 'M', '福'), + (0x2F957, 'M', '秫'), + (0x2F958, 'M', '䄯'), + (0x2F959, 'M', '穀'), + (0x2F95A, 'M', '穊'), + (0x2F95B, 'M', '穏'), + (0x2F95C, 'M', '𥥼'), + (0x2F95D, 'M', '𥪧'), (0x2F95F, 'X'), - (0x2F960, 'M', u'䈂'), - (0x2F961, 'M', u'𥮫'), - (0x2F962, 'M', u'篆'), - (0x2F963, 'M', u'築'), - (0x2F964, 'M', u'䈧'), - (0x2F965, 'M', u'𥲀'), - (0x2F966, 'M', u'糒'), - (0x2F967, 'M', u'䊠'), - (0x2F968, 'M', u'糨'), - (0x2F969, 'M', u'糣'), - (0x2F96A, 'M', u'紀'), - (0x2F96B, 'M', u'𥾆'), - (0x2F96C, 'M', u'絣'), - (0x2F96D, 'M', u'䌁'), - (0x2F96E, 'M', u'緇'), - (0x2F96F, 'M', u'縂'), - (0x2F970, 'M', u'繅'), - (0x2F971, 'M', u'䌴'), - (0x2F972, 'M', u'𦈨'), - (0x2F973, 'M', u'𦉇'), - (0x2F974, 'M', u'䍙'), - (0x2F975, 'M', u'𦋙'), - (0x2F976, 'M', u'罺'), - (0x2F977, 'M', u'𦌾'), - (0x2F978, 'M', u'羕'), - (0x2F979, 'M', u'翺'), - (0x2F97A, 'M', u'者'), - (0x2F97B, 'M', u'𦓚'), - (0x2F97C, 'M', u'𦔣'), - (0x2F97D, 'M', u'聠'), - (0x2F97E, 'M', u'𦖨'), - (0x2F97F, 'M', u'聰'), - (0x2F980, 'M', u'𣍟'), - (0x2F981, 'M', u'䏕'), - (0x2F982, 'M', u'育'), - (0x2F983, 'M', u'脃'), - (0x2F984, 'M', u'䐋'), - (0x2F985, 'M', u'脾'), - (0x2F986, 'M', u'媵'), - (0x2F987, 'M', u'𦞧'), - (0x2F988, 'M', u'𦞵'), - (0x2F989, 'M', u'𣎓'), - (0x2F98A, 'M', u'𣎜'), - (0x2F98B, 'M', u'舁'), - (0x2F98C, 'M', u'舄'), - (0x2F98D, 'M', u'辞'), - (0x2F98E, 'M', u'䑫'), - (0x2F98F, 'M', u'芑'), - (0x2F990, 'M', u'芋'), - (0x2F991, 'M', u'芝'), - (0x2F992, 'M', u'劳'), - (0x2F993, 'M', u'花'), - (0x2F994, 'M', u'芳'), - (0x2F995, 'M', u'芽'), - (0x2F996, 'M', u'苦'), - (0x2F997, 'M', u'𦬼'), - (0x2F998, 'M', u'若'), - (0x2F999, 'M', u'茝'), - (0x2F99A, 'M', u'荣'), - (0x2F99B, 'M', u'莭'), - (0x2F99C, 'M', u'茣'), - (0x2F99D, 'M', u'莽'), - (0x2F99E, 'M', u'菧'), - (0x2F99F, 'M', u'著'), - (0x2F9A0, 'M', u'荓'), - (0x2F9A1, 'M', u'菊'), - (0x2F9A2, 'M', u'菌'), - (0x2F9A3, 'M', u'菜'), - (0x2F9A4, 'M', u'𦰶'), - (0x2F9A5, 'M', u'𦵫'), - (0x2F9A6, 'M', u'𦳕'), - (0x2F9A7, 'M', u'䔫'), - (0x2F9A8, 'M', u'蓱'), - (0x2F9A9, 'M', u'蓳'), - (0x2F9AA, 'M', u'蔖'), - (0x2F9AB, 'M', u'𧏊'), - (0x2F9AC, 'M', u'蕤'), - (0x2F9AD, 'M', u'𦼬'), - (0x2F9AE, 'M', u'䕝'), - (0x2F9AF, 'M', u'䕡'), - (0x2F9B0, 'M', u'𦾱'), - (0x2F9B1, 'M', u'𧃒'), - (0x2F9B2, 'M', u'䕫'), - (0x2F9B3, 'M', u'虐'), + (0x2F960, 'M', '䈂'), + (0x2F961, 'M', '𥮫'), + (0x2F962, 'M', '篆'), + (0x2F963, 'M', '築'), + (0x2F964, 'M', '䈧'), + (0x2F965, 'M', '𥲀'), + (0x2F966, 'M', '糒'), + (0x2F967, 'M', '䊠'), + (0x2F968, 'M', '糨'), + (0x2F969, 'M', '糣'), + (0x2F96A, 'M', '紀'), + (0x2F96B, 'M', '𥾆'), + (0x2F96C, 'M', '絣'), + (0x2F96D, 'M', '䌁'), + (0x2F96E, 'M', '緇'), + (0x2F96F, 'M', '縂'), + (0x2F970, 'M', '繅'), + (0x2F971, 'M', '䌴'), + (0x2F972, 'M', '𦈨'), + (0x2F973, 'M', '𦉇'), + (0x2F974, 'M', '䍙'), + (0x2F975, 'M', '𦋙'), + (0x2F976, 'M', '罺'), + (0x2F977, 'M', '𦌾'), + (0x2F978, 'M', '羕'), + (0x2F979, 'M', '翺'), + (0x2F97A, 'M', '者'), + (0x2F97B, 'M', '𦓚'), + (0x2F97C, 'M', '𦔣'), + (0x2F97D, 'M', '聠'), + (0x2F97E, 'M', '𦖨'), + (0x2F97F, 'M', '聰'), + (0x2F980, 'M', '𣍟'), + (0x2F981, 'M', '䏕'), + (0x2F982, 'M', '育'), + (0x2F983, 'M', '脃'), + (0x2F984, 'M', '䐋'), + (0x2F985, 'M', '脾'), + (0x2F986, 'M', '媵'), + (0x2F987, 'M', '𦞧'), + (0x2F988, 'M', '𦞵'), + (0x2F989, 'M', '𣎓'), + (0x2F98A, 'M', '𣎜'), + (0x2F98B, 'M', '舁'), + (0x2F98C, 'M', '舄'), + (0x2F98D, 'M', '辞'), ] def _seg_78(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2F9B4, 'M', u'虜'), - (0x2F9B5, 'M', u'虧'), - (0x2F9B6, 'M', u'虩'), - (0x2F9B7, 'M', u'蚩'), - (0x2F9B8, 'M', u'蚈'), - (0x2F9B9, 'M', u'蜎'), - (0x2F9BA, 'M', u'蛢'), - (0x2F9BB, 'M', u'蝹'), - (0x2F9BC, 'M', u'蜨'), - (0x2F9BD, 'M', u'蝫'), - (0x2F9BE, 'M', u'螆'), + (0x2F98E, 'M', '䑫'), + (0x2F98F, 'M', '芑'), + (0x2F990, 'M', '芋'), + (0x2F991, 'M', '芝'), + (0x2F992, 'M', '劳'), + (0x2F993, 'M', '花'), + (0x2F994, 'M', '芳'), + (0x2F995, 'M', '芽'), + (0x2F996, 'M', '苦'), + (0x2F997, 'M', '𦬼'), + (0x2F998, 'M', '若'), + (0x2F999, 'M', '茝'), + (0x2F99A, 'M', '荣'), + (0x2F99B, 'M', '莭'), + (0x2F99C, 'M', '茣'), + (0x2F99D, 'M', '莽'), + (0x2F99E, 'M', '菧'), + (0x2F99F, 'M', '著'), + (0x2F9A0, 'M', '荓'), + (0x2F9A1, 'M', '菊'), + (0x2F9A2, 'M', '菌'), + (0x2F9A3, 'M', '菜'), + (0x2F9A4, 'M', '𦰶'), + (0x2F9A5, 'M', '𦵫'), + (0x2F9A6, 'M', '𦳕'), + (0x2F9A7, 'M', '䔫'), + (0x2F9A8, 'M', '蓱'), + (0x2F9A9, 'M', '蓳'), + (0x2F9AA, 'M', '蔖'), + (0x2F9AB, 'M', '𧏊'), + (0x2F9AC, 'M', '蕤'), + (0x2F9AD, 'M', '𦼬'), + (0x2F9AE, 'M', '䕝'), + (0x2F9AF, 'M', '䕡'), + (0x2F9B0, 'M', '𦾱'), + (0x2F9B1, 'M', '𧃒'), + (0x2F9B2, 'M', '䕫'), + (0x2F9B3, 'M', '虐'), + (0x2F9B4, 'M', '虜'), + (0x2F9B5, 'M', '虧'), + (0x2F9B6, 'M', '虩'), + (0x2F9B7, 'M', '蚩'), + (0x2F9B8, 'M', '蚈'), + (0x2F9B9, 'M', '蜎'), + (0x2F9BA, 'M', '蛢'), + (0x2F9BB, 'M', '蝹'), + (0x2F9BC, 'M', '蜨'), + (0x2F9BD, 'M', '蝫'), + (0x2F9BE, 'M', '螆'), (0x2F9BF, 'X'), - (0x2F9C0, 'M', u'蟡'), - (0x2F9C1, 'M', u'蠁'), - (0x2F9C2, 'M', u'䗹'), - (0x2F9C3, 'M', u'衠'), - (0x2F9C4, 'M', u'衣'), - (0x2F9C5, 'M', u'𧙧'), - (0x2F9C6, 'M', u'裗'), - (0x2F9C7, 'M', u'裞'), - (0x2F9C8, 'M', u'䘵'), - (0x2F9C9, 'M', u'裺'), - (0x2F9CA, 'M', u'㒻'), - (0x2F9CB, 'M', u'𧢮'), - (0x2F9CC, 'M', u'𧥦'), - (0x2F9CD, 'M', u'䚾'), - (0x2F9CE, 'M', u'䛇'), - (0x2F9CF, 'M', u'誠'), - (0x2F9D0, 'M', u'諭'), - (0x2F9D1, 'M', u'變'), - (0x2F9D2, 'M', u'豕'), - (0x2F9D3, 'M', u'𧲨'), - (0x2F9D4, 'M', u'貫'), - (0x2F9D5, 'M', u'賁'), - (0x2F9D6, 'M', u'贛'), - (0x2F9D7, 'M', u'起'), - (0x2F9D8, 'M', u'𧼯'), - (0x2F9D9, 'M', u'𠠄'), - (0x2F9DA, 'M', u'跋'), - (0x2F9DB, 'M', u'趼'), - (0x2F9DC, 'M', u'跰'), - (0x2F9DD, 'M', u'𠣞'), - (0x2F9DE, 'M', u'軔'), - (0x2F9DF, 'M', u'輸'), - (0x2F9E0, 'M', u'𨗒'), - (0x2F9E1, 'M', u'𨗭'), - (0x2F9E2, 'M', u'邔'), - (0x2F9E3, 'M', u'郱'), - (0x2F9E4, 'M', u'鄑'), - (0x2F9E5, 'M', u'𨜮'), - (0x2F9E6, 'M', u'鄛'), - (0x2F9E7, 'M', u'鈸'), - (0x2F9E8, 'M', u'鋗'), - (0x2F9E9, 'M', u'鋘'), - (0x2F9EA, 'M', u'鉼'), - (0x2F9EB, 'M', u'鏹'), - (0x2F9EC, 'M', u'鐕'), - (0x2F9ED, 'M', u'𨯺'), - (0x2F9EE, 'M', u'開'), - (0x2F9EF, 'M', u'䦕'), - (0x2F9F0, 'M', u'閷'), - (0x2F9F1, 'M', u'𨵷'), - (0x2F9F2, 'M', u'䧦'), - (0x2F9F3, 'M', u'雃'), - (0x2F9F4, 'M', u'嶲'), - (0x2F9F5, 'M', u'霣'), - (0x2F9F6, 'M', u'𩅅'), - (0x2F9F7, 'M', u'𩈚'), - (0x2F9F8, 'M', u'䩮'), - (0x2F9F9, 'M', u'䩶'), - (0x2F9FA, 'M', u'韠'), - (0x2F9FB, 'M', u'𩐊'), - (0x2F9FC, 'M', u'䪲'), - (0x2F9FD, 'M', u'𩒖'), - (0x2F9FE, 'M', u'頋'), - (0x2FA00, 'M', u'頩'), - (0x2FA01, 'M', u'𩖶'), - (0x2FA02, 'M', u'飢'), - (0x2FA03, 'M', u'䬳'), - (0x2FA04, 'M', u'餩'), - (0x2FA05, 'M', u'馧'), - (0x2FA06, 'M', u'駂'), - (0x2FA07, 'M', u'駾'), - (0x2FA08, 'M', u'䯎'), - (0x2FA09, 'M', u'𩬰'), - (0x2FA0A, 'M', u'鬒'), - (0x2FA0B, 'M', u'鱀'), - (0x2FA0C, 'M', u'鳽'), - (0x2FA0D, 'M', u'䳎'), - (0x2FA0E, 'M', u'䳭'), - (0x2FA0F, 'M', u'鵧'), - (0x2FA10, 'M', u'𪃎'), - (0x2FA11, 'M', u'䳸'), - (0x2FA12, 'M', u'𪄅'), - (0x2FA13, 'M', u'𪈎'), - (0x2FA14, 'M', u'𪊑'), - (0x2FA15, 'M', u'麻'), - (0x2FA16, 'M', u'䵖'), - (0x2FA17, 'M', u'黹'), - (0x2FA18, 'M', u'黾'), + (0x2F9C0, 'M', '蟡'), + (0x2F9C1, 'M', '蠁'), + (0x2F9C2, 'M', '䗹'), + (0x2F9C3, 'M', '衠'), + (0x2F9C4, 'M', '衣'), + (0x2F9C5, 'M', '𧙧'), + (0x2F9C6, 'M', '裗'), + (0x2F9C7, 'M', '裞'), + (0x2F9C8, 'M', '䘵'), + (0x2F9C9, 'M', '裺'), + (0x2F9CA, 'M', '㒻'), + (0x2F9CB, 'M', '𧢮'), + (0x2F9CC, 'M', '𧥦'), + (0x2F9CD, 'M', '䚾'), + (0x2F9CE, 'M', '䛇'), + (0x2F9CF, 'M', '誠'), + (0x2F9D0, 'M', '諭'), + (0x2F9D1, 'M', '變'), + (0x2F9D2, 'M', '豕'), + (0x2F9D3, 'M', '𧲨'), + (0x2F9D4, 'M', '貫'), + (0x2F9D5, 'M', '賁'), + (0x2F9D6, 'M', '贛'), + (0x2F9D7, 'M', '起'), + (0x2F9D8, 'M', '𧼯'), + (0x2F9D9, 'M', '𠠄'), + (0x2F9DA, 'M', '跋'), + (0x2F9DB, 'M', '趼'), + (0x2F9DC, 'M', '跰'), + (0x2F9DD, 'M', '𠣞'), + (0x2F9DE, 'M', '軔'), + (0x2F9DF, 'M', '輸'), + (0x2F9E0, 'M', '𨗒'), + (0x2F9E1, 'M', '𨗭'), + (0x2F9E2, 'M', '邔'), + (0x2F9E3, 'M', '郱'), + (0x2F9E4, 'M', '鄑'), + (0x2F9E5, 'M', '𨜮'), + (0x2F9E6, 'M', '鄛'), + (0x2F9E7, 'M', '鈸'), + (0x2F9E8, 'M', '鋗'), + (0x2F9E9, 'M', '鋘'), + (0x2F9EA, 'M', '鉼'), + (0x2F9EB, 'M', '鏹'), + (0x2F9EC, 'M', '鐕'), + (0x2F9ED, 'M', '𨯺'), + (0x2F9EE, 'M', '開'), + (0x2F9EF, 'M', '䦕'), + (0x2F9F0, 'M', '閷'), + (0x2F9F1, 'M', '𨵷'), ] def _seg_79(): + # type: () -> List[Union[Tuple[int, str], Tuple[int, str, str]]] return [ - (0x2FA19, 'M', u'鼅'), - (0x2FA1A, 'M', u'鼏'), - (0x2FA1B, 'M', u'鼖'), - (0x2FA1C, 'M', u'鼻'), - (0x2FA1D, 'M', u'𪘀'), + (0x2F9F2, 'M', '䧦'), + (0x2F9F3, 'M', '雃'), + (0x2F9F4, 'M', '嶲'), + (0x2F9F5, 'M', '霣'), + (0x2F9F6, 'M', '𩅅'), + (0x2F9F7, 'M', '𩈚'), + (0x2F9F8, 'M', '䩮'), + (0x2F9F9, 'M', '䩶'), + (0x2F9FA, 'M', '韠'), + (0x2F9FB, 'M', '𩐊'), + (0x2F9FC, 'M', '䪲'), + (0x2F9FD, 'M', '𩒖'), + (0x2F9FE, 'M', '頋'), + (0x2FA00, 'M', '頩'), + (0x2FA01, 'M', '𩖶'), + (0x2FA02, 'M', '飢'), + (0x2FA03, 'M', '䬳'), + (0x2FA04, 'M', '餩'), + (0x2FA05, 'M', '馧'), + (0x2FA06, 'M', '駂'), + (0x2FA07, 'M', '駾'), + (0x2FA08, 'M', '䯎'), + (0x2FA09, 'M', '𩬰'), + (0x2FA0A, 'M', '鬒'), + (0x2FA0B, 'M', '鱀'), + (0x2FA0C, 'M', '鳽'), + (0x2FA0D, 'M', '䳎'), + (0x2FA0E, 'M', '䳭'), + (0x2FA0F, 'M', '鵧'), + (0x2FA10, 'M', '𪃎'), + (0x2FA11, 'M', '䳸'), + (0x2FA12, 'M', '𪄅'), + (0x2FA13, 'M', '𪈎'), + (0x2FA14, 'M', '𪊑'), + (0x2FA15, 'M', '麻'), + (0x2FA16, 'M', '䵖'), + (0x2FA17, 'M', '黹'), + (0x2FA18, 'M', '黾'), + (0x2FA19, 'M', '鼅'), + (0x2FA1A, 'M', '鼏'), + (0x2FA1B, 'M', '鼖'), + (0x2FA1C, 'M', '鼻'), + (0x2FA1D, 'M', '𪘀'), (0x2FA1E, 'X'), + (0x30000, 'V'), + (0x3134B, 'X'), (0xE0100, 'I'), (0xE01F0, 'X'), ] @@ -8314,4 +8435,4 @@ uts46data = tuple( + _seg_77() + _seg_78() + _seg_79() -) +) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/venv/Lib/site-packages/pip/_vendor/ipaddress.py b/venv/Lib/site-packages/pip/_vendor/ipaddress.py deleted file mode 100644 index 3e6f9e499c32bc2c93273545dec1a7ebcb088e42..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/ipaddress.py +++ /dev/null @@ -1,2420 +0,0 @@ -# Copyright 2007 Google Inc. -# Licensed to PSF under a Contributor Agreement. - -"""A fast, lightweight IPv4/IPv6 manipulation library in Python. - -This library is used to create/poke/manipulate IPv4 and IPv6 addresses -and networks. - -""" - -from __future__ import unicode_literals - - -import itertools -import struct - -__version__ = '1.0.23' - -# Compatibility functions -_compat_int_types = (int,) -try: - _compat_int_types = (int, long) -except NameError: - pass -try: - _compat_str = unicode -except NameError: - _compat_str = str - assert bytes != str -if b'\0'[0] == 0: # Python 3 semantics - def _compat_bytes_to_byte_vals(byt): - return byt -else: - def _compat_bytes_to_byte_vals(byt): - return [struct.unpack(b'!B', b)[0] for b in byt] -try: - _compat_int_from_byte_vals = int.from_bytes -except AttributeError: - def _compat_int_from_byte_vals(bytvals, endianess): - assert endianess == 'big' - res = 0 - for bv in bytvals: - assert isinstance(bv, _compat_int_types) - res = (res << 8) + bv - return res - - -def _compat_to_bytes(intval, length, endianess): - assert isinstance(intval, _compat_int_types) - assert endianess == 'big' - if length == 4: - if intval < 0 or intval >= 2 ** 32: - raise struct.error("integer out of range for 'I' format code") - return struct.pack(b'!I', intval) - elif length == 16: - if intval < 0 or intval >= 2 ** 128: - raise struct.error("integer out of range for 'QQ' format code") - return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff) - else: - raise NotImplementedError() - - -if hasattr(int, 'bit_length'): - # Not int.bit_length , since that won't work in 2.7 where long exists - def _compat_bit_length(i): - return i.bit_length() -else: - def _compat_bit_length(i): - for res in itertools.count(): - if i >> res == 0: - return res - - -def _compat_range(start, end, step=1): - assert step > 0 - i = start - while i < end: - yield i - i += step - - -class _TotalOrderingMixin(object): - __slots__ = () - - # Helper that derives the other comparison operations from - # __lt__ and __eq__ - # We avoid functools.total_ordering because it doesn't handle - # NotImplemented correctly yet (http://bugs.python.org/issue10042) - def __eq__(self, other): - raise NotImplementedError - - def __ne__(self, other): - equal = self.__eq__(other) - if equal is NotImplemented: - return NotImplemented - return not equal - - def __lt__(self, other): - raise NotImplementedError - - def __le__(self, other): - less = self.__lt__(other) - if less is NotImplemented or not less: - return self.__eq__(other) - return less - - def __gt__(self, other): - less = self.__lt__(other) - if less is NotImplemented: - return NotImplemented - equal = self.__eq__(other) - if equal is NotImplemented: - return NotImplemented - return not (less or equal) - - def __ge__(self, other): - less = self.__lt__(other) - if less is NotImplemented: - return NotImplemented - return not less - - -IPV4LENGTH = 32 -IPV6LENGTH = 128 - - -class AddressValueError(ValueError): - """A Value Error related to the address.""" - - -class NetmaskValueError(ValueError): - """A Value Error related to the netmask.""" - - -def ip_address(address): - """Take an IP string/int and return an object of the correct type. - - Args: - address: A string or integer, the IP address. Either IPv4 or - IPv6 addresses may be supplied; integers less than 2**32 will - be considered to be IPv4 by default. - - Returns: - An IPv4Address or IPv6Address object. - - Raises: - ValueError: if the *address* passed isn't either a v4 or a v6 - address - - """ - try: - return IPv4Address(address) - except (AddressValueError, NetmaskValueError): - pass - - try: - return IPv6Address(address) - except (AddressValueError, NetmaskValueError): - pass - - if isinstance(address, bytes): - raise AddressValueError( - '%r does not appear to be an IPv4 or IPv6 address. ' - 'Did you pass in a bytes (str in Python 2) instead of' - ' a unicode object?' % address) - - raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % - address) - - -def ip_network(address, strict=True): - """Take an IP string/int and return an object of the correct type. - - Args: - address: A string or integer, the IP network. Either IPv4 or - IPv6 networks may be supplied; integers less than 2**32 will - be considered to be IPv4 by default. - - Returns: - An IPv4Network or IPv6Network object. - - Raises: - ValueError: if the string passed isn't either a v4 or a v6 - address. Or if the network has host bits set. - - """ - try: - return IPv4Network(address, strict) - except (AddressValueError, NetmaskValueError): - pass - - try: - return IPv6Network(address, strict) - except (AddressValueError, NetmaskValueError): - pass - - if isinstance(address, bytes): - raise AddressValueError( - '%r does not appear to be an IPv4 or IPv6 network. ' - 'Did you pass in a bytes (str in Python 2) instead of' - ' a unicode object?' % address) - - raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % - address) - - -def ip_interface(address): - """Take an IP string/int and return an object of the correct type. - - Args: - address: A string or integer, the IP address. Either IPv4 or - IPv6 addresses may be supplied; integers less than 2**32 will - be considered to be IPv4 by default. - - Returns: - An IPv4Interface or IPv6Interface object. - - Raises: - ValueError: if the string passed isn't either a v4 or a v6 - address. - - Notes: - The IPv?Interface classes describe an Address on a particular - Network, so they're basically a combination of both the Address - and Network classes. - - """ - try: - return IPv4Interface(address) - except (AddressValueError, NetmaskValueError): - pass - - try: - return IPv6Interface(address) - except (AddressValueError, NetmaskValueError): - pass - - raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' % - address) - - -def v4_int_to_packed(address): - """Represent an address as 4 packed bytes in network (big-endian) order. - - Args: - address: An integer representation of an IPv4 IP address. - - Returns: - The integer address packed as 4 bytes in network (big-endian) order. - - Raises: - ValueError: If the integer is negative or too large to be an - IPv4 IP address. - - """ - try: - return _compat_to_bytes(address, 4, 'big') - except (struct.error, OverflowError): - raise ValueError("Address negative or too large for IPv4") - - -def v6_int_to_packed(address): - """Represent an address as 16 packed bytes in network (big-endian) order. - - Args: - address: An integer representation of an IPv6 IP address. - - Returns: - The integer address packed as 16 bytes in network (big-endian) order. - - """ - try: - return _compat_to_bytes(address, 16, 'big') - except (struct.error, OverflowError): - raise ValueError("Address negative or too large for IPv6") - - -def _split_optional_netmask(address): - """Helper to split the netmask and raise AddressValueError if needed""" - addr = _compat_str(address).split('/') - if len(addr) > 2: - raise AddressValueError("Only one '/' permitted in %r" % address) - return addr - - -def _find_address_range(addresses): - """Find a sequence of sorted deduplicated IPv#Address. - - Args: - addresses: a list of IPv#Address objects. - - Yields: - A tuple containing the first and last IP addresses in the sequence. - - """ - it = iter(addresses) - first = last = next(it) - for ip in it: - if ip._ip != last._ip + 1: - yield first, last - first = ip - last = ip - yield first, last - - -def _count_righthand_zero_bits(number, bits): - """Count the number of zero bits on the right hand side. - - Args: - number: an integer. - bits: maximum number of bits to count. - - Returns: - The number of zero bits on the right hand side of the number. - - """ - if number == 0: - return bits - return min(bits, _compat_bit_length(~number & (number - 1))) - - -def summarize_address_range(first, last): - """Summarize a network range given the first and last IP addresses. - - Example: - >>> list(summarize_address_range(IPv4Address('192.0.2.0'), - ... IPv4Address('192.0.2.130'))) - ... #doctest: +NORMALIZE_WHITESPACE - [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), - IPv4Network('192.0.2.130/32')] - - Args: - first: the first IPv4Address or IPv6Address in the range. - last: the last IPv4Address or IPv6Address in the range. - - Returns: - An iterator of the summarized IPv(4|6) network objects. - - Raise: - TypeError: - If the first and last objects are not IP addresses. - If the first and last objects are not the same version. - ValueError: - If the last object is not greater than the first. - If the version of the first address is not 4 or 6. - - """ - if (not (isinstance(first, _BaseAddress) and - isinstance(last, _BaseAddress))): - raise TypeError('first and last must be IP addresses, not networks') - if first.version != last.version: - raise TypeError("%s and %s are not of the same version" % ( - first, last)) - if first > last: - raise ValueError('last IP address must be greater than first') - - if first.version == 4: - ip = IPv4Network - elif first.version == 6: - ip = IPv6Network - else: - raise ValueError('unknown IP version') - - ip_bits = first._max_prefixlen - first_int = first._ip - last_int = last._ip - while first_int <= last_int: - nbits = min(_count_righthand_zero_bits(first_int, ip_bits), - _compat_bit_length(last_int - first_int + 1) - 1) - net = ip((first_int, ip_bits - nbits)) - yield net - first_int += 1 << nbits - if first_int - 1 == ip._ALL_ONES: - break - - -def _collapse_addresses_internal(addresses): - """Loops through the addresses, collapsing concurrent netblocks. - - Example: - - ip1 = IPv4Network('192.0.2.0/26') - ip2 = IPv4Network('192.0.2.64/26') - ip3 = IPv4Network('192.0.2.128/26') - ip4 = IPv4Network('192.0.2.192/26') - - _collapse_addresses_internal([ip1, ip2, ip3, ip4]) -> - [IPv4Network('192.0.2.0/24')] - - This shouldn't be called directly; it is called via - collapse_addresses([]). - - Args: - addresses: A list of IPv4Network's or IPv6Network's - - Returns: - A list of IPv4Network's or IPv6Network's depending on what we were - passed. - - """ - # First merge - to_merge = list(addresses) - subnets = {} - while to_merge: - net = to_merge.pop() - supernet = net.supernet() - existing = subnets.get(supernet) - if existing is None: - subnets[supernet] = net - elif existing != net: - # Merge consecutive subnets - del subnets[supernet] - to_merge.append(supernet) - # Then iterate over resulting networks, skipping subsumed subnets - last = None - for net in sorted(subnets.values()): - if last is not None: - # Since they are sorted, - # last.network_address <= net.network_address is a given. - if last.broadcast_address >= net.broadcast_address: - continue - yield net - last = net - - -def collapse_addresses(addresses): - """Collapse a list of IP objects. - - Example: - collapse_addresses([IPv4Network('192.0.2.0/25'), - IPv4Network('192.0.2.128/25')]) -> - [IPv4Network('192.0.2.0/24')] - - Args: - addresses: An iterator of IPv4Network or IPv6Network objects. - - Returns: - An iterator of the collapsed IPv(4|6)Network objects. - - Raises: - TypeError: If passed a list of mixed version objects. - - """ - addrs = [] - ips = [] - nets = [] - - # split IP addresses and networks - for ip in addresses: - if isinstance(ip, _BaseAddress): - if ips and ips[-1]._version != ip._version: - raise TypeError("%s and %s are not of the same version" % ( - ip, ips[-1])) - ips.append(ip) - elif ip._prefixlen == ip._max_prefixlen: - if ips and ips[-1]._version != ip._version: - raise TypeError("%s and %s are not of the same version" % ( - ip, ips[-1])) - try: - ips.append(ip.ip) - except AttributeError: - ips.append(ip.network_address) - else: - if nets and nets[-1]._version != ip._version: - raise TypeError("%s and %s are not of the same version" % ( - ip, nets[-1])) - nets.append(ip) - - # sort and dedup - ips = sorted(set(ips)) - - # find consecutive address ranges in the sorted sequence and summarize them - if ips: - for first, last in _find_address_range(ips): - addrs.extend(summarize_address_range(first, last)) - - return _collapse_addresses_internal(addrs + nets) - - -def get_mixed_type_key(obj): - """Return a key suitable for sorting between networks and addresses. - - Address and Network objects are not sortable by default; they're - fundamentally different so the expression - - IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24') - - doesn't make any sense. There are some times however, where you may wish - to have ipaddress sort these for you anyway. If you need to do this, you - can use this function as the key= argument to sorted(). - - Args: - obj: either a Network or Address object. - Returns: - appropriate key. - - """ - if isinstance(obj, _BaseNetwork): - return obj._get_networks_key() - elif isinstance(obj, _BaseAddress): - return obj._get_address_key() - return NotImplemented - - -class _IPAddressBase(_TotalOrderingMixin): - - """The mother class.""" - - __slots__ = () - - @property - def exploded(self): - """Return the longhand version of the IP address as a string.""" - return self._explode_shorthand_ip_string() - - @property - def compressed(self): - """Return the shorthand version of the IP address as a string.""" - return _compat_str(self) - - @property - def reverse_pointer(self): - """The name of the reverse DNS pointer for the IP address, e.g.: - >>> ipaddress.ip_address("127.0.0.1").reverse_pointer - '1.0.0.127.in-addr.arpa' - >>> ipaddress.ip_address("2001:db8::1").reverse_pointer - '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' - - """ - return self._reverse_pointer() - - @property - def version(self): - msg = '%200s has no version specified' % (type(self),) - raise NotImplementedError(msg) - - def _check_int_address(self, address): - if address < 0: - msg = "%d (< 0) is not permitted as an IPv%d address" - raise AddressValueError(msg % (address, self._version)) - if address > self._ALL_ONES: - msg = "%d (>= 2**%d) is not permitted as an IPv%d address" - raise AddressValueError(msg % (address, self._max_prefixlen, - self._version)) - - def _check_packed_address(self, address, expected_len): - address_len = len(address) - if address_len != expected_len: - msg = ( - '%r (len %d != %d) is not permitted as an IPv%d address. ' - 'Did you pass in a bytes (str in Python 2) instead of' - ' a unicode object?') - raise AddressValueError(msg % (address, address_len, - expected_len, self._version)) - - @classmethod - def _ip_int_from_prefix(cls, prefixlen): - """Turn the prefix length into a bitwise netmask - - Args: - prefixlen: An integer, the prefix length. - - Returns: - An integer. - - """ - return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen) - - @classmethod - def _prefix_from_ip_int(cls, ip_int): - """Return prefix length from the bitwise netmask. - - Args: - ip_int: An integer, the netmask in expanded bitwise format - - Returns: - An integer, the prefix length. - - Raises: - ValueError: If the input intermingles zeroes & ones - """ - trailing_zeroes = _count_righthand_zero_bits(ip_int, - cls._max_prefixlen) - prefixlen = cls._max_prefixlen - trailing_zeroes - leading_ones = ip_int >> trailing_zeroes - all_ones = (1 << prefixlen) - 1 - if leading_ones != all_ones: - byteslen = cls._max_prefixlen // 8 - details = _compat_to_bytes(ip_int, byteslen, 'big') - msg = 'Netmask pattern %r mixes zeroes & ones' - raise ValueError(msg % details) - return prefixlen - - @classmethod - def _report_invalid_netmask(cls, netmask_str): - msg = '%r is not a valid netmask' % netmask_str - raise NetmaskValueError(msg) - - @classmethod - def _prefix_from_prefix_string(cls, prefixlen_str): - """Return prefix length from a numeric string - - Args: - prefixlen_str: The string to be converted - - Returns: - An integer, the prefix length. - - Raises: - NetmaskValueError: If the input is not a valid netmask - """ - # int allows a leading +/- as well as surrounding whitespace, - # so we ensure that isn't the case - if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): - cls._report_invalid_netmask(prefixlen_str) - try: - prefixlen = int(prefixlen_str) - except ValueError: - cls._report_invalid_netmask(prefixlen_str) - if not (0 <= prefixlen <= cls._max_prefixlen): - cls._report_invalid_netmask(prefixlen_str) - return prefixlen - - @classmethod - def _prefix_from_ip_string(cls, ip_str): - """Turn a netmask/hostmask string into a prefix length - - Args: - ip_str: The netmask/hostmask to be converted - - Returns: - An integer, the prefix length. - - Raises: - NetmaskValueError: If the input is not a valid netmask/hostmask - """ - # Parse the netmask/hostmask like an IP address. - try: - ip_int = cls._ip_int_from_string(ip_str) - except AddressValueError: - cls._report_invalid_netmask(ip_str) - - # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). - # Note that the two ambiguous cases (all-ones and all-zeroes) are - # treated as netmasks. - try: - return cls._prefix_from_ip_int(ip_int) - except ValueError: - pass - - # Invert the bits, and try matching a /0+1+/ hostmask instead. - ip_int ^= cls._ALL_ONES - try: - return cls._prefix_from_ip_int(ip_int) - except ValueError: - cls._report_invalid_netmask(ip_str) - - def __reduce__(self): - return self.__class__, (_compat_str(self),) - - -class _BaseAddress(_IPAddressBase): - - """A generic IP object. - - This IP class contains the version independent methods which are - used by single IP addresses. - """ - - __slots__ = () - - def __int__(self): - return self._ip - - def __eq__(self, other): - try: - return (self._ip == other._ip and - self._version == other._version) - except AttributeError: - return NotImplemented - - def __lt__(self, other): - if not isinstance(other, _IPAddressBase): - return NotImplemented - if not isinstance(other, _BaseAddress): - raise TypeError('%s and %s are not of the same type' % ( - self, other)) - if self._version != other._version: - raise TypeError('%s and %s are not of the same version' % ( - self, other)) - if self._ip != other._ip: - return self._ip < other._ip - return False - - # Shorthand for Integer addition and subtraction. This is not - # meant to ever support addition/subtraction of addresses. - def __add__(self, other): - if not isinstance(other, _compat_int_types): - return NotImplemented - return self.__class__(int(self) + other) - - def __sub__(self, other): - if not isinstance(other, _compat_int_types): - return NotImplemented - return self.__class__(int(self) - other) - - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) - - def __str__(self): - return _compat_str(self._string_from_ip_int(self._ip)) - - def __hash__(self): - return hash(hex(int(self._ip))) - - def _get_address_key(self): - return (self._version, self) - - def __reduce__(self): - return self.__class__, (self._ip,) - - -class _BaseNetwork(_IPAddressBase): - - """A generic IP network object. - - This IP class contains the version independent methods which are - used by networks. - - """ - def __init__(self, address): - self._cache = {} - - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) - - def __str__(self): - return '%s/%d' % (self.network_address, self.prefixlen) - - def hosts(self): - """Generate Iterator over usable hosts in a network. - - This is like __iter__ except it doesn't return the network - or broadcast addresses. - - """ - network = int(self.network_address) - broadcast = int(self.broadcast_address) - for x in _compat_range(network + 1, broadcast): - yield self._address_class(x) - - def __iter__(self): - network = int(self.network_address) - broadcast = int(self.broadcast_address) - for x in _compat_range(network, broadcast + 1): - yield self._address_class(x) - - def __getitem__(self, n): - network = int(self.network_address) - broadcast = int(self.broadcast_address) - if n >= 0: - if network + n > broadcast: - raise IndexError('address out of range') - return self._address_class(network + n) - else: - n += 1 - if broadcast + n < network: - raise IndexError('address out of range') - return self._address_class(broadcast + n) - - def __lt__(self, other): - if not isinstance(other, _IPAddressBase): - return NotImplemented - if not isinstance(other, _BaseNetwork): - raise TypeError('%s and %s are not of the same type' % ( - self, other)) - if self._version != other._version: - raise TypeError('%s and %s are not of the same version' % ( - self, other)) - if self.network_address != other.network_address: - return self.network_address < other.network_address - if self.netmask != other.netmask: - return self.netmask < other.netmask - return False - - def __eq__(self, other): - try: - return (self._version == other._version and - self.network_address == other.network_address and - int(self.netmask) == int(other.netmask)) - except AttributeError: - return NotImplemented - - def __hash__(self): - return hash(int(self.network_address) ^ int(self.netmask)) - - def __contains__(self, other): - # always false if one is v4 and the other is v6. - if self._version != other._version: - return False - # dealing with another network. - if isinstance(other, _BaseNetwork): - return False - # dealing with another address - else: - # address - return (int(self.network_address) <= int(other._ip) <= - int(self.broadcast_address)) - - def overlaps(self, other): - """Tell if self is partly contained in other.""" - return self.network_address in other or ( - self.broadcast_address in other or ( - other.network_address in self or ( - other.broadcast_address in self))) - - @property - def broadcast_address(self): - x = self._cache.get('broadcast_address') - if x is None: - x = self._address_class(int(self.network_address) | - int(self.hostmask)) - self._cache['broadcast_address'] = x - return x - - @property - def hostmask(self): - x = self._cache.get('hostmask') - if x is None: - x = self._address_class(int(self.netmask) ^ self._ALL_ONES) - self._cache['hostmask'] = x - return x - - @property - def with_prefixlen(self): - return '%s/%d' % (self.network_address, self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (self.network_address, self.netmask) - - @property - def with_hostmask(self): - return '%s/%s' % (self.network_address, self.hostmask) - - @property - def num_addresses(self): - """Number of hosts in the current subnet.""" - return int(self.broadcast_address) - int(self.network_address) + 1 - - @property - def _address_class(self): - # Returning bare address objects (rather than interfaces) allows for - # more consistent behaviour across the network address, broadcast - # address and individual host addresses. - msg = '%200s has no associated address class' % (type(self),) - raise NotImplementedError(msg) - - @property - def prefixlen(self): - return self._prefixlen - - def address_exclude(self, other): - """Remove an address from a larger block. - - For example: - - addr1 = ip_network('192.0.2.0/28') - addr2 = ip_network('192.0.2.1/32') - list(addr1.address_exclude(addr2)) = - [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'), - IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')] - - or IPv6: - - addr1 = ip_network('2001:db8::1/32') - addr2 = ip_network('2001:db8::1/128') - list(addr1.address_exclude(addr2)) = - [ip_network('2001:db8::1/128'), - ip_network('2001:db8::2/127'), - ip_network('2001:db8::4/126'), - ip_network('2001:db8::8/125'), - ... - ip_network('2001:db8:8000::/33')] - - Args: - other: An IPv4Network or IPv6Network object of the same type. - - Returns: - An iterator of the IPv(4|6)Network objects which is self - minus other. - - Raises: - TypeError: If self and other are of differing address - versions, or if other is not a network object. - ValueError: If other is not completely contained by self. - - """ - if not self._version == other._version: - raise TypeError("%s and %s are not of the same version" % ( - self, other)) - - if not isinstance(other, _BaseNetwork): - raise TypeError("%s is not a network object" % other) - - if not other.subnet_of(self): - raise ValueError('%s not contained in %s' % (other, self)) - if other == self: - return - - # Make sure we're comparing the network of other. - other = other.__class__('%s/%s' % (other.network_address, - other.prefixlen)) - - s1, s2 = self.subnets() - while s1 != other and s2 != other: - if other.subnet_of(s1): - yield s2 - s1, s2 = s1.subnets() - elif other.subnet_of(s2): - yield s1 - s1, s2 = s2.subnets() - else: - # If we got here, there's a bug somewhere. - raise AssertionError('Error performing exclusion: ' - 's1: %s s2: %s other: %s' % - (s1, s2, other)) - if s1 == other: - yield s2 - elif s2 == other: - yield s1 - else: - # If we got here, there's a bug somewhere. - raise AssertionError('Error performing exclusion: ' - 's1: %s s2: %s other: %s' % - (s1, s2, other)) - - def compare_networks(self, other): - """Compare two IP objects. - - This is only concerned about the comparison of the integer - representation of the network addresses. This means that the - host bits aren't considered at all in this method. If you want - to compare host bits, you can easily enough do a - 'HostA._ip < HostB._ip' - - Args: - other: An IP object. - - Returns: - If the IP versions of self and other are the same, returns: - - -1 if self < other: - eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25') - IPv6Network('2001:db8::1000/124') < - IPv6Network('2001:db8::2000/124') - 0 if self == other - eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24') - IPv6Network('2001:db8::1000/124') == - IPv6Network('2001:db8::1000/124') - 1 if self > other - eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25') - IPv6Network('2001:db8::2000/124') > - IPv6Network('2001:db8::1000/124') - - Raises: - TypeError if the IP versions are different. - - """ - # does this need to raise a ValueError? - if self._version != other._version: - raise TypeError('%s and %s are not of the same type' % ( - self, other)) - # self._version == other._version below here: - if self.network_address < other.network_address: - return -1 - if self.network_address > other.network_address: - return 1 - # self.network_address == other.network_address below here: - if self.netmask < other.netmask: - return -1 - if self.netmask > other.netmask: - return 1 - return 0 - - def _get_networks_key(self): - """Network-only key function. - - Returns an object that identifies this address' network and - netmask. This function is a suitable "key" argument for sorted() - and list.sort(). - - """ - return (self._version, self.network_address, self.netmask) - - def subnets(self, prefixlen_diff=1, new_prefix=None): - """The subnets which join to make the current subnet. - - In the case that self contains only one IP - (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 - for IPv6), yield an iterator with just ourself. - - Args: - prefixlen_diff: An integer, the amount the prefix length - should be increased by. This should not be set if - new_prefix is also set. - new_prefix: The desired new prefix length. This must be a - larger number (smaller prefix) than the existing prefix. - This should not be set if prefixlen_diff is also set. - - Returns: - An iterator of IPv(4|6) objects. - - Raises: - ValueError: The prefixlen_diff is too small or too large. - OR - prefixlen_diff and new_prefix are both set or new_prefix - is a smaller number than the current prefix (smaller - number means a larger network) - - """ - if self._prefixlen == self._max_prefixlen: - yield self - return - - if new_prefix is not None: - if new_prefix < self._prefixlen: - raise ValueError('new prefix must be longer') - if prefixlen_diff != 1: - raise ValueError('cannot set prefixlen_diff and new_prefix') - prefixlen_diff = new_prefix - self._prefixlen - - if prefixlen_diff < 0: - raise ValueError('prefix length diff must be > 0') - new_prefixlen = self._prefixlen + prefixlen_diff - - if new_prefixlen > self._max_prefixlen: - raise ValueError( - 'prefix length diff %d is invalid for netblock %s' % ( - new_prefixlen, self)) - - start = int(self.network_address) - end = int(self.broadcast_address) + 1 - step = (int(self.hostmask) + 1) >> prefixlen_diff - for new_addr in _compat_range(start, end, step): - current = self.__class__((new_addr, new_prefixlen)) - yield current - - def supernet(self, prefixlen_diff=1, new_prefix=None): - """The supernet containing the current network. - - Args: - prefixlen_diff: An integer, the amount the prefix length of - the network should be decreased by. For example, given a - /24 network and a prefixlen_diff of 3, a supernet with a - /21 netmask is returned. - - Returns: - An IPv4 network object. - - Raises: - ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have - a negative prefix length. - OR - If prefixlen_diff and new_prefix are both set or new_prefix is a - larger number than the current prefix (larger number means a - smaller network) - - """ - if self._prefixlen == 0: - return self - - if new_prefix is not None: - if new_prefix > self._prefixlen: - raise ValueError('new prefix must be shorter') - if prefixlen_diff != 1: - raise ValueError('cannot set prefixlen_diff and new_prefix') - prefixlen_diff = self._prefixlen - new_prefix - - new_prefixlen = self.prefixlen - prefixlen_diff - if new_prefixlen < 0: - raise ValueError( - 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % - (self.prefixlen, prefixlen_diff)) - return self.__class__(( - int(self.network_address) & (int(self.netmask) << prefixlen_diff), - new_prefixlen)) - - @property - def is_multicast(self): - """Test if the address is reserved for multicast use. - - Returns: - A boolean, True if the address is a multicast address. - See RFC 2373 2.7 for details. - - """ - return (self.network_address.is_multicast and - self.broadcast_address.is_multicast) - - @staticmethod - def _is_subnet_of(a, b): - try: - # Always false if one is v4 and the other is v6. - if a._version != b._version: - raise TypeError( - "%s and %s are not of the same version" % (a, b)) - return (b.network_address <= a.network_address and - b.broadcast_address >= a.broadcast_address) - except AttributeError: - raise TypeError("Unable to test subnet containment " - "between %s and %s" % (a, b)) - - def subnet_of(self, other): - """Return True if this network is a subnet of other.""" - return self._is_subnet_of(self, other) - - def supernet_of(self, other): - """Return True if this network is a supernet of other.""" - return self._is_subnet_of(other, self) - - @property - def is_reserved(self): - """Test if the address is otherwise IETF reserved. - - Returns: - A boolean, True if the address is within one of the - reserved IPv6 Network ranges. - - """ - return (self.network_address.is_reserved and - self.broadcast_address.is_reserved) - - @property - def is_link_local(self): - """Test if the address is reserved for link-local. - - Returns: - A boolean, True if the address is reserved per RFC 4291. - - """ - return (self.network_address.is_link_local and - self.broadcast_address.is_link_local) - - @property - def is_private(self): - """Test if this address is allocated for private networks. - - Returns: - A boolean, True if the address is reserved per - iana-ipv4-special-registry or iana-ipv6-special-registry. - - """ - return (self.network_address.is_private and - self.broadcast_address.is_private) - - @property - def is_global(self): - """Test if this address is allocated for public networks. - - Returns: - A boolean, True if the address is not reserved per - iana-ipv4-special-registry or iana-ipv6-special-registry. - - """ - return not self.is_private - - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 2373 2.5.2. - - """ - return (self.network_address.is_unspecified and - self.broadcast_address.is_unspecified) - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback address as defined in - RFC 2373 2.5.3. - - """ - return (self.network_address.is_loopback and - self.broadcast_address.is_loopback) - - -class _BaseV4(object): - - """Base IPv4 object. - - The following methods are used by IPv4 objects in both single IP - addresses and networks. - - """ - - __slots__ = () - _version = 4 - # Equivalent to 255.255.255.255 or 32 bits of 1's. - _ALL_ONES = (2 ** IPV4LENGTH) - 1 - _DECIMAL_DIGITS = frozenset('0123456789') - - # the valid octets for host and netmasks. only useful for IPv4. - _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0]) - - _max_prefixlen = IPV4LENGTH - # There are only a handful of valid v4 netmasks, so we cache them all - # when constructed (see _make_netmask()). - _netmask_cache = {} - - def _explode_shorthand_ip_string(self): - return _compat_str(self) - - @classmethod - def _make_netmask(cls, arg): - """Make a (netmask, prefix_len) tuple from the given argument. - - Argument can be: - - an integer (the prefix length) - - a string representing the prefix length (e.g. "24") - - a string representing the prefix netmask (e.g. "255.255.255.0") - """ - if arg not in cls._netmask_cache: - if isinstance(arg, _compat_int_types): - prefixlen = arg - else: - try: - # Check for a netmask in prefix length form - prefixlen = cls._prefix_from_prefix_string(arg) - except NetmaskValueError: - # Check for a netmask or hostmask in dotted-quad form. - # This may raise NetmaskValueError. - prefixlen = cls._prefix_from_ip_string(arg) - netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen)) - cls._netmask_cache[arg] = netmask, prefixlen - return cls._netmask_cache[arg] - - @classmethod - def _ip_int_from_string(cls, ip_str): - """Turn the given IP string into an integer for comparison. - - Args: - ip_str: A string, the IP ip_str. - - Returns: - The IP ip_str as an integer. - - Raises: - AddressValueError: if ip_str isn't a valid IPv4 Address. - - """ - if not ip_str: - raise AddressValueError('Address cannot be empty') - - octets = ip_str.split('.') - if len(octets) != 4: - raise AddressValueError("Expected 4 octets in %r" % ip_str) - - try: - return _compat_int_from_byte_vals( - map(cls._parse_octet, octets), 'big') - except ValueError as exc: - raise AddressValueError("%s in %r" % (exc, ip_str)) - - @classmethod - def _parse_octet(cls, octet_str): - """Convert a decimal octet into an integer. - - Args: - octet_str: A string, the number to parse. - - Returns: - The octet as an integer. - - Raises: - ValueError: if the octet isn't strictly a decimal from [0..255]. - - """ - if not octet_str: - raise ValueError("Empty octet not permitted") - # Whitelist the characters, since int() allows a lot of bizarre stuff. - if not cls._DECIMAL_DIGITS.issuperset(octet_str): - msg = "Only decimal digits permitted in %r" - raise ValueError(msg % octet_str) - # We do the length check second, since the invalid character error - # is likely to be more informative for the user - if len(octet_str) > 3: - msg = "At most 3 characters permitted in %r" - raise ValueError(msg % octet_str) - # Convert to integer (we know digits are legal) - octet_int = int(octet_str, 10) - # Any octets that look like they *might* be written in octal, - # and which don't look exactly the same in both octal and - # decimal are rejected as ambiguous - if octet_int > 7 and octet_str[0] == '0': - msg = "Ambiguous (octal/decimal) value in %r not permitted" - raise ValueError(msg % octet_str) - if octet_int > 255: - raise ValueError("Octet %d (> 255) not permitted" % octet_int) - return octet_int - - @classmethod - def _string_from_ip_int(cls, ip_int): - """Turns a 32-bit integer into dotted decimal notation. - - Args: - ip_int: An integer, the IP address. - - Returns: - The IP address as a string in dotted decimal notation. - - """ - return '.'.join(_compat_str(struct.unpack(b'!B', b)[0] - if isinstance(b, bytes) - else b) - for b in _compat_to_bytes(ip_int, 4, 'big')) - - def _is_hostmask(self, ip_str): - """Test if the IP string is a hostmask (rather than a netmask). - - Args: - ip_str: A string, the potential hostmask. - - Returns: - A boolean, True if the IP string is a hostmask. - - """ - bits = ip_str.split('.') - try: - parts = [x for x in map(int, bits) if x in self._valid_mask_octets] - except ValueError: - return False - if len(parts) != len(bits): - return False - if parts[0] < parts[-1]: - return True - return False - - def _reverse_pointer(self): - """Return the reverse DNS pointer name for the IPv4 address. - - This implements the method described in RFC1035 3.5. - - """ - reverse_octets = _compat_str(self).split('.')[::-1] - return '.'.join(reverse_octets) + '.in-addr.arpa' - - @property - def max_prefixlen(self): - return self._max_prefixlen - - @property - def version(self): - return self._version - - -class IPv4Address(_BaseV4, _BaseAddress): - - """Represent and manipulate single IPv4 Addresses.""" - - __slots__ = ('_ip', '__weakref__') - - def __init__(self, address): - - """ - Args: - address: A string or integer representing the IP - - Additionally, an integer can be passed, so - IPv4Address('192.0.2.1') == IPv4Address(3221225985). - or, more generally - IPv4Address(int(IPv4Address('192.0.2.1'))) == - IPv4Address('192.0.2.1') - - Raises: - AddressValueError: If ipaddress isn't a valid IPv4 address. - - """ - # Efficient constructor from integer. - if isinstance(address, _compat_int_types): - self._check_int_address(address) - self._ip = address - return - - # Constructing from a packed address - if isinstance(address, bytes): - self._check_packed_address(address, 4) - bvs = _compat_bytes_to_byte_vals(address) - self._ip = _compat_int_from_byte_vals(bvs, 'big') - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP string. - addr_str = _compat_str(address) - if '/' in addr_str: - raise AddressValueError("Unexpected '/' in %r" % address) - self._ip = self._ip_int_from_string(addr_str) - - @property - def packed(self): - """The binary representation of this address.""" - return v4_int_to_packed(self._ip) - - @property - def is_reserved(self): - """Test if the address is otherwise IETF reserved. - - Returns: - A boolean, True if the address is within the - reserved IPv4 Network range. - - """ - return self in self._constants._reserved_network - - @property - def is_private(self): - """Test if this address is allocated for private networks. - - Returns: - A boolean, True if the address is reserved per - iana-ipv4-special-registry. - - """ - return any(self in net for net in self._constants._private_networks) - - @property - def is_global(self): - return ( - self not in self._constants._public_network and - not self.is_private) - - @property - def is_multicast(self): - """Test if the address is reserved for multicast use. - - Returns: - A boolean, True if the address is multicast. - See RFC 3171 for details. - - """ - return self in self._constants._multicast_network - - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 5735 3. - - """ - return self == self._constants._unspecified_address - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback per RFC 3330. - - """ - return self in self._constants._loopback_network - - @property - def is_link_local(self): - """Test if the address is reserved for link-local. - - Returns: - A boolean, True if the address is link-local per RFC 3927. - - """ - return self in self._constants._linklocal_network - - -class IPv4Interface(IPv4Address): - - def __init__(self, address): - if isinstance(address, (bytes, _compat_int_types)): - IPv4Address.__init__(self, address) - self.network = IPv4Network(self._ip) - self._prefixlen = self._max_prefixlen - return - - if isinstance(address, tuple): - IPv4Address.__init__(self, address[0]) - if len(address) > 1: - self._prefixlen = int(address[1]) - else: - self._prefixlen = self._max_prefixlen - - self.network = IPv4Network(address, strict=False) - self.netmask = self.network.netmask - self.hostmask = self.network.hostmask - return - - addr = _split_optional_netmask(address) - IPv4Address.__init__(self, addr[0]) - - self.network = IPv4Network(address, strict=False) - self._prefixlen = self.network._prefixlen - - self.netmask = self.network.netmask - self.hostmask = self.network.hostmask - - def __str__(self): - return '%s/%d' % (self._string_from_ip_int(self._ip), - self.network.prefixlen) - - def __eq__(self, other): - address_equal = IPv4Address.__eq__(self, other) - if not address_equal or address_equal is NotImplemented: - return address_equal - try: - return self.network == other.network - except AttributeError: - # An interface with an associated network is NOT the - # same as an unassociated address. That's why the hash - # takes the extra info into account. - return False - - def __lt__(self, other): - address_less = IPv4Address.__lt__(self, other) - if address_less is NotImplemented: - return NotImplemented - try: - return (self.network < other.network or - self.network == other.network and address_less) - except AttributeError: - # We *do* allow addresses and interfaces to be sorted. The - # unassociated address is considered less than all interfaces. - return False - - def __hash__(self): - return self._ip ^ self._prefixlen ^ int(self.network.network_address) - - __reduce__ = _IPAddressBase.__reduce__ - - @property - def ip(self): - return IPv4Address(self._ip) - - @property - def with_prefixlen(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.netmask) - - @property - def with_hostmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.hostmask) - - -class IPv4Network(_BaseV4, _BaseNetwork): - - """This class represents and manipulates 32-bit IPv4 network + addresses.. - - Attributes: [examples for IPv4Network('192.0.2.0/27')] - .network_address: IPv4Address('192.0.2.0') - .hostmask: IPv4Address('0.0.0.31') - .broadcast_address: IPv4Address('192.0.2.32') - .netmask: IPv4Address('255.255.255.224') - .prefixlen: 27 - - """ - # Class to use when creating address objects - _address_class = IPv4Address - - def __init__(self, address, strict=True): - - """Instantiate a new IPv4 network object. - - Args: - address: A string or integer representing the IP [& network]. - '192.0.2.0/24' - '192.0.2.0/255.255.255.0' - '192.0.0.2/0.0.0.255' - are all functionally the same in IPv4. Similarly, - '192.0.2.1' - '192.0.2.1/255.255.255.255' - '192.0.2.1/32' - are also functionally equivalent. That is to say, failing to - provide a subnetmask will create an object with a mask of /32. - - If the mask (portion after the / in the argument) is given in - dotted quad form, it is treated as a netmask if it starts with a - non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it - starts with a zero field (e.g. 0.255.255.255 == /8), with the - single exception of an all-zero mask which is treated as a - netmask == /0. If no mask is given, a default of /32 is used. - - Additionally, an integer can be passed, so - IPv4Network('192.0.2.1') == IPv4Network(3221225985) - or, more generally - IPv4Interface(int(IPv4Interface('192.0.2.1'))) == - IPv4Interface('192.0.2.1') - - Raises: - AddressValueError: If ipaddress isn't a valid IPv4 address. - NetmaskValueError: If the netmask isn't valid for - an IPv4 address. - ValueError: If strict is True and a network address is not - supplied. - - """ - _BaseNetwork.__init__(self, address) - - # Constructing from a packed address or integer - if isinstance(address, (_compat_int_types, bytes)): - self.network_address = IPv4Address(address) - self.netmask, self._prefixlen = self._make_netmask( - self._max_prefixlen) - # fixme: address/network test here. - return - - if isinstance(address, tuple): - if len(address) > 1: - arg = address[1] - else: - # We weren't given an address[1] - arg = self._max_prefixlen - self.network_address = IPv4Address(address[0]) - self.netmask, self._prefixlen = self._make_netmask(arg) - packed = int(self.network_address) - if packed & int(self.netmask) != packed: - if strict: - raise ValueError('%s has host bits set' % self) - else: - self.network_address = IPv4Address(packed & - int(self.netmask)) - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP prefix string. - addr = _split_optional_netmask(address) - self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) - - if len(addr) == 2: - arg = addr[1] - else: - arg = self._max_prefixlen - self.netmask, self._prefixlen = self._make_netmask(arg) - - if strict: - if (IPv4Address(int(self.network_address) & int(self.netmask)) != - self.network_address): - raise ValueError('%s has host bits set' % self) - self.network_address = IPv4Address(int(self.network_address) & - int(self.netmask)) - - if self._prefixlen == (self._max_prefixlen - 1): - self.hosts = self.__iter__ - - @property - def is_global(self): - """Test if this address is allocated for public networks. - - Returns: - A boolean, True if the address is not reserved per - iana-ipv4-special-registry. - - """ - return (not (self.network_address in IPv4Network('100.64.0.0/10') and - self.broadcast_address in IPv4Network('100.64.0.0/10')) and - not self.is_private) - - -class _IPv4Constants(object): - - _linklocal_network = IPv4Network('169.254.0.0/16') - - _loopback_network = IPv4Network('127.0.0.0/8') - - _multicast_network = IPv4Network('224.0.0.0/4') - - _public_network = IPv4Network('100.64.0.0/10') - - _private_networks = [ - IPv4Network('0.0.0.0/8'), - IPv4Network('10.0.0.0/8'), - IPv4Network('127.0.0.0/8'), - IPv4Network('169.254.0.0/16'), - IPv4Network('172.16.0.0/12'), - IPv4Network('192.0.0.0/29'), - IPv4Network('192.0.0.170/31'), - IPv4Network('192.0.2.0/24'), - IPv4Network('192.168.0.0/16'), - IPv4Network('198.18.0.0/15'), - IPv4Network('198.51.100.0/24'), - IPv4Network('203.0.113.0/24'), - IPv4Network('240.0.0.0/4'), - IPv4Network('255.255.255.255/32'), - ] - - _reserved_network = IPv4Network('240.0.0.0/4') - - _unspecified_address = IPv4Address('0.0.0.0') - - -IPv4Address._constants = _IPv4Constants - - -class _BaseV6(object): - - """Base IPv6 object. - - The following methods are used by IPv6 objects in both single IP - addresses and networks. - - """ - - __slots__ = () - _version = 6 - _ALL_ONES = (2 ** IPV6LENGTH) - 1 - _HEXTET_COUNT = 8 - _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') - _max_prefixlen = IPV6LENGTH - - # There are only a bunch of valid v6 netmasks, so we cache them all - # when constructed (see _make_netmask()). - _netmask_cache = {} - - @classmethod - def _make_netmask(cls, arg): - """Make a (netmask, prefix_len) tuple from the given argument. - - Argument can be: - - an integer (the prefix length) - - a string representing the prefix length (e.g. "24") - - a string representing the prefix netmask (e.g. "255.255.255.0") - """ - if arg not in cls._netmask_cache: - if isinstance(arg, _compat_int_types): - prefixlen = arg - else: - prefixlen = cls._prefix_from_prefix_string(arg) - netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen)) - cls._netmask_cache[arg] = netmask, prefixlen - return cls._netmask_cache[arg] - - @classmethod - def _ip_int_from_string(cls, ip_str): - """Turn an IPv6 ip_str into an integer. - - Args: - ip_str: A string, the IPv6 ip_str. - - Returns: - An int, the IPv6 address - - Raises: - AddressValueError: if ip_str isn't a valid IPv6 Address. - - """ - if not ip_str: - raise AddressValueError('Address cannot be empty') - - parts = ip_str.split(':') - - # An IPv6 address needs at least 2 colons (3 parts). - _min_parts = 3 - if len(parts) < _min_parts: - msg = "At least %d parts expected in %r" % (_min_parts, ip_str) - raise AddressValueError(msg) - - # If the address has an IPv4-style suffix, convert it to hexadecimal. - if '.' in parts[-1]: - try: - ipv4_int = IPv4Address(parts.pop())._ip - except AddressValueError as exc: - raise AddressValueError("%s in %r" % (exc, ip_str)) - parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) - parts.append('%x' % (ipv4_int & 0xFFFF)) - - # An IPv6 address can't have more than 8 colons (9 parts). - # The extra colon comes from using the "::" notation for a single - # leading or trailing zero part. - _max_parts = cls._HEXTET_COUNT + 1 - if len(parts) > _max_parts: - msg = "At most %d colons permitted in %r" % ( - _max_parts - 1, ip_str) - raise AddressValueError(msg) - - # Disregarding the endpoints, find '::' with nothing in between. - # This indicates that a run of zeroes has been skipped. - skip_index = None - for i in _compat_range(1, len(parts) - 1): - if not parts[i]: - if skip_index is not None: - # Can't have more than one '::' - msg = "At most one '::' permitted in %r" % ip_str - raise AddressValueError(msg) - skip_index = i - - # parts_hi is the number of parts to copy from above/before the '::' - # parts_lo is the number of parts to copy from below/after the '::' - if skip_index is not None: - # If we found a '::', then check if it also covers the endpoints. - parts_hi = skip_index - parts_lo = len(parts) - skip_index - 1 - if not parts[0]: - parts_hi -= 1 - if parts_hi: - msg = "Leading ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # ^: requires ^:: - if not parts[-1]: - parts_lo -= 1 - if parts_lo: - msg = "Trailing ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # :$ requires ::$ - parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo) - if parts_skipped < 1: - msg = "Expected at most %d other parts with '::' in %r" - raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str)) - else: - # Otherwise, allocate the entire address to parts_hi. The - # endpoints could still be empty, but _parse_hextet() will check - # for that. - if len(parts) != cls._HEXTET_COUNT: - msg = "Exactly %d parts expected without '::' in %r" - raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str)) - if not parts[0]: - msg = "Leading ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # ^: requires ^:: - if not parts[-1]: - msg = "Trailing ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # :$ requires ::$ - parts_hi = len(parts) - parts_lo = 0 - parts_skipped = 0 - - try: - # Now, parse the hextets into a 128-bit integer. - ip_int = 0 - for i in range(parts_hi): - ip_int <<= 16 - ip_int |= cls._parse_hextet(parts[i]) - ip_int <<= 16 * parts_skipped - for i in range(-parts_lo, 0): - ip_int <<= 16 - ip_int |= cls._parse_hextet(parts[i]) - return ip_int - except ValueError as exc: - raise AddressValueError("%s in %r" % (exc, ip_str)) - - @classmethod - def _parse_hextet(cls, hextet_str): - """Convert an IPv6 hextet string into an integer. - - Args: - hextet_str: A string, the number to parse. - - Returns: - The hextet as an integer. - - Raises: - ValueError: if the input isn't strictly a hex number from - [0..FFFF]. - - """ - # Whitelist the characters, since int() allows a lot of bizarre stuff. - if not cls._HEX_DIGITS.issuperset(hextet_str): - raise ValueError("Only hex digits permitted in %r" % hextet_str) - # We do the length check second, since the invalid character error - # is likely to be more informative for the user - if len(hextet_str) > 4: - msg = "At most 4 characters permitted in %r" - raise ValueError(msg % hextet_str) - # Length check means we can skip checking the integer value - return int(hextet_str, 16) - - @classmethod - def _compress_hextets(cls, hextets): - """Compresses a list of hextets. - - Compresses a list of strings, replacing the longest continuous - sequence of "0" in the list with "" and adding empty strings at - the beginning or at the end of the string such that subsequently - calling ":".join(hextets) will produce the compressed version of - the IPv6 address. - - Args: - hextets: A list of strings, the hextets to compress. - - Returns: - A list of strings. - - """ - best_doublecolon_start = -1 - best_doublecolon_len = 0 - doublecolon_start = -1 - doublecolon_len = 0 - for index, hextet in enumerate(hextets): - if hextet == '0': - doublecolon_len += 1 - if doublecolon_start == -1: - # Start of a sequence of zeros. - doublecolon_start = index - if doublecolon_len > best_doublecolon_len: - # This is the longest sequence of zeros so far. - best_doublecolon_len = doublecolon_len - best_doublecolon_start = doublecolon_start - else: - doublecolon_len = 0 - doublecolon_start = -1 - - if best_doublecolon_len > 1: - best_doublecolon_end = (best_doublecolon_start + - best_doublecolon_len) - # For zeros at the end of the address. - if best_doublecolon_end == len(hextets): - hextets += [''] - hextets[best_doublecolon_start:best_doublecolon_end] = [''] - # For zeros at the beginning of the address. - if best_doublecolon_start == 0: - hextets = [''] + hextets - - return hextets - - @classmethod - def _string_from_ip_int(cls, ip_int=None): - """Turns a 128-bit integer into hexadecimal notation. - - Args: - ip_int: An integer, the IP address. - - Returns: - A string, the hexadecimal representation of the address. - - Raises: - ValueError: The address is bigger than 128 bits of all ones. - - """ - if ip_int is None: - ip_int = int(cls._ip) - - if ip_int > cls._ALL_ONES: - raise ValueError('IPv6 address is too large') - - hex_str = '%032x' % ip_int - hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] - - hextets = cls._compress_hextets(hextets) - return ':'.join(hextets) - - def _explode_shorthand_ip_string(self): - """Expand a shortened IPv6 address. - - Args: - ip_str: A string, the IPv6 address. - - Returns: - A string, the expanded IPv6 address. - - """ - if isinstance(self, IPv6Network): - ip_str = _compat_str(self.network_address) - elif isinstance(self, IPv6Interface): - ip_str = _compat_str(self.ip) - else: - ip_str = _compat_str(self) - - ip_int = self._ip_int_from_string(ip_str) - hex_str = '%032x' % ip_int - parts = [hex_str[x:x + 4] for x in range(0, 32, 4)] - if isinstance(self, (_BaseNetwork, IPv6Interface)): - return '%s/%d' % (':'.join(parts), self._prefixlen) - return ':'.join(parts) - - def _reverse_pointer(self): - """Return the reverse DNS pointer name for the IPv6 address. - - This implements the method described in RFC3596 2.5. - - """ - reverse_chars = self.exploded[::-1].replace(':', '') - return '.'.join(reverse_chars) + '.ip6.arpa' - - @property - def max_prefixlen(self): - return self._max_prefixlen - - @property - def version(self): - return self._version - - -class IPv6Address(_BaseV6, _BaseAddress): - - """Represent and manipulate single IPv6 Addresses.""" - - __slots__ = ('_ip', '__weakref__') - - def __init__(self, address): - """Instantiate a new IPv6 address object. - - Args: - address: A string or integer representing the IP - - Additionally, an integer can be passed, so - IPv6Address('2001:db8::') == - IPv6Address(42540766411282592856903984951653826560) - or, more generally - IPv6Address(int(IPv6Address('2001:db8::'))) == - IPv6Address('2001:db8::') - - Raises: - AddressValueError: If address isn't a valid IPv6 address. - - """ - # Efficient constructor from integer. - if isinstance(address, _compat_int_types): - self._check_int_address(address) - self._ip = address - return - - # Constructing from a packed address - if isinstance(address, bytes): - self._check_packed_address(address, 16) - bvs = _compat_bytes_to_byte_vals(address) - self._ip = _compat_int_from_byte_vals(bvs, 'big') - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP string. - addr_str = _compat_str(address) - if '/' in addr_str: - raise AddressValueError("Unexpected '/' in %r" % address) - self._ip = self._ip_int_from_string(addr_str) - - @property - def packed(self): - """The binary representation of this address.""" - return v6_int_to_packed(self._ip) - - @property - def is_multicast(self): - """Test if the address is reserved for multicast use. - - Returns: - A boolean, True if the address is a multicast address. - See RFC 2373 2.7 for details. - - """ - return self in self._constants._multicast_network - - @property - def is_reserved(self): - """Test if the address is otherwise IETF reserved. - - Returns: - A boolean, True if the address is within one of the - reserved IPv6 Network ranges. - - """ - return any(self in x for x in self._constants._reserved_networks) - - @property - def is_link_local(self): - """Test if the address is reserved for link-local. - - Returns: - A boolean, True if the address is reserved per RFC 4291. - - """ - return self in self._constants._linklocal_network - - @property - def is_site_local(self): - """Test if the address is reserved for site-local. - - Note that the site-local address space has been deprecated by RFC 3879. - Use is_private to test if this address is in the space of unique local - addresses as defined by RFC 4193. - - Returns: - A boolean, True if the address is reserved per RFC 3513 2.5.6. - - """ - return self in self._constants._sitelocal_network - - @property - def is_private(self): - """Test if this address is allocated for private networks. - - Returns: - A boolean, True if the address is reserved per - iana-ipv6-special-registry. - - """ - return any(self in net for net in self._constants._private_networks) - - @property - def is_global(self): - """Test if this address is allocated for public networks. - - Returns: - A boolean, true if the address is not reserved per - iana-ipv6-special-registry. - - """ - return not self.is_private - - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 2373 2.5.2. - - """ - return self._ip == 0 - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback address as defined in - RFC 2373 2.5.3. - - """ - return self._ip == 1 - - @property - def ipv4_mapped(self): - """Return the IPv4 mapped address. - - Returns: - If the IPv6 address is a v4 mapped address, return the - IPv4 mapped address. Return None otherwise. - - """ - if (self._ip >> 32) != 0xFFFF: - return None - return IPv4Address(self._ip & 0xFFFFFFFF) - - @property - def teredo(self): - """Tuple of embedded teredo IPs. - - Returns: - Tuple of the (server, client) IPs or None if the address - doesn't appear to be a teredo address (doesn't start with - 2001::/32) - - """ - if (self._ip >> 96) != 0x20010000: - return None - return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), - IPv4Address(~self._ip & 0xFFFFFFFF)) - - @property - def sixtofour(self): - """Return the IPv4 6to4 embedded address. - - Returns: - The IPv4 6to4-embedded address if present or None if the - address doesn't appear to contain a 6to4 embedded address. - - """ - if (self._ip >> 112) != 0x2002: - return None - return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) - - -class IPv6Interface(IPv6Address): - - def __init__(self, address): - if isinstance(address, (bytes, _compat_int_types)): - IPv6Address.__init__(self, address) - self.network = IPv6Network(self._ip) - self._prefixlen = self._max_prefixlen - return - if isinstance(address, tuple): - IPv6Address.__init__(self, address[0]) - if len(address) > 1: - self._prefixlen = int(address[1]) - else: - self._prefixlen = self._max_prefixlen - self.network = IPv6Network(address, strict=False) - self.netmask = self.network.netmask - self.hostmask = self.network.hostmask - return - - addr = _split_optional_netmask(address) - IPv6Address.__init__(self, addr[0]) - self.network = IPv6Network(address, strict=False) - self.netmask = self.network.netmask - self._prefixlen = self.network._prefixlen - self.hostmask = self.network.hostmask - - def __str__(self): - return '%s/%d' % (self._string_from_ip_int(self._ip), - self.network.prefixlen) - - def __eq__(self, other): - address_equal = IPv6Address.__eq__(self, other) - if not address_equal or address_equal is NotImplemented: - return address_equal - try: - return self.network == other.network - except AttributeError: - # An interface with an associated network is NOT the - # same as an unassociated address. That's why the hash - # takes the extra info into account. - return False - - def __lt__(self, other): - address_less = IPv6Address.__lt__(self, other) - if address_less is NotImplemented: - return NotImplemented - try: - return (self.network < other.network or - self.network == other.network and address_less) - except AttributeError: - # We *do* allow addresses and interfaces to be sorted. The - # unassociated address is considered less than all interfaces. - return False - - def __hash__(self): - return self._ip ^ self._prefixlen ^ int(self.network.network_address) - - __reduce__ = _IPAddressBase.__reduce__ - - @property - def ip(self): - return IPv6Address(self._ip) - - @property - def with_prefixlen(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.netmask) - - @property - def with_hostmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.hostmask) - - @property - def is_unspecified(self): - return self._ip == 0 and self.network.is_unspecified - - @property - def is_loopback(self): - return self._ip == 1 and self.network.is_loopback - - -class IPv6Network(_BaseV6, _BaseNetwork): - - """This class represents and manipulates 128-bit IPv6 networks. - - Attributes: [examples for IPv6('2001:db8::1000/124')] - .network_address: IPv6Address('2001:db8::1000') - .hostmask: IPv6Address('::f') - .broadcast_address: IPv6Address('2001:db8::100f') - .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0') - .prefixlen: 124 - - """ - - # Class to use when creating address objects - _address_class = IPv6Address - - def __init__(self, address, strict=True): - """Instantiate a new IPv6 Network object. - - Args: - address: A string or integer representing the IPv6 network or the - IP and prefix/netmask. - '2001:db8::/128' - '2001:db8:0000:0000:0000:0000:0000:0000/128' - '2001:db8::' - are all functionally the same in IPv6. That is to say, - failing to provide a subnetmask will create an object with - a mask of /128. - - Additionally, an integer can be passed, so - IPv6Network('2001:db8::') == - IPv6Network(42540766411282592856903984951653826560) - or, more generally - IPv6Network(int(IPv6Network('2001:db8::'))) == - IPv6Network('2001:db8::') - - strict: A boolean. If true, ensure that we have been passed - A true network address, eg, 2001:db8::1000/124 and not an - IP address on a network, eg, 2001:db8::1/124. - - Raises: - AddressValueError: If address isn't a valid IPv6 address. - NetmaskValueError: If the netmask isn't valid for - an IPv6 address. - ValueError: If strict was True and a network address was not - supplied. - - """ - _BaseNetwork.__init__(self, address) - - # Efficient constructor from integer or packed address - if isinstance(address, (bytes, _compat_int_types)): - self.network_address = IPv6Address(address) - self.netmask, self._prefixlen = self._make_netmask( - self._max_prefixlen) - return - - if isinstance(address, tuple): - if len(address) > 1: - arg = address[1] - else: - arg = self._max_prefixlen - self.netmask, self._prefixlen = self._make_netmask(arg) - self.network_address = IPv6Address(address[0]) - packed = int(self.network_address) - if packed & int(self.netmask) != packed: - if strict: - raise ValueError('%s has host bits set' % self) - else: - self.network_address = IPv6Address(packed & - int(self.netmask)) - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP prefix string. - addr = _split_optional_netmask(address) - - self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) - - if len(addr) == 2: - arg = addr[1] - else: - arg = self._max_prefixlen - self.netmask, self._prefixlen = self._make_netmask(arg) - - if strict: - if (IPv6Address(int(self.network_address) & int(self.netmask)) != - self.network_address): - raise ValueError('%s has host bits set' % self) - self.network_address = IPv6Address(int(self.network_address) & - int(self.netmask)) - - if self._prefixlen == (self._max_prefixlen - 1): - self.hosts = self.__iter__ - - def hosts(self): - """Generate Iterator over usable hosts in a network. - - This is like __iter__ except it doesn't return the - Subnet-Router anycast address. - - """ - network = int(self.network_address) - broadcast = int(self.broadcast_address) - for x in _compat_range(network + 1, broadcast + 1): - yield self._address_class(x) - - @property - def is_site_local(self): - """Test if the address is reserved for site-local. - - Note that the site-local address space has been deprecated by RFC 3879. - Use is_private to test if this address is in the space of unique local - addresses as defined by RFC 4193. - - Returns: - A boolean, True if the address is reserved per RFC 3513 2.5.6. - - """ - return (self.network_address.is_site_local and - self.broadcast_address.is_site_local) - - -class _IPv6Constants(object): - - _linklocal_network = IPv6Network('fe80::/10') - - _multicast_network = IPv6Network('ff00::/8') - - _private_networks = [ - IPv6Network('::1/128'), - IPv6Network('::/128'), - IPv6Network('::ffff:0:0/96'), - IPv6Network('100::/64'), - IPv6Network('2001::/23'), - IPv6Network('2001:2::/48'), - IPv6Network('2001:db8::/32'), - IPv6Network('2001:10::/28'), - IPv6Network('fc00::/7'), - IPv6Network('fe80::/10'), - ] - - _reserved_networks = [ - IPv6Network('::/8'), IPv6Network('100::/8'), - IPv6Network('200::/7'), IPv6Network('400::/6'), - IPv6Network('800::/5'), IPv6Network('1000::/4'), - IPv6Network('4000::/3'), IPv6Network('6000::/3'), - IPv6Network('8000::/3'), IPv6Network('A000::/3'), - IPv6Network('C000::/3'), IPv6Network('E000::/4'), - IPv6Network('F000::/5'), IPv6Network('F800::/6'), - IPv6Network('FE00::/9'), - ] - - _sitelocal_network = IPv6Network('fec0::/10') - - -IPv6Address._constants = _IPv6Constants diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py b/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py index 9f55cf50dc6b6849498710e50a5656694e5705c3..1c83c8ed3d618cd68e774a30be6737da1dda8b15 100644 --- a/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/_version.py @@ -1 +1 @@ -version = (1, 0, 0) +version = (1, 0, 2) diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/ext.py b/venv/Lib/site-packages/pip/_vendor/msgpack/ext.py index 8341c68b8abbda39e1f49934b2aff7b99857fd53..4eb9dd65adc9aff07547f5ef7541bdf2be91124a 100644 --- a/venv/Lib/site-packages/pip/_vendor/msgpack/ext.py +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/ext.py @@ -178,7 +178,9 @@ class Timestamp(object): :rtype: datetime. """ - return datetime.datetime.fromtimestamp(self.to_unix(), _utc) + return datetime.datetime.fromtimestamp(0, _utc) + datetime.timedelta( + seconds=self.to_unix() + ) @staticmethod def from_datetime(dt): diff --git a/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py b/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py index 9f6665b3eb3baf73b29283e0c0e65a4c1d2f79d6..0bfa94eacb6ae5cfacf395f13511413dd6f18ad4 100644 --- a/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py +++ b/venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py @@ -365,18 +365,19 @@ class Unpacker(object): return self._buffer[self._buff_i :] def read_bytes(self, n): - ret = self._read(n) + ret = self._read(n, raise_outofdata=False) self._consume() return ret - def _read(self, n): + def _read(self, n, raise_outofdata=True): # (int) -> bytearray - self._reserve(n) + self._reserve(n, raise_outofdata=raise_outofdata) i = self._buff_i - self._buff_i = i + n - return self._buffer[i : i + n] + ret = self._buffer[i : i + n] + self._buff_i = i + len(ret) + return ret - def _reserve(self, n): + def _reserve(self, n, raise_outofdata=True): remain_bytes = len(self._buffer) - self._buff_i - n # Fast path: buffer has n bytes already @@ -404,7 +405,7 @@ class Unpacker(object): self._buffer += read_data remain_bytes -= len(read_data) - if len(self._buffer) < n + self._buff_i: + if len(self._buffer) < n + self._buff_i and raise_outofdata: self._buff_i = 0 # rollback raise OutOfData @@ -743,7 +744,7 @@ class Packer(object): """ MessagePack Packer - Usage: + Usage:: packer = Packer() astream.write(packer.pack(a)) @@ -783,6 +784,29 @@ class Packer(object): :param str unicode_errors: The error handler for encoding unicode. (default: 'strict') DO NOT USE THIS!! This option is kept for very specific usage. + + Example of streaming deserialize from file-like object:: + + unpacker = Unpacker(file_like) + for o in unpacker: + process(o) + + Example of streaming deserialize from socket:: + + unpacker = Unpacker() + while True: + buf = sock.recv(1024**2) + if not buf: + break + unpacker.feed(buf) + for o in unpacker: + process(o) + + Raises ``ExtraData`` when *packed* contains extra bytes. + Raises ``OutOfData`` when *packed* is incomplete. + Raises ``FormatError`` when *packed* is not valid msgpack. + Raises ``StackError`` when *packed* contains too nested. + Other exceptions can be raised during unpacking. """ def __init__( @@ -920,7 +944,7 @@ class Packer(object): len(obj), dict_iteritems(obj), nest_limit - 1 ) - if self._datetime and check(obj, _DateTime): + if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None: obj = Timestamp.from_datetime(obj) default_used = 1 continue diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py b/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py index 5161d141be74b128228263c01b4c7d98de4c27e4..e70d692c85a13872f7d87bc12b6ab82ea99e429b 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/__about__.py @@ -1,7 +1,6 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function __all__ = [ "__title__", @@ -18,10 +17,10 @@ __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "20.3" +__version__ = "21.0" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" -__license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py b/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py index a0cf67df5245be16a020ca048832e180f7ce8661..3c50c5dcfeeda2efed282200a5c5cc8c5f7542f7 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/__init__.py @@ -1,7 +1,6 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function from .__about__ import ( __author__, diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/_compat.py b/venv/Lib/site-packages/pip/_vendor/packaging/_compat.py deleted file mode 100644 index a145f7eeb3972375d152026255fb1256d81bb024..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/packaging/_compat.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import sys - -from ._typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import Any, Dict, Tuple, Type - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = (str,) -else: - string_types = (basestring,) - - -def with_metaclass(meta, *bases): - # type: (Type[Any], Tuple[Type[Any], ...]) -> Any - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): # type: ignore - def __new__(cls, name, this_bases, d): - # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py b/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py index 800d5c5588c99dc216cdea5084da440efb641945..951549753afa255148c7c60d868303963f8c1813 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/_structures.py @@ -1,85 +1,66 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function -class InfinityType(object): - def __repr__(self): - # type: () -> str +class InfinityType: + def __repr__(self) -> str: return "Infinity" - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): - # type: (object) -> bool + def __lt__(self, other: object) -> bool: return False - def __le__(self, other): - # type: (object) -> bool + def __le__(self, other: object) -> bool: return False - def __eq__(self, other): - # type: (object) -> bool + def __eq__(self, other: object) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): - # type: (object) -> bool + def __ne__(self, other: object) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): - # type: (object) -> bool + def __gt__(self, other: object) -> bool: return True - def __ge__(self, other): - # type: (object) -> bool + def __ge__(self, other: object) -> bool: return True - def __neg__(self): - # type: (object) -> NegativeInfinityType + def __neg__(self: object) -> "NegativeInfinityType": return NegativeInfinity Infinity = InfinityType() -class NegativeInfinityType(object): - def __repr__(self): - # type: () -> str +class NegativeInfinityType: + def __repr__(self) -> str: return "-Infinity" - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): - # type: (object) -> bool + def __lt__(self, other: object) -> bool: return True - def __le__(self, other): - # type: (object) -> bool + def __le__(self, other: object) -> bool: return True - def __eq__(self, other): - # type: (object) -> bool + def __eq__(self, other: object) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): - # type: (object) -> bool + def __ne__(self, other: object) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): - # type: (object) -> bool + def __gt__(self, other: object) -> bool: return False - def __ge__(self, other): - # type: (object) -> bool + def __ge__(self, other: object) -> bool: return False - def __neg__(self): - # type: (object) -> InfinityType + def __neg__(self: object) -> InfinityType: return Infinity diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/_typing.py b/venv/Lib/site-packages/pip/_vendor/packaging/_typing.py deleted file mode 100644 index 945b39c30a0c944393b3a73e43e4981cb75762b7..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/packaging/_typing.py +++ /dev/null @@ -1,39 +0,0 @@ -"""For neatly implementing static typing in packaging. - -`mypy` - the static type analysis tool we use - uses the `typing` module, which -provides core functionality fundamental to mypy's functioning. - -Generally, `typing` would be imported at runtime and used in that fashion - -it acts as a no-op at runtime and does not have any run-time overhead by -design. - -As it turns out, `typing` is not vendorable - it uses separate sources for -Python 2/Python 3. Thus, this codebase can not expect it to be present. -To work around this, mypy allows the typing import to be behind a False-y -optional to prevent it from running at runtime and type-comments can be used -to remove the need for the types to be accessible directly during runtime. - -This module provides the False-y guard in a nicely named fashion so that a -curious maintainer can reach here to read this. - -In packaging, all static-typing related imports should be guarded as follows: - - from pip._vendor.packaging._typing import MYPY_CHECK_RUNNING - - if MYPY_CHECK_RUNNING: - from typing import ... - -Ref: https://github.com/python/mypy/issues/3216 -""" - -MYPY_CHECK_RUNNING = False - -if MYPY_CHECK_RUNNING: # pragma: no cover - import typing - - cast = typing.cast -else: - # typing's cast() is needed at runtime, but we don't want to import typing. - # Thus, we use a dummy no-op version, which we tell mypy to ignore. - def cast(type_, value): # type: ignore - return value diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/markers.py b/venv/Lib/site-packages/pip/_vendor/packaging/markers.py index b24f8edf9348a82017548eeeb274949e3e78a302..540e7a4dc79d02a820e291b57c43335d5aa25a41 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/markers.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/markers.py @@ -1,26 +1,26 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import operator import os import platform import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from pip._vendor.pyparsing import ( # noqa: N817 + Forward, + Group, + Literal as L, + ParseException, + ParseResults, + QuotedString, + ZeroOrMore, + stringEnd, + stringStart, +) -from pip._vendor.pyparsing import ParseException, ParseResults, stringStart, stringEnd -from pip._vendor.pyparsing import ZeroOrMore, Group, Forward, QuotedString -from pip._vendor.pyparsing import Literal as L # noqa - -from ._compat import string_types -from ._typing import MYPY_CHECK_RUNNING -from .specifiers import Specifier, InvalidSpecifier - -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import Any, Callable, Dict, List, Optional, Tuple, Union - - Operator = Callable[[str, str], bool] - +from .specifiers import InvalidSpecifier, Specifier __all__ = [ "InvalidMarker", @@ -30,6 +30,8 @@ __all__ = [ "default_environment", ] +Operator = Callable[[str, str], bool] + class InvalidMarker(ValueError): """ @@ -50,39 +52,32 @@ class UndefinedEnvironmentName(ValueError): """ -class Node(object): - def __init__(self, value): - # type: (Any) -> None +class Node: + def __init__(self, value: Any) -> None: self.value = value - def __str__(self): - # type: () -> str + def __str__(self) -> str: return str(self.value) - def __repr__(self): - # type: () -> str - return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" - def serialize(self): - # type: () -> str + def serialize(self) -> str: raise NotImplementedError class Variable(Node): - def serialize(self): - # type: () -> str + def serialize(self) -> str: return str(self) class Value(Node): - def serialize(self): - # type: () -> str - return '"{0}"'.format(self) + def serialize(self) -> str: + return f'"{self}"' class Op(Node): - def serialize(self): - # type: () -> str + def serialize(self) -> str: return str(self) @@ -143,18 +138,18 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) MARKER = stringStart + MARKER_EXPR + stringEnd -def _coerce_parse_result(results): - # type: (Union[ParseResults, List[Any]]) -> List[Any] +def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: if isinstance(results, ParseResults): return [_coerce_parse_result(i) for i in results] else: return results -def _format_marker(marker, first=True): - # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str +def _format_marker( + marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True +) -> str: - assert isinstance(marker, (list, tuple, string_types)) + assert isinstance(marker, (list, tuple, str)) # Sometimes we have a structure like [[...]] which is a single item list # where the single item is itself it's own list. In that case we want skip @@ -179,7 +174,7 @@ def _format_marker(marker, first=True): return marker -_operators = { +_operators: Dict[str, Operator] = { "in": lambda lhs, rhs: lhs in rhs, "not in": lambda lhs, rhs: lhs not in rhs, "<": operator.lt, @@ -188,11 +183,10 @@ _operators = { "!=": operator.ne, ">=": operator.ge, ">": operator.gt, -} # type: Dict[str, Operator] +} -def _eval_op(lhs, op, rhs): - # type: (str, Op, str) -> bool +def _eval_op(lhs: str, op: Op, rhs: str) -> bool: try: spec = Specifier("".join([op.serialize(), rhs])) except InvalidSpecifier: @@ -200,40 +194,36 @@ def _eval_op(lhs, op, rhs): else: return spec.contains(lhs) - oper = _operators.get(op.serialize()) # type: Optional[Operator] + oper: Optional[Operator] = _operators.get(op.serialize()) if oper is None: - raise UndefinedComparison( - "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) - ) + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") return oper(lhs, rhs) -class Undefined(object): +class Undefined: pass _undefined = Undefined() -def _get_env(environment, name): - # type: (Dict[str, str], str) -> str - value = environment.get(name, _undefined) # type: Union[str, Undefined] +def _get_env(environment: Dict[str, str], name: str) -> str: + value: Union[str, Undefined] = environment.get(name, _undefined) if isinstance(value, Undefined): raise UndefinedEnvironmentName( - "{0!r} does not exist in evaluation environment.".format(name) + f"{name!r} does not exist in evaluation environment." ) return value -def _evaluate_markers(markers, environment): - # type: (List[Any], Dict[str, str]) -> bool - groups = [[]] # type: List[List[bool]] +def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] for marker in markers: - assert isinstance(marker, (list, tuple, string_types)) + assert isinstance(marker, (list, tuple, str)) if isinstance(marker, list): groups[-1].append(_evaluate_markers(marker, environment)) @@ -256,8 +246,7 @@ def _evaluate_markers(markers, environment): return any(all(item) for item in groups) -def format_full_version(info): - # type: (sys._version_info) -> str +def format_full_version(info: "sys._version_info") -> str: version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel if kind != "final": @@ -265,18 +254,9 @@ def format_full_version(info): return version -def default_environment(): - # type: () -> Dict[str, str] - if hasattr(sys, "implementation"): - # Ignoring the `sys.implementation` reference for type checking due to - # mypy not liking that the attribute doesn't exist in Python 2.7 when - # run with the `--py27` flag. - iver = format_full_version(sys.implementation.version) # type: ignore - implementation_name = sys.implementation.name # type: ignore - else: - iver = "0" - implementation_name = "" - +def default_environment() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name return { "implementation_name": implementation_name, "implementation_version": iver, @@ -292,27 +272,23 @@ def default_environment(): } -class Marker(object): - def __init__(self, marker): - # type: (str) -> None +class Marker: + def __init__(self, marker: str) -> None: try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: - err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc : e.loc + 8] + raise InvalidMarker( + f"Invalid marker: {marker!r}, parse error at " + f"{marker[e.loc : e.loc + 8]!r}" ) - raise InvalidMarker(err_str) - def __str__(self): - # type: () -> str + def __str__(self) -> str: return _format_marker(self._markers) - def __repr__(self): - # type: () -> str - return "".format(str(self)) + def __repr__(self) -> str: + return f"" - def evaluate(self, environment=None): - # type: (Optional[Dict[str, str]]) -> bool + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: """Evaluate a marker. Return the boolean from evaluating the given marker against the diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py b/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py index 1e32a9376ec048085bb3dabd103dc68f4761b610..1eab7dd66d9bfdefea1a0e159303f1c09fa16d67 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/requirements.py @@ -1,23 +1,28 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function -import string import re +import string +import urllib.parse +from typing import List, Optional as TOptional, Set + +from pip._vendor.pyparsing import ( # noqa + Combine, + Literal as L, + Optional, + ParseException, + Regex, + Word, + ZeroOrMore, + originalTextFor, + stringEnd, + stringStart, +) -from pip._vendor.pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from pip._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from pip._vendor.pyparsing import Literal as L # noqa -from pip._vendor.six.moves.urllib import parse as urlparse - -from ._typing import MYPY_CHECK_RUNNING from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import List - class InvalidRequirement(ValueError): """ @@ -55,7 +60,7 @@ VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY VERSION_MANY = Combine( VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False )("_raw_spec") -_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) _VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") @@ -79,7 +84,7 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd REQUIREMENT.parseString("x[]") -class Requirement(object): +class Requirement: """Parse a requirement. Parse a given requirement string into its parts, such as name, specifier, @@ -92,54 +97,50 @@ class Requirement(object): # the thing as well as the version? What about the markers? # TODO: Can we normalize the name and extra name? - def __init__(self, requirement_string): - # type: (str) -> None + def __init__(self, requirement_string: str) -> None: try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( - 'Parse error at "{0!r}": {1}'.format( - requirement_string[e.loc : e.loc + 8], e.msg - ) + f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' ) - self.name = req.name + self.name: str = req.name if req.url: - parsed_url = urlparse.urlparse(req.url) + parsed_url = urllib.parse.urlparse(req.url) if parsed_url.scheme == "file": - if urlparse.urlunparse(parsed_url) != req.url: + if urllib.parse.urlunparse(parsed_url) != req.url: raise InvalidRequirement("Invalid URL given") elif not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): - raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url + raise InvalidRequirement(f"Invalid URL: {req.url}") + self.url: TOptional[str] = req.url else: self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None + self.extras: Set[str] = set(req.extras.asList() if req.extras else []) + self.specifier: SpecifierSet = SpecifierSet(req.specifier) + self.marker: TOptional[Marker] = req.marker if req.marker else None - def __str__(self): - # type: () -> str - parts = [self.name] # type: List[str] + def __str__(self) -> str: + parts: List[str] = [self.name] if self.extras: - parts.append("[{0}]".format(",".join(sorted(self.extras)))) + formatted_extras = ",".join(sorted(self.extras)) + parts.append(f"[{formatted_extras}]") if self.specifier: parts.append(str(self.specifier)) if self.url: - parts.append("@ {0}".format(self.url)) + parts.append(f"@ {self.url}") if self.marker: parts.append(" ") if self.marker: - parts.append("; {0}".format(self.marker)) + parts.append(f"; {self.marker}") return "".join(parts) - def __repr__(self): - # type: () -> str - return "".format(str(self)) + def __repr__(self) -> str: + return f"" diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py index 94987486d4b47275fb32b97a5dcb11b76742c73b..ce66bd4addbde1e332e9a42f6eb62adc471193e5 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py @@ -1,33 +1,33 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import abc import functools import itertools import re - -from ._compat import string_types, with_metaclass -from ._typing import MYPY_CHECK_RUNNING -from .version import Version, LegacyVersion, parse - -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import ( - List, - Dict, - Union, - Iterable, - Iterator, - Optional, - Callable, - Tuple, - FrozenSet, - ) - - ParsedVersion = Union[Version, LegacyVersion] - UnparsedVersion = Union[Version, LegacyVersion, str] - CallableOperator = Callable[[ParsedVersion, str], bool] +import warnings +from typing import ( + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Pattern, + Set, + Tuple, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import LegacyVersion, Version, parse + +ParsedVersion = Union[Version, LegacyVersion] +UnparsedVersion = Union[Version, LegacyVersion, str] +VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) +CallableOperator = Callable[[ParsedVersion, str], bool] class InvalidSpecifier(ValueError): @@ -36,64 +36,58 @@ class InvalidSpecifier(ValueError): """ -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore +class BaseSpecifier(metaclass=abc.ABCMeta): @abc.abstractmethod - def __str__(self): - # type: () -> str + def __str__(self) -> str: """ Returns the str representation of this Specifier like object. This should be representative of the Specifier itself. """ @abc.abstractmethod - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: """ Returns a hash value for this Specifier like object. """ @abc.abstractmethod - def __eq__(self, other): - # type: (object) -> bool + def __eq__(self, other: object) -> bool: """ Returns a boolean representing whether or not the two Specifier like objects are equal. """ @abc.abstractmethod - def __ne__(self, other): - # type: (object) -> bool + def __ne__(self, other: object) -> bool: """ Returns a boolean representing whether or not the two Specifier like objects are not equal. """ @abc.abstractproperty - def prereleases(self): - # type: () -> Optional[bool] + def prereleases(self) -> Optional[bool]: """ Returns whether or not pre-releases as a whole are allowed by this specifier. """ @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None + def prereleases(self, value: bool) -> None: """ Sets whether or not pre-releases as a whole are allowed by this specifier. """ @abc.abstractmethod - def contains(self, item, prereleases=None): - # type: (str, Optional[bool]) -> bool + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. @@ -102,43 +96,43 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore class _IndividualSpecifier(BaseSpecifier): - _operators = {} # type: Dict[str, str] + _operators: Dict[str, str] = {} + _regex: Pattern[str] - def __init__(self, spec="", prereleases=None): - # type: (str, Optional[bool]) -> None + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: match = self._regex.search(spec) if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - self._spec = ( + self._spec: Tuple[str, str] = ( match.group("operator").strip(), match.group("version").strip(), - ) # type: Tuple[str, str] + ) # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: pre = ( - ", prereleases={0!r}".format(self.prereleases) + f", prereleases={self.prereleases!r}" if self._prereleases is not None else "" ) - return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) + return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre) - def __str__(self): - # type: () -> str - return "{0}{1}".format(*self._spec) + def __str__(self) -> str: + return "{}{}".format(*self._spec) - def __hash__(self): - # type: () -> int - return hash(self._spec) + @property + def _canonical_spec(self) -> Tuple[str, str]: + return self._spec[0], canonicalize_version(self._spec[1]) + + def __hash__(self) -> int: + return hash(self._canonical_spec) - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): + def __eq__(self, other: object) -> bool: + if isinstance(other, str): try: other = self.__class__(str(other)) except InvalidSpecifier: @@ -146,11 +140,10 @@ class _IndividualSpecifier(BaseSpecifier): elif not isinstance(other, self.__class__): return NotImplemented - return self._spec == other._spec + return self._canonical_spec == other._canonical_spec - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, string_types): + def __ne__(self, other: object) -> bool: + if isinstance(other, str): try: other = self.__class__(str(other)) except InvalidSpecifier: @@ -160,45 +153,39 @@ class _IndividualSpecifier(BaseSpecifier): return self._spec != other._spec - def _get_operator(self, op): - # type: (str) -> CallableOperator - operator_callable = getattr( - self, "_compare_{0}".format(self._operators[op]) - ) # type: CallableOperator + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) return operator_callable - def _coerce_version(self, version): - # type: (UnparsedVersion) -> ParsedVersion + def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: if not isinstance(version, (LegacyVersion, Version)): version = parse(version) return version @property - def operator(self): - # type: () -> str + def operator(self) -> str: return self._spec[0] @property - def version(self): - # type: () -> str + def version(self) -> str: return self._spec[1] @property - def prereleases(self): - # type: () -> Optional[bool] + def prereleases(self) -> Optional[bool]: return self._prereleases @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None + def prereleases(self, value: bool) -> None: self._prereleases = value - def __contains__(self, item): - # type: (str) -> bool + def __contains__(self, item: str) -> bool: return self.contains(item) - def contains(self, item, prereleases=None): - # type: (UnparsedVersion, Optional[bool]) -> bool + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: # Determine if prereleases are to be allowed or not. if prereleases is None: @@ -216,11 +203,12 @@ class _IndividualSpecifier(BaseSpecifier): # Actually do the comparison to determine if this item is contained # within this Specifier or not. - operator_callable = self._get_operator(self.operator) # type: CallableOperator + operator_callable: CallableOperator = self._get_operator(self.operator) return operator_callable(normalized_item, self.version) - def filter(self, iterable, prereleases=None): - # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: yielded = False found_prereleases = [] @@ -234,7 +222,7 @@ class _IndividualSpecifier(BaseSpecifier): if self.contains(parsed_version, **kw): # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing + # prereleases, then we'll store it for later in case nothing # else matches this specifier. if parsed_version.is_prerelease and not ( prereleases or self.prereleases @@ -279,44 +267,46 @@ class LegacySpecifier(_IndividualSpecifier): ">": "greater_than", } - def _coerce_version(self, version): - # type: (Union[ParsedVersion, str]) -> LegacyVersion + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + super().__init__(spec, prereleases) + + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: if not isinstance(version, LegacyVersion): version = LegacyVersion(str(version)) return version - def _compare_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool + def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective == self._coerce_version(spec) - def _compare_not_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool + def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective != self._coerce_version(spec) - def _compare_less_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool + def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective <= self._coerce_version(spec) - def _compare_greater_than_equal(self, prospective, spec): - # type: (LegacyVersion, str) -> bool + def _compare_greater_than_equal( + self, prospective: LegacyVersion, spec: str + ) -> bool: return prospective >= self._coerce_version(spec) - def _compare_less_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool + def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: return prospective < self._coerce_version(spec) - def _compare_greater_than(self, prospective, spec): - # type: (LegacyVersion, str) -> bool + def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: return prospective > self._coerce_version(spec) def _require_version_compare( - fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) -): - # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] + fn: Callable[["Specifier", ParsedVersion, str], bool] +) -> Callable[["Specifier", ParsedVersion, str], bool]: @functools.wraps(fn) - def wrapped(self, prospective, spec): - # type: (Specifier, ParsedVersion, str) -> bool + def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: if not isinstance(prospective, Version): return False return fn(self, prospective, spec) @@ -433,8 +423,7 @@ class Specifier(_IndividualSpecifier): } @_require_version_compare - def _compare_compatible(self, prospective, spec): - # type: (ParsedVersion, str) -> bool + def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to @@ -443,15 +432,9 @@ class Specifier(_IndividualSpecifier): # the other specifiers. # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. + # ignore suffix segments. prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] ) # Add the prefix notation to the end of our string @@ -462,8 +445,7 @@ class Specifier(_IndividualSpecifier): ) @_require_version_compare - def _compare_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool + def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: # We need special logic to handle prefix matching if spec.endswith(".*"): @@ -503,23 +485,29 @@ class Specifier(_IndividualSpecifier): return prospective == spec_version @_require_version_compare - def _compare_not_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool + def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: return not self._compare_equal(prospective, spec) @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - return prospective <= Version(spec) + def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - # type: (ParsedVersion, str) -> bool - return prospective >= Version(spec) + def _compare_greater_than_equal( + self, prospective: ParsedVersion, spec: str + ) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) @_require_version_compare - def _compare_less_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool + def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: # Convert our spec to a Version instance, since we'll want to work with # it as a version. @@ -545,8 +533,7 @@ class Specifier(_IndividualSpecifier): return True @_require_version_compare - def _compare_greater_than(self, prospective, spec_str): - # type: (ParsedVersion, str) -> bool + def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: # Convert our spec to a Version instance, since we'll want to work with # it as a version. @@ -577,13 +564,11 @@ class Specifier(_IndividualSpecifier): # same version in the spec. return True - def _compare_arbitrary(self, prospective, spec): - # type: (Version, str) -> bool + def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: return str(prospective).lower() == str(spec).lower() @property - def prereleases(self): - # type: () -> bool + def prereleases(self) -> bool: # If there is an explicit prereleases set for this, then we'll just # blindly use that. @@ -608,17 +593,15 @@ class Specifier(_IndividualSpecifier): return False @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None + def prereleases(self, value: bool) -> None: self._prereleases = value _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") -def _version_split(version): - # type: (str) -> List[str] - result = [] # type: List[str] +def _version_split(version: str) -> List[str]: + result: List[str] = [] for item in version.split("."): match = _prefix_regex.search(item) if match: @@ -628,8 +611,13 @@ def _version_split(version): return result -def _pad_version(left, right): - # type: (List[str], List[str]) -> Tuple[List[str], List[str]] +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: left_split, right_split = [], [] # Get the release segment of our versions @@ -648,8 +636,9 @@ def _pad_version(left, right): class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): - # type: (str, Optional[bool]) -> None + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: # Split on , to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. @@ -657,7 +646,7 @@ class SpecifierSet(BaseSpecifier): # Parsed each individual specifier, attempting first to make it a # Specifier and falling back to a LegacySpecifier. - parsed = set() + parsed: Set[_IndividualSpecifier] = set() for specifier in split_specifiers: try: parsed.add(Specifier(specifier)) @@ -671,27 +660,23 @@ class SpecifierSet(BaseSpecifier): # we accept prereleases or not. self._prereleases = prereleases - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: pre = ( - ", prereleases={0!r}".format(self.prereleases) + f", prereleases={self.prereleases!r}" if self._prereleases is not None else "" ) - return "".format(str(self), pre) + return "".format(str(self), pre) - def __str__(self): - # type: () -> str + def __str__(self) -> str: return ",".join(sorted(str(s) for s in self._specs)) - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash(self._specs) - def __and__(self, other): - # type: (Union[SpecifierSet, str]) -> SpecifierSet - if isinstance(other, string_types): + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + if isinstance(other, str): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -713,35 +698,30 @@ class SpecifierSet(BaseSpecifier): return specifier - def __eq__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): + def __eq__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs == other._specs - def __ne__(self, other): - # type: (object) -> bool - if isinstance(other, (string_types, _IndividualSpecifier)): + def __ne__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs != other._specs - def __len__(self): - # type: () -> int + def __len__(self) -> int: return len(self._specs) - def __iter__(self): - # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] + def __iter__(self) -> Iterator[_IndividualSpecifier]: return iter(self._specs) @property - def prereleases(self): - # type: () -> Optional[bool] + def prereleases(self) -> Optional[bool]: # If we have been given an explicit prerelease modifier, then we'll # pass that through here. @@ -759,16 +739,15 @@ class SpecifierSet(BaseSpecifier): return any(s.prereleases for s in self._specs) @prereleases.setter - def prereleases(self, value): - # type: (bool) -> None + def prereleases(self, value: bool) -> None: self._prereleases = value - def __contains__(self, item): - # type: (Union[ParsedVersion, str]) -> bool + def __contains__(self, item: UnparsedVersion) -> bool: return self.contains(item) - def contains(self, item, prereleases=None): - # type: (Union[ParsedVersion, str], Optional[bool]) -> bool + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: # Ensure that our item is a Version or LegacyVersion instance. if not isinstance(item, (LegacyVersion, Version)): @@ -796,11 +775,8 @@ class SpecifierSet(BaseSpecifier): return all(s.contains(item, prereleases=prereleases) for s in self._specs) def filter( - self, - iterable, # type: Iterable[Union[ParsedVersion, str]] - prereleases=None, # type: Optional[bool] - ): - # type: (...) -> Iterable[Union[ParsedVersion, str]] + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the @@ -819,8 +795,11 @@ class SpecifierSet(BaseSpecifier): # which will filter out any pre-releases, unless there are no final # releases, and which will filter out LegacyVersion in general. else: - filtered = [] # type: List[Union[ParsedVersion, str]] - found_prereleases = [] # type: List[Union[ParsedVersion, str]] + filtered: List[VersionTypeVar] = [] + found_prereleases: List[VersionTypeVar] = [] + + item: UnparsedVersion + parsed_version: Union[Version, LegacyVersion] for item in iterable: # Ensure that we some kind of Version class for this item. diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/tags.py b/venv/Lib/site-packages/pip/_vendor/packaging/tags.py index 300faab847616e48be1883babffeb39197aee1a6..82a47cdae8b3fa48189f578e5e49e67f4f4ae443 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/tags.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/tags.py @@ -2,112 +2,104 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import - -import distutils.util - -try: - from importlib.machinery import EXTENSION_SUFFIXES -except ImportError: # pragma: no cover - import imp - - EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] - del imp import logging -import os import platform -import re -import struct import sys import sysconfig -import warnings - -from ._typing import MYPY_CHECK_RUNNING, cast - -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import ( - Dict, - FrozenSet, - IO, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - ) - - PythonVersion = Sequence[int] - MacVersion = Tuple[int, int] - GlibcVersion = Tuple[int, int] - +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux logger = logging.getLogger(__name__) -INTERPRETER_SHORT_NAMES = { +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { "python": "py", # Generic. "cpython": "cp", "pypy": "pp", "ironpython": "ip", "jython": "jy", -} # type: Dict[str, str] +} _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 -class Tag(object): +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ - __slots__ = ["_interpreter", "_abi", "_platform"] + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] - def __init__(self, interpreter, abi, platform): - # type: (str, str, str) -> None + def __init__(self, interpreter: str, abi: str, platform: str) -> None: self._interpreter = interpreter.lower() self._abi = abi.lower() self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) @property - def interpreter(self): - # type: () -> str + def interpreter(self) -> str: return self._interpreter @property - def abi(self): - # type: () -> str + def abi(self) -> str: return self._abi @property - def platform(self): - # type: () -> str + def platform(self) -> str: return self._platform - def __eq__(self, other): - # type: (object) -> bool + def __eq__(self, other: object) -> bool: if not isinstance(other, Tag): return NotImplemented return ( - (self.platform == other.platform) - and (self.abi == other.abi) - and (self.interpreter == other.interpreter) + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) ) - def __hash__(self): - # type: () -> int - return hash((self._interpreter, self._abi, self._platform)) + def __hash__(self) -> int: + return self._hash - def __str__(self): - # type: () -> str - return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" - def __repr__(self): - # type: () -> str + def __repr__(self) -> str: return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) -def parse_tag(tag): - # type: (str) -> FrozenSet[Tag] +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ tags = set() interpreters, abis, platforms = tag.split("-") for interpreter in interpreters.split("."): @@ -117,24 +109,7 @@ def parse_tag(tag): return frozenset(tags) -def _warn_keyword_parameter(func_name, kwargs): - # type: (str, Dict[str, bool]) -> bool - """ - Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. - """ - if not kwargs: - return False - elif len(kwargs) > 1 or "warn" not in kwargs: - kwargs.pop("warn", None) - arg = next(iter(kwargs.keys())) - raise TypeError( - "{}() got an unexpected keyword argument {!r}".format(func_name, arg) - ) - return kwargs["warn"] - - -def _get_config_var(name, warn=False): - # type: (str, bool) -> Union[int, str, None] +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: value = sysconfig.get_config_var(name) if value is None and warn: logger.debug( @@ -143,13 +118,11 @@ def _get_config_var(name, warn=False): return value -def _normalize_string(string): - # type: (str) -> str +def _normalize_string(string: str) -> str: return string.replace(".", "_").replace("-", "_") -def _abi3_applies(python_version): - # type: (PythonVersion) -> bool +def _abi3_applies(python_version: PythonVersion) -> bool: """ Determine if the Python version supports abi3. @@ -158,8 +131,7 @@ def _abi3_applies(python_version): return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version, warn=False): - # type: (PythonVersion, bool) -> List[str] +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: py_version = tuple(py_version) # To allow for version comparison. abis = [] version = _version_nodot(py_version[:2]) @@ -185,7 +157,7 @@ def _cpython_abis(py_version, warn=False): elif debug: # Debug builds can also load "normal" extension modules. # We can also assume no UCS-4 or pymalloc requirement. - abis.append("cp{version}".format(version=version)) + abis.append(f"cp{version}") abis.insert( 0, "cp{version}{debug}{pymalloc}{ucs4}".format( @@ -196,12 +168,12 @@ def _cpython_abis(py_version, warn=False): def cpython_tags( - python_version=None, # type: Optional[PythonVersion] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: """ Yields the tags for a CPython interpreter. @@ -217,7 +189,6 @@ def cpython_tags( If 'abi3' or 'none' are specified in 'abis' then they will be yielded at their normal position and not at the beginning. """ - warn = _warn_keyword_parameter("cpython_tags", kwargs) if not python_version: python_version = sys.version_info[:2] @@ -241,10 +212,8 @@ def cpython_tags( for platform_ in platforms: yield Tag(interpreter, abi, platform_) if _abi3_applies(python_version): - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): - yield tag + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) if _abi3_applies(python_version): for minor_version in range(python_version[1] - 1, 1, -1): @@ -255,20 +224,19 @@ def cpython_tags( yield Tag(interpreter, "abi3", platform_) -def _generic_abi(): - # type: () -> Iterator[str] +def _generic_abi() -> Iterator[str]: abi = sysconfig.get_config_var("SOABI") if abi: yield _normalize_string(abi) def generic_tags( - interpreter=None, # type: Optional[str] - abis=None, # type: Optional[Iterable[str]] - platforms=None, # type: Optional[Iterable[str]] - **kwargs # type: bool -): - # type: (...) -> Iterator[Tag] + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: """ Yields the tags for a generic interpreter. @@ -277,7 +245,6 @@ def generic_tags( The "none" ABI will be added if it was not explicitly provided. """ - warn = _warn_keyword_parameter("generic_tags", kwargs) if not interpreter: interp_name = interpreter_name() interp_version = interpreter_version(warn=warn) @@ -293,8 +260,7 @@ def generic_tags( yield Tag(interpreter, abi, platform_) -def _py_interpreter_range(py_version): - # type: (PythonVersion) -> Iterator[str] +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: """ Yields Python versions in descending order. @@ -310,11 +276,10 @@ def _py_interpreter_range(py_version): def compatible_tags( - python_version=None, # type: Optional[PythonVersion] - interpreter=None, # type: Optional[str] - platforms=None, # type: Optional[Iterable[str]] -): - # type: (...) -> Iterator[Tag] + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> Iterator[Tag]: """ Yields the sequence of tags that are compatible with a specific version of Python. @@ -335,8 +300,7 @@ def compatible_tags( yield Tag(version, "none", "any") -def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): - # type: (str, bool) -> str +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: if not is_32bit: return arch @@ -346,8 +310,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): return "i386" -def _mac_binary_formats(version, cpu_arch): - # type: (MacVersion, str) -> List[str] +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -370,12 +333,18 @@ def _mac_binary_formats(version, cpu_arch): return [] formats.extend(["fat32", "fat"]) - formats.append("universal") + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + return formats -def mac_platforms(version=None, arch=None): - # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> Iterator[str]: """ Yields the platform tags for a macOS system. @@ -384,7 +353,7 @@ def mac_platforms(version=None, arch=None): generate platform tags for. Both parameters default to the appropriate value for the current system. """ - version_str, _, cpu_arch = platform.mac_ver() # type: ignore + version_str, _, cpu_arch = platform.mac_ver() if version is None: version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) else: @@ -393,283 +362,76 @@ def mac_platforms(version=None, arch=None): arch = _mac_arch(cpu_arch) else: arch = arch - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # type: (str, GlibcVersion) -> bool - # Check for presence of _manylinux module. - try: - import _manylinux # noqa - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. - pass - - return _have_compatible_glibc(*glibc_version) - - -def _glibc_version_string(): - # type: () -> Optional[str] - # Returns glibc version string, or None if not using glibc. - return _glibc_version_string_confstr() or _glibc_version_string_ctypes() - - -def _glibc_version_string_confstr(): - # type: () -> Optional[str] - """ - Primary implementation of glibc_version_string using os.confstr. - """ - # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely - # to be broken or missing. This strategy is used in the standard library - # platform module. - # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 - try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 - "CS_GNU_LIBC_VERSION" - ) - assert version_string is not None - _, version = version_string.split() # type: Tuple[str, str] - except (AssertionError, AttributeError, OSError, ValueError): - # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... - return None - return version - + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) -def _glibc_version_string_ctypes(): - # type: () -> Optional[str] - """ - Fallback implementation of glibc_version_string using ctypes. - """ - try: - import ctypes - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # Note: typeshed is wrong here so we are ignoring this line. - process_namespace = ctypes.CDLL(None) # type: ignore - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() # type: str - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # type: (str, int, int) -> bool - # Parse string and check against requested version. - # - # We use a regexp instead of str.split because we want to discard any - # random junk that might come after the minor version -- this might happen - # in patched/forked versions of glibc (e.g. Linaro's version of glibc - # uses version strings like "2.20-2014.11"). See gh-3588. - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) -def _have_compatible_glibc(required_major, minimum_minor): - # type: (int, int) -> bool - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader(object): - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file): - # type: (IO[bytes]) -> None - def unpack(fmt): - # type: (str) -> int - try: - result, = struct.unpack( - fmt, file.read(struct.calcsize(fmt)) - ) # type: (int, ) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header(): - # type: () -> Optional[_ELFFileHeader] - try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header - - -def _is_linux_armhf(): - # type: () -> bool - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686(): - # type: () -> bool - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result - - -def _have_compatible_manylinux_abi(arch): - # type: (str) -> bool - if arch == "armv7l": - return _is_linux_armhf() - if arch == "i686": - return _is_linux_i686() - return True - - -def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): - # type: (bool) -> Iterator[str] - linux = _normalize_string(distutils.util.get_platform()) +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) if is_32bit: if linux == "linux_x86_64": linux = "linux_i686" elif linux == "linux_aarch64": linux = "linux_armv7l" - manylinux_support = [] _, arch = linux.split("_", 1) - if _have_compatible_manylinux_abi(arch): - if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: - manylinux_support.append( - ("manylinux2014", (2, 17)) - ) # CentOS 7 w/ glibc 2.17 (PEP 599) - if arch in {"x86_64", "i686"}: - manylinux_support.append( - ("manylinux2010", (2, 12)) - ) # CentOS 6 w/ glibc 2.12 (PEP 571) - manylinux_support.append( - ("manylinux1", (2, 5)) - ) # CentOS 5 w/ glibc 2.5 (PEP 513) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - yield linux.replace("linux", name) - break - # Support for a later manylinux implies support for an earlier version. - for name, _ in manylinux_support_iter: - yield linux.replace("linux", name) + yield from _manylinux.platform_tags(linux, arch) + yield from _musllinux.platform_tags(arch) yield linux -def _generic_platforms(): - # type: () -> Iterator[str] - yield _normalize_string(distutils.util.get_platform()) +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) -def _platform_tags(): - # type: () -> Iterator[str] +def _platform_tags() -> Iterator[str]: """ Provides the platform tags for this installation. """ @@ -681,25 +443,18 @@ def _platform_tags(): return _generic_platforms() -def interpreter_name(): - # type: () -> str +def interpreter_name() -> str: """ Returns the name of the running interpreter. """ - try: - name = sys.implementation.name # type: ignore - except AttributeError: # pragma: no cover - # Python 2.7 compatibility. - name = platform.python_implementation().lower() + name = sys.implementation.name return INTERPRETER_SHORT_NAMES.get(name) or name -def interpreter_version(**kwargs): - # type: (bool) -> str +def interpreter_version(*, warn: bool = False) -> str: """ Returns the version of the running interpreter. """ - warn = _warn_keyword_parameter("interpreter_version", kwargs) version = _get_config_var("py_version_nodot", warn=warn) if version: version = str(version) @@ -708,32 +463,22 @@ def interpreter_version(**kwargs): return version -def _version_nodot(version): - # type: (PythonVersion) -> str - if any(v >= 10 for v in version): - sep = "_" - else: - sep = "" - return sep.join(map(str, version)) +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) -def sys_tags(**kwargs): - # type: (bool) -> Iterator[Tag] +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: """ Returns the sequence of tag triples for the running interpreter. The order of the sequence corresponds to priority order for the interpreter, from most to least important. """ - warn = _warn_keyword_parameter("sys_tags", kwargs) interp_name = interpreter_name() if interp_name == "cp": - for tag in cpython_tags(warn=warn): - yield tag + yield from cpython_tags(warn=warn) else: - for tag in generic_tags(): - yield tag + yield from generic_tags() - for tag in compatible_tags(): - yield tag + yield from compatible_tags() diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/utils.py b/venv/Lib/site-packages/pip/_vendor/packaging/utils.py index 44f1bf987325b92c5c2353fa236d1c7ae6c9cfb6..bab11b80c60f10a4f3bccb12eb5b17c48a449767 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/utils.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/utils.py @@ -1,62 +1,136 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import re +from typing import FrozenSet, NewType, Tuple, Union, cast -from ._typing import MYPY_CHECK_RUNNING +from .tags import Tag, parse_tag from .version import InvalidVersion, Version -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import Union +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + _canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") -def canonicalize_name(name): - # type: (str) -> str +def canonicalize_name(name: str) -> NormalizedName: # This is taken from PEP 503. - return _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) -def canonicalize_version(_version): - # type: (str) -> Union[Version, str] +def canonicalize_version(version: Union[Version, str]) -> str: """ This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. """ - - try: - version = Version(_version) - except InvalidVersion: - # Legacy versions cannot be normalized - return _version + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version parts = [] # Epoch - if version.epoch != 0: - parts.append("{0}!".format(version.epoch)) + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") # Release segment # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) # Pre-release - if version.pre is not None: - parts.append("".join(str(x) for x in version.pre)) + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) # Post-release - if version.post is not None: - parts.append(".post{0}".format(version.post)) + if parsed.post is not None: + parts.append(f".post{parsed.post}") # Development release - if version.dev is not None: - parts.append(".dev{0}".format(version.dev)) + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") # Local version segment - if version.local is not None: - parts.append("+{0}".format(version.local)) + if parsed.local is not None: + parts.append(f"+{parsed.local}") return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/venv/Lib/site-packages/pip/_vendor/packaging/version.py b/venv/Lib/site-packages/pip/_vendor/packaging/version.py index f39a2a12a1b428d2cac999932643c1f1976cce6c..de9a09a4ed3b078b37e7490a6686f660ae935aca 100644 --- a/venv/Lib/site-packages/pip/_vendor/packaging/version.py +++ b/venv/Lib/site-packages/pip/_vendor/packaging/version.py @@ -1,52 +1,45 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import collections import itertools import re +import warnings +from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union -from ._structures import Infinity, NegativeInfinity -from ._typing import MYPY_CHECK_RUNNING - -if MYPY_CHECK_RUNNING: # pragma: no cover - from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union - - from ._structures import InfinityType, NegativeInfinityType - - InfiniteTypes = Union[InfinityType, NegativeInfinityType] - PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] - SubLocalType = Union[InfiniteTypes, int, str] - LocalType = Union[ - NegativeInfinityType, - Tuple[ - Union[ - SubLocalType, - Tuple[SubLocalType, str], - Tuple[NegativeInfinityType, SubLocalType], - ], - ..., - ], - ] - CmpKey = Tuple[ - int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType - ] - LegacyCmpKey = Tuple[int, Tuple[str, ...]] - VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool - ] +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType __all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] +InfiniteTypes = Union[InfinityType, NegativeInfinityType] +PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] +SubLocalType = Union[InfiniteTypes, int, str] +LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], +] +CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType +] +LegacyCmpKey = Tuple[int, Tuple[str, ...]] +VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool +] _Version = collections.namedtuple( "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) -def parse(version): - # type: (str) -> Union[LegacyVersion, Version] +def parse(version: str) -> Union["LegacyVersion", "Version"]: """ Parse the given version string and return either a :class:`Version` object or a :class:`LegacyVersion` object depending on if the given version is @@ -64,112 +57,111 @@ class InvalidVersion(ValueError): """ -class _BaseVersion(object): - _key = None # type: Union[CmpKey, LegacyCmpKey] +class _BaseVersion: + _key: Union[CmpKey, LegacyCmpKey] - def __hash__(self): - # type: () -> int + def __hash__(self) -> int: return hash(self._key) - def __lt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s < o) + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key - def __le__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s <= o) + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented - def __eq__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s == o) + return self._key == other._key - def __ge__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s >= o) + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented - def __gt__(self, other): - # type: (_BaseVersion) -> bool - return self._compare(other, lambda s, o: s > o) + return self._key >= other._key - def __ne__(self, other): - # type: (object) -> bool - return self._compare(other, lambda s, o: s != o) + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key - def _compare(self, other, method): - # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] + def __ne__(self, other: object) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented - return method(self._key, other._key) + return self._key != other._key class LegacyVersion(_BaseVersion): - def __init__(self, version): - # type: (str) -> None + def __init__(self, version: str) -> None: self._version = str(version) self._key = _legacy_cmpkey(self._version) - def __str__(self): - # type: () -> str + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def __str__(self) -> str: return self._version - def __repr__(self): - # type: () -> str - return "".format(repr(str(self))) + def __repr__(self) -> str: + return f"" @property - def public(self): - # type: () -> str + def public(self) -> str: return self._version @property - def base_version(self): - # type: () -> str + def base_version(self) -> str: return self._version @property - def epoch(self): - # type: () -> int + def epoch(self) -> int: return -1 @property - def release(self): - # type: () -> None + def release(self) -> None: return None @property - def pre(self): - # type: () -> None + def pre(self) -> None: return None @property - def post(self): - # type: () -> None + def post(self) -> None: return None @property - def dev(self): - # type: () -> None + def dev(self) -> None: return None @property - def local(self): - # type: () -> None + def local(self) -> None: return None @property - def is_prerelease(self): - # type: () -> bool + def is_prerelease(self) -> bool: return False @property - def is_postrelease(self): - # type: () -> bool + def is_postrelease(self) -> bool: return False @property - def is_devrelease(self): - # type: () -> bool + def is_devrelease(self) -> bool: return False @@ -184,8 +176,7 @@ _legacy_version_replacement_map = { } -def _parse_version_parts(s): - # type: (str) -> Iterator[str] +def _parse_version_parts(s: str) -> Iterator[str]: for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -202,8 +193,7 @@ def _parse_version_parts(s): yield "*final" -def _legacy_cmpkey(version): - # type: (str) -> LegacyCmpKey +def _legacy_cmpkey(version: str) -> LegacyCmpKey: # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, @@ -213,7 +203,7 @@ def _legacy_cmpkey(version): # This scheme is taken from pkg_resources.parse_version setuptools prior to # it's adoption of the packaging library. - parts = [] # type: List[str] + parts: List[str] = [] for part in _parse_version_parts(version.lower()): if part.startswith("*"): # remove "-" before a prerelease tag @@ -268,13 +258,12 @@ class Version(_BaseVersion): _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) - def __init__(self, version): - # type: (str) -> None + def __init__(self, version: str) -> None: # Validate the version and parse it into pieces match = self._regex.search(version) if not match: - raise InvalidVersion("Invalid version: '{0}'".format(version)) + raise InvalidVersion(f"Invalid version: '{version}'") # Store the parsed out pieces of the version self._version = _Version( @@ -298,17 +287,15 @@ class Version(_BaseVersion): self._version.local, ) - def __repr__(self): - # type: () -> str - return "".format(repr(str(self))) + def __repr__(self) -> str: + return f"" - def __str__(self): - # type: () -> str + def __str__(self) -> str: parts = [] # Epoch if self.epoch != 0: - parts.append("{0}!".format(self.epoch)) + parts.append(f"{self.epoch}!") # Release segment parts.append(".".join(str(x) for x in self.release)) @@ -319,67 +306,59 @@ class Version(_BaseVersion): # Post-release if self.post is not None: - parts.append(".post{0}".format(self.post)) + parts.append(f".post{self.post}") # Development release if self.dev is not None: - parts.append(".dev{0}".format(self.dev)) + parts.append(f".dev{self.dev}") # Local version segment if self.local is not None: - parts.append("+{0}".format(self.local)) + parts.append(f"+{self.local}") return "".join(parts) @property - def epoch(self): - # type: () -> int - _epoch = self._version.epoch # type: int + def epoch(self) -> int: + _epoch: int = self._version.epoch return _epoch @property - def release(self): - # type: () -> Tuple[int, ...] - _release = self._version.release # type: Tuple[int, ...] + def release(self) -> Tuple[int, ...]: + _release: Tuple[int, ...] = self._version.release return _release @property - def pre(self): - # type: () -> Optional[Tuple[str, int]] - _pre = self._version.pre # type: Optional[Tuple[str, int]] + def pre(self) -> Optional[Tuple[str, int]]: + _pre: Optional[Tuple[str, int]] = self._version.pre return _pre @property - def post(self): - # type: () -> Optional[Tuple[str, int]] + def post(self) -> Optional[int]: return self._version.post[1] if self._version.post else None @property - def dev(self): - # type: () -> Optional[Tuple[str, int]] + def dev(self) -> Optional[int]: return self._version.dev[1] if self._version.dev else None @property - def local(self): - # type: () -> Optional[str] + def local(self) -> Optional[str]: if self._version.local: return ".".join(str(x) for x in self._version.local) else: return None @property - def public(self): - # type: () -> str + def public(self) -> str: return str(self).split("+", 1)[0] @property - def base_version(self): - # type: () -> str + def base_version(self) -> str: parts = [] # Epoch if self.epoch != 0: - parts.append("{0}!".format(self.epoch)) + parts.append(f"{self.epoch}!") # Release segment parts.append(".".join(str(x) for x in self.release)) @@ -387,41 +366,33 @@ class Version(_BaseVersion): return "".join(parts) @property - def is_prerelease(self): - # type: () -> bool + def is_prerelease(self) -> bool: return self.dev is not None or self.pre is not None @property - def is_postrelease(self): - # type: () -> bool + def is_postrelease(self) -> bool: return self.post is not None @property - def is_devrelease(self): - # type: () -> bool + def is_devrelease(self) -> bool: return self.dev is not None @property - def major(self): - # type: () -> int + def major(self) -> int: return self.release[0] if len(self.release) >= 1 else 0 @property - def minor(self): - # type: () -> int + def minor(self) -> int: return self.release[1] if len(self.release) >= 2 else 0 @property - def micro(self): - # type: () -> int + def micro(self) -> int: return self.release[2] if len(self.release) >= 3 else 0 def _parse_letter_version( - letter, # type: str - number, # type: Union[str, bytes, SupportsInt] -): - # type: (...) -> Optional[Tuple[str, int]] + letter: str, number: Union[str, bytes, SupportsInt] +) -> Optional[Tuple[str, int]]: if letter: # We consider there to be an implicit 0 in a pre-release if there is @@ -458,8 +429,7 @@ def _parse_letter_version( _local_version_separators = re.compile(r"[\._-]") -def _parse_local_version(local): - # type: (str) -> Optional[LocalType] +def _parse_local_version(local: str) -> Optional[LocalType]: """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -472,14 +442,13 @@ def _parse_local_version(local): def _cmpkey( - epoch, # type: int - release, # type: Tuple[int, ...] - pre, # type: Optional[Tuple[str, int]] - post, # type: Optional[Tuple[str, int]] - dev, # type: Optional[Tuple[str, int]] - local, # type: Optional[Tuple[SubLocalType]] -): - # type: (...) -> CmpKey + epoch: int, + release: Tuple[int, ...], + pre: Optional[Tuple[str, int]], + post: Optional[Tuple[str, int]], + dev: Optional[Tuple[str, int]], + local: Optional[Tuple[SubLocalType]], +) -> CmpKey: # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now @@ -495,7 +464,7 @@ def _cmpkey( # if there is not a pre or a post segment. If we have one of those then # the normal sorting rules will handle this case correctly. if pre is None and post is None and dev is not None: - _pre = NegativeInfinity # type: PrePostDevType + _pre: PrePostDevType = NegativeInfinity # Versions without a pre-release (except as noted above) should sort after # those with one. elif pre is None: @@ -505,21 +474,21 @@ def _cmpkey( # Versions without a post segment should sort before those with one. if post is None: - _post = NegativeInfinity # type: PrePostDevType + _post: PrePostDevType = NegativeInfinity else: _post = post # Versions without a development segment should sort after those with one. if dev is None: - _dev = Infinity # type: PrePostDevType + _dev: PrePostDevType = Infinity else: _dev = dev if local is None: # Versions without a local segment should sort before those with one. - _local = NegativeInfinity # type: LocalType + _local: LocalType = NegativeInfinity else: # Versions with a local segment need that segment parsed to implement # the sorting rules in PEP440. diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py b/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py index 7355b68a2402f132a4c51e7b2bd09f3d64f0fd2c..2b6b8856790aab7b0e1f70ad7a75be7f9a21e736 100644 --- a/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/pep517/__init__.py @@ -1,4 +1,6 @@ """Wrappers to build Python packages using PEP 517 hooks """ -__version__ = '0.8.2' +__version__ = '0.12.0' + +from .wrappers import * # noqa: F401, F403 diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py b/venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py deleted file mode 100644 index a536b03e6bbea0e63459d2f7b71f027ec6c7a150..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/pep517/_in_process.py +++ /dev/null @@ -1,280 +0,0 @@ -"""This is invoked in a subprocess to call the build backend hooks. - -It expects: -- Command line args: hook_name, control_dir -- Environment variables: - PEP517_BUILD_BACKEND=entry.point:spec - PEP517_BACKEND_PATH=paths (separated with os.pathsep) -- control_dir/input.json: - - {"kwargs": {...}} - -Results: -- control_dir/output.json - - {"return_val": ...} -""" -from glob import glob -from importlib import import_module -import json -import os -import os.path -from os.path import join as pjoin -import re -import shutil -import sys -import traceback - -# This file is run as a script, and `import compat` is not zip-safe, so we -# include write_json() and read_json() from compat.py. -# -# Handle reading and writing JSON in UTF-8, on Python 3 and 2. - -if sys.version_info[0] >= 3: - # Python 3 - def write_json(obj, path, **kwargs): - with open(path, 'w', encoding='utf-8') as f: - json.dump(obj, f, **kwargs) - - def read_json(path): - with open(path, 'r', encoding='utf-8') as f: - return json.load(f) - -else: - # Python 2 - def write_json(obj, path, **kwargs): - with open(path, 'wb') as f: - json.dump(obj, f, encoding='utf-8', **kwargs) - - def read_json(path): - with open(path, 'rb') as f: - return json.load(f) - - -class BackendUnavailable(Exception): - """Raised if we cannot import the backend""" - def __init__(self, traceback): - self.traceback = traceback - - -class BackendInvalid(Exception): - """Raised if the backend is invalid""" - def __init__(self, message): - self.message = message - - -class HookMissing(Exception): - """Raised if a hook is missing and we are not executing the fallback""" - - -def contained_in(filename, directory): - """Test if a file is located within the given directory.""" - filename = os.path.normcase(os.path.abspath(filename)) - directory = os.path.normcase(os.path.abspath(directory)) - return os.path.commonprefix([filename, directory]) == directory - - -def _build_backend(): - """Find and load the build backend""" - # Add in-tree backend directories to the front of sys.path. - backend_path = os.environ.get('PEP517_BACKEND_PATH') - if backend_path: - extra_pathitems = backend_path.split(os.pathsep) - sys.path[:0] = extra_pathitems - - ep = os.environ['PEP517_BUILD_BACKEND'] - mod_path, _, obj_path = ep.partition(':') - try: - obj = import_module(mod_path) - except ImportError: - raise BackendUnavailable(traceback.format_exc()) - - if backend_path: - if not any( - contained_in(obj.__file__, path) - for path in extra_pathitems - ): - raise BackendInvalid("Backend was not loaded from backend-path") - - if obj_path: - for path_part in obj_path.split('.'): - obj = getattr(obj, path_part) - return obj - - -def get_requires_for_build_wheel(config_settings): - """Invoke the optional get_requires_for_build_wheel hook - - Returns [] if the hook is not defined. - """ - backend = _build_backend() - try: - hook = backend.get_requires_for_build_wheel - except AttributeError: - return [] - else: - return hook(config_settings) - - -def prepare_metadata_for_build_wheel( - metadata_directory, config_settings, _allow_fallback): - """Invoke optional prepare_metadata_for_build_wheel - - Implements a fallback by building a wheel if the hook isn't defined, - unless _allow_fallback is False in which case HookMissing is raised. - """ - backend = _build_backend() - try: - hook = backend.prepare_metadata_for_build_wheel - except AttributeError: - if not _allow_fallback: - raise HookMissing() - return _get_wheel_metadata_from_wheel(backend, metadata_directory, - config_settings) - else: - return hook(metadata_directory, config_settings) - - -WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL' - - -def _dist_info_files(whl_zip): - """Identify the .dist-info folder inside a wheel ZipFile.""" - res = [] - for path in whl_zip.namelist(): - m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path) - if m: - res.append(path) - if res: - return res - raise Exception("No .dist-info folder found in wheel") - - -def _get_wheel_metadata_from_wheel( - backend, metadata_directory, config_settings): - """Build a wheel and extract the metadata from it. - - Fallback for when the build backend does not - define the 'get_wheel_metadata' hook. - """ - from zipfile import ZipFile - whl_basename = backend.build_wheel(metadata_directory, config_settings) - with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'): - pass # Touch marker file - - whl_file = os.path.join(metadata_directory, whl_basename) - with ZipFile(whl_file) as zipf: - dist_info = _dist_info_files(zipf) - zipf.extractall(path=metadata_directory, members=dist_info) - return dist_info[0].split('/')[0] - - -def _find_already_built_wheel(metadata_directory): - """Check for a wheel already built during the get_wheel_metadata hook. - """ - if not metadata_directory: - return None - metadata_parent = os.path.dirname(metadata_directory) - if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): - return None - - whl_files = glob(os.path.join(metadata_parent, '*.whl')) - if not whl_files: - print('Found wheel built marker, but no .whl files') - return None - if len(whl_files) > 1: - print('Found multiple .whl files; unspecified behaviour. ' - 'Will call build_wheel.') - return None - - # Exactly one .whl file - return whl_files[0] - - -def build_wheel(wheel_directory, config_settings, metadata_directory=None): - """Invoke the mandatory build_wheel hook. - - If a wheel was already built in the - prepare_metadata_for_build_wheel fallback, this - will copy it rather than rebuilding the wheel. - """ - prebuilt_whl = _find_already_built_wheel(metadata_directory) - if prebuilt_whl: - shutil.copy2(prebuilt_whl, wheel_directory) - return os.path.basename(prebuilt_whl) - - return _build_backend().build_wheel(wheel_directory, config_settings, - metadata_directory) - - -def get_requires_for_build_sdist(config_settings): - """Invoke the optional get_requires_for_build_wheel hook - - Returns [] if the hook is not defined. - """ - backend = _build_backend() - try: - hook = backend.get_requires_for_build_sdist - except AttributeError: - return [] - else: - return hook(config_settings) - - -class _DummyException(Exception): - """Nothing should ever raise this exception""" - - -class GotUnsupportedOperation(Exception): - """For internal use when backend raises UnsupportedOperation""" - def __init__(self, traceback): - self.traceback = traceback - - -def build_sdist(sdist_directory, config_settings): - """Invoke the mandatory build_sdist hook.""" - backend = _build_backend() - try: - return backend.build_sdist(sdist_directory, config_settings) - except getattr(backend, 'UnsupportedOperation', _DummyException): - raise GotUnsupportedOperation(traceback.format_exc()) - - -HOOK_NAMES = { - 'get_requires_for_build_wheel', - 'prepare_metadata_for_build_wheel', - 'build_wheel', - 'get_requires_for_build_sdist', - 'build_sdist', -} - - -def main(): - if len(sys.argv) < 3: - sys.exit("Needs args: hook_name, control_dir") - hook_name = sys.argv[1] - control_dir = sys.argv[2] - if hook_name not in HOOK_NAMES: - sys.exit("Unknown hook: %s" % hook_name) - hook = globals()[hook_name] - - hook_input = read_json(pjoin(control_dir, 'input.json')) - - json_out = {'unsupported': False, 'return_val': None} - try: - json_out['return_val'] = hook(**hook_input['kwargs']) - except BackendUnavailable as e: - json_out['no_backend'] = True - json_out['traceback'] = e.traceback - except BackendInvalid as e: - json_out['backend_invalid'] = True - json_out['backend_error'] = e.message - except GotUnsupportedOperation as e: - json_out['unsupported'] = True - json_out['traceback'] = e.traceback - except HookMissing: - json_out['hook_missing'] = True - - write_json(json_out, pjoin(control_dir, 'output.json'), indent=2) - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/build.py b/venv/Lib/site-packages/pip/_vendor/pep517/build.py index 264301447e200747cab07691f015d469809461b8..bc463b2ba6dd4db64ccf5c2f749f8a8dfc2d86f1 100644 --- a/venv/Lib/site-packages/pip/_vendor/pep517/build.py +++ b/venv/Lib/site-packages/pip/_vendor/pep517/build.py @@ -1,15 +1,15 @@ """Build a project using PEP 517 hooks. """ import argparse +import io import logging import os -from pip._vendor import toml import shutil from .envbuild import BuildEnvironment from .wrappers import Pep517HookCaller from .dirtools import tempdir, mkdir_p -from .compat import FileNotFoundError +from .compat import FileNotFoundError, toml_load log = logging.getLogger(__name__) @@ -31,8 +31,8 @@ def load_system(source_dir): Load the build system from a source dir (pyproject.toml). """ pyproject = os.path.join(source_dir, 'pyproject.toml') - with open(pyproject) as f: - pyproject_data = toml.load(f) + with io.open(pyproject, 'rb') as f: + pyproject_data = toml_load(f) return pyproject_data['build-system'] @@ -110,6 +110,9 @@ parser.add_argument( def main(args): + log.warning('pep517.build is deprecated. ' + 'Consider switching to https://pypi.org/project/build/') + # determine which dists to build dists = list(filter(None, ( 'sdist' if args.source or not args.binary else None, diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/check.py b/venv/Lib/site-packages/pip/_vendor/pep517/check.py index 13e722a374866b517dc0c84efddcf12ea0dba9b6..bf3c722641e91bf3840e0829752ac3d67e4def76 100644 --- a/venv/Lib/site-packages/pip/_vendor/pep517/check.py +++ b/venv/Lib/site-packages/pip/_vendor/pep517/check.py @@ -1,10 +1,10 @@ """Check a project and backend by attempting to build using PEP 517 hooks. """ import argparse +import io import logging import os from os.path import isfile, join as pjoin -from pip._vendor.toml import TomlDecodeError, load as toml_load import shutil from subprocess import CalledProcessError import sys @@ -13,6 +13,7 @@ from tempfile import mkdtemp import zipfile from .colorlog import enable_colourful_output +from .compat import TOMLDecodeError, toml_load from .envbuild import BuildEnvironment from .wrappers import Pep517HookCaller @@ -141,7 +142,7 @@ def check(source_dir): return False try: - with open(pyproject) as f: + with io.open(pyproject, 'rb') as f: pyproject_data = toml_load(f) # Ensure the mandatory data can be loaded buildsys = pyproject_data['build-system'] @@ -149,7 +150,7 @@ def check(source_dir): backend = buildsys['build-backend'] backend_path = buildsys.get('backend-path') log.info('Loaded pyproject.toml') - except (TomlDecodeError, KeyError): + except (TOMLDecodeError, KeyError): log.error("Invalid pyproject.toml", exc_info=True) return False @@ -167,6 +168,9 @@ def check(source_dir): def main(argv=None): + log.warning('pep517.check is deprecated. ' + 'Consider switching to https://pypi.org/project/build/') + ap = argparse.ArgumentParser() ap.add_argument( 'source_dir', diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/compat.py b/venv/Lib/site-packages/pip/_vendor/pep517/compat.py index 8432acb7324db7c3f26c764a584012849b6c88bb..730ef5ffaa1a57580bd6a4626e223735633ee049 100644 --- a/venv/Lib/site-packages/pip/_vendor/pep517/compat.py +++ b/venv/Lib/site-packages/pip/_vendor/pep517/compat.py @@ -1,4 +1,5 @@ """Python 2/3 compatibility""" +import io import json import sys @@ -32,3 +33,19 @@ try: FileNotFoundError = FileNotFoundError except NameError: FileNotFoundError = IOError + + +if sys.version_info < (3, 6): + from toml import load as _toml_load # noqa: F401 + + def toml_load(f): + w = io.TextIOWrapper(f, encoding="utf8", newline="") + try: + return _toml_load(w) + finally: + w.detach() + + from toml import TomlDecodeError as TOMLDecodeError # noqa: F401 +else: + from pip._vendor.tomli import load as toml_load # noqa: F401 + from pip._vendor.tomli import TOMLDecodeError # noqa: F401 diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py b/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py index 4088dcdb40a81efbedc9bb595c2ce69e05946217..fe8873c64a90d2ae3e44510453191e1ab4b5c84e 100644 --- a/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py +++ b/venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py @@ -1,23 +1,27 @@ """Build wheels/sdists by installing build deps to a temporary environment. """ +import io import os import logging -from pip._vendor import toml import shutil from subprocess import check_call import sys from sysconfig import get_paths from tempfile import mkdtemp +from .compat import toml_load from .wrappers import Pep517HookCaller, LoggerWrapper log = logging.getLogger(__name__) def _load_pyproject(source_dir): - with open(os.path.join(source_dir, 'pyproject.toml')) as f: - pyproject_data = toml.load(f) + with io.open( + os.path.join(source_dir, 'pyproject.toml'), + 'rb', + ) as f: + pyproject_data = toml_load(f) buildsys = pyproject_data['build-system'] return ( buildsys['requires'], diff --git a/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py b/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py index 00a3d1a789ffee002efec8592600ad9afa1b9171..e031ed7087556da5c760d01196046712e4392752 100644 --- a/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py +++ b/venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py @@ -1,24 +1,24 @@ import threading from contextlib import contextmanager import os -from os.path import dirname, abspath, join as pjoin +from os.path import abspath, join as pjoin import shutil from subprocess import check_call, check_output, STDOUT import sys from tempfile import mkdtemp from . import compat +from .in_process import _in_proc_script_path - -try: - import importlib.resources as resources - - def _in_proc_script_path(): - return resources.path(__package__, '_in_process.py') -except ImportError: - @contextmanager - def _in_proc_script_path(): - yield pjoin(dirname(abspath(__file__)), '_in_process.py') +__all__ = [ + 'BackendUnavailable', + 'BackendInvalid', + 'HookMissing', + 'UnsupportedOperation', + 'default_subprocess_runner', + 'quiet_subprocess_runner', + 'Pep517HookCaller', +] @contextmanager @@ -102,19 +102,22 @@ def norm_and_check(source_tree, requested): class Pep517HookCaller(object): """A wrapper around a source directory to be built with a PEP 517 backend. - source_dir : The path to the source directory, containing pyproject.toml. - build_backend : The build backend spec, as per PEP 517, from + :param source_dir: The path to the source directory, containing pyproject.toml. - backend_path : The backend path, as per PEP 517, from pyproject.toml. - runner : A callable that invokes the wrapper subprocess. + :param build_backend: The build backend spec, as per PEP 517, from + pyproject.toml. + :param backend_path: The backend path, as per PEP 517, from pyproject.toml. + :param runner: A callable that invokes the wrapper subprocess. + :param python_executable: The Python executable used to invoke the backend The 'runner', if provided, must expect the following: - cmd : a list of strings representing the command and arguments to - execute, as would be passed to e.g. 'subprocess.check_call'. - cwd : a string representing the working directory that must be - used for the subprocess. Corresponds to the provided source_dir. - extra_environ : a dict mapping environment variable names to values - which must be set for the subprocess execution. + + - cmd: a list of strings representing the command and arguments to + execute, as would be passed to e.g. 'subprocess.check_call'. + - cwd: a string representing the working directory that must be + used for the subprocess. Corresponds to the provided source_dir. + - extra_environ: a dict mapping environment variable names to values + which must be set for the subprocess execution. """ def __init__( self, @@ -122,6 +125,7 @@ class Pep517HookCaller(object): build_backend, backend_path=None, runner=None, + python_executable=None, ): if runner is None: runner = default_subprocess_runner @@ -134,6 +138,9 @@ class Pep517HookCaller(object): ] self.backend_path = backend_path self._subprocess_runner = runner + if not python_executable: + python_executable = sys.executable + self.python_executable = python_executable @contextmanager def subprocess_runner(self, runner): @@ -147,10 +154,15 @@ class Pep517HookCaller(object): finally: self._subprocess_runner = prev + def _supported_features(self): + """Return the list of optional features supported by the backend.""" + return self._call_hook('_supported_features', {}) + def get_requires_for_build_wheel(self, config_settings=None): """Identify packages required for building a wheel - Returns a list of dependency specifications, e.g.: + Returns a list of dependency specifications, e.g.:: + ["wheel >= 0.25", "setuptools"] This does not include requirements specified in pyproject.toml. @@ -164,7 +176,7 @@ class Pep517HookCaller(object): def prepare_metadata_for_build_wheel( self, metadata_directory, config_settings=None, _allow_fallback=True): - """Prepare a *.dist-info folder with metadata for this project. + """Prepare a ``*.dist-info`` folder with metadata for this project. Returns the name of the newly created folder. @@ -199,10 +211,64 @@ class Pep517HookCaller(object): 'metadata_directory': metadata_directory, }) + def get_requires_for_build_editable(self, config_settings=None): + """Identify packages required for building an editable wheel + + Returns a list of dependency specifications, e.g.:: + + ["wheel >= 0.25", "setuptools"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_editable', { + 'config_settings': config_settings + }) + + def prepare_metadata_for_build_editable( + self, metadata_directory, config_settings=None, + _allow_fallback=True): + """Prepare a ``*.dist-info`` folder with metadata for this project. + + Returns the name of the newly created folder. + + If the build backend defines a hook with this name, it will be called + in a subprocess. If not, the backend will be asked to build an editable + wheel, and the dist-info extracted from that (unless _allow_fallback is + False). + """ + return self._call_hook('prepare_metadata_for_build_editable', { + 'metadata_directory': abspath(metadata_directory), + 'config_settings': config_settings, + '_allow_fallback': _allow_fallback, + }) + + def build_editable( + self, wheel_directory, config_settings=None, + metadata_directory=None): + """Build an editable wheel from this project. + + Returns the name of the newly created file. + + In general, this will call the 'build_editable' hook in the backend. + However, if that was previously called by + 'prepare_metadata_for_build_editable', and the same metadata_directory + is used, the previously built wheel will be copied to wheel_directory. + """ + if metadata_directory is not None: + metadata_directory = abspath(metadata_directory) + return self._call_hook('build_editable', { + 'wheel_directory': abspath(wheel_directory), + 'config_settings': config_settings, + 'metadata_directory': metadata_directory, + }) + def get_requires_for_build_sdist(self, config_settings=None): """Identify packages required for building a wheel - Returns a list of dependency specifications, e.g.: + Returns a list of dependency specifications, e.g.:: + ["setuptools >= 26"] This does not include requirements specified in pyproject.toml. @@ -252,8 +318,9 @@ class Pep517HookCaller(object): # Run the hook in a subprocess with _in_proc_script_path() as script: + python = self.python_executable self._subprocess_runner( - [sys.executable, str(script), hook_name, td], + [python, abspath(str(script)), hook_name, td], cwd=self.source_dir, extra_environ=extra_environ ) @@ -270,7 +337,7 @@ class Pep517HookCaller(object): message=data.get('backend_error', '') ) if data.get('hook_missing'): - raise HookMissing(hook_name) + raise HookMissing(data.get('missing_hook_name') or hook_name) return data['return_val'] diff --git a/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py b/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py index a457ff27ef0f766ed6f9bd19858ac1ecb2f109cf..4cd562cf94c6d16f6b2b49b38549db9b914a6178 100644 --- a/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py @@ -77,7 +77,7 @@ except ImportError: importlib_machinery = None from . import py31compat -from pip._vendor import appdirs +from pip._vendor import platformdirs from pip._vendor import packaging __import__('pip._vendor.packaging.version') __import__('pip._vendor.packaging.specifiers') @@ -1310,7 +1310,7 @@ def get_default_cache(): """ return ( os.environ.get('PYTHON_EGG_CACHE') - or appdirs.user_cache_dir(appname='Python-Eggs') + or platformdirs.user_cache_dir(appname='Python-Eggs') ) diff --git a/venv/Lib/site-packages/pip/_vendor/progress/__init__.py b/venv/Lib/site-packages/pip/_vendor/progress/__init__.py index e434c257fefd4eda43f5daf5b8dcf7d4cf9da30b..b434b300ad75a980774c64d5b22edaad4ac83dc1 100644 --- a/venv/Lib/site-packages/pip/_vendor/progress/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/progress/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2012 Giorgos Verigakis +# Copyright (c) 2012 Georgios Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -24,7 +24,7 @@ except ImportError: from time import time as monotonic -__version__ = '1.5' +__version__ = '1.6' HIDE_CURSOR = '\x1b[?25l' SHOW_CURSOR = '\x1b[?25h' @@ -46,14 +46,19 @@ class Infinite(object): for key, val in kwargs.items(): setattr(self, key, val) - self._width = 0 + self._max_width = 0 + self._hidden_cursor = False self.message = message if self.file and self.is_tty(): if self.hide_cursor: print(HIDE_CURSOR, end='', file=self.file) - print(self.message, end='', file=self.file) - self.file.flush() + self._hidden_cursor = True + self.writeln('') + + def __del__(self): + if self._hidden_cursor: + print(SHOW_CURSOR, end='', file=self.file) def __getitem__(self, key): if key.startswith('_'): @@ -85,31 +90,30 @@ class Infinite(object): def start(self): pass - def clearln(self): - if self.file and self.is_tty(): - print('\r\x1b[K', end='', file=self.file) - - def write(self, s): - if self.file and self.is_tty(): - line = self.message + s.ljust(self._width) - print('\r' + line, end='', file=self.file) - self._width = max(self._width, len(s)) - self.file.flush() - def writeln(self, line): if self.file and self.is_tty(): - self.clearln() - print(line, end='', file=self.file) + width = len(line) + if width < self._max_width: + # Add padding to cover previous contents + line += ' ' * (self._max_width - width) + else: + self._max_width = width + print('\r' + line, end='', file=self.file) self.file.flush() def finish(self): if self.file and self.is_tty(): print(file=self.file) - if self.hide_cursor: + if self._hidden_cursor: print(SHOW_CURSOR, end='', file=self.file) + self._hidden_cursor = False def is_tty(self): - return self.file.isatty() if self.check_tty else True + try: + return self.file.isatty() if self.check_tty else True + except AttributeError: + msg = "%s has no attribute 'isatty'. Try setting check_tty=False." % self + raise AttributeError(msg) def next(self, n=1): now = monotonic() @@ -120,10 +124,13 @@ class Infinite(object): self.update() def iter(self, it): + self.iter_value = None with self: for x in it: + self.iter_value = x yield x self.next() + del self.iter_value def __enter__(self): self.start() @@ -152,6 +159,8 @@ class Progress(Infinite): @property def progress(self): + if self.max == 0: + return 0 return min(1, self.index / self.max) @property @@ -171,7 +180,10 @@ class Progress(Infinite): except TypeError: pass + self.iter_value = None with self: for x in it: + self.iter_value = x yield x self.next() + del self.iter_value diff --git a/venv/Lib/site-packages/pip/_vendor/progress/bar.py b/venv/Lib/site-packages/pip/_vendor/progress/bar.py index 8819efda65b9f6bf1315ad15bedc5a3857bf67fc..df4e8b61f89e020ee6a536209fa04f049637c86f 100644 --- a/venv/Lib/site-packages/pip/_vendor/progress/bar.py +++ b/venv/Lib/site-packages/pip/_vendor/progress/bar.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2012 Giorgos Verigakis +# Copyright (c) 2012 Georgios Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,7 @@ from __future__ import unicode_literals import sys from . import Progress +from .colors import color class Bar(Progress): @@ -28,13 +29,14 @@ class Bar(Progress): bar_suffix = '| ' empty_fill = ' ' fill = '#' + color = None def update(self): filled_length = int(self.width * self.progress) empty_length = self.width - filled_length message = self.message % self - bar = self.fill * filled_length + bar = color(self.fill * filled_length, fg=self.color) empty = self.empty_fill * empty_length suffix = self.suffix % self line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, @@ -74,7 +76,7 @@ class IncrementalBar(Bar): nempty = self.width - nfull # Number of empty chars message = self.message % self - bar = self.phases[-1] * nfull + bar = color(self.phases[-1] * nfull, fg=self.color) current = self.phases[phase] if phase > 0 else '' empty = self.empty_fill * max(0, nempty - len(current)) suffix = self.suffix % self diff --git a/venv/Lib/site-packages/pip/_vendor/progress/counter.py b/venv/Lib/site-packages/pip/_vendor/progress/counter.py index d955ca4771bb2cd78a8e9334a6917f9f6cce59b1..d0fbe7ef354a3c503d5429bd97cdb7d04640bb32 100644 --- a/venv/Lib/site-packages/pip/_vendor/progress/counter.py +++ b/venv/Lib/site-packages/pip/_vendor/progress/counter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2012 Giorgos Verigakis +# Copyright (c) 2012 Georgios Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -20,12 +20,16 @@ from . import Infinite, Progress class Counter(Infinite): def update(self): - self.write(str(self.index)) + message = self.message % self + line = ''.join([message, str(self.index)]) + self.writeln(line) class Countdown(Progress): def update(self): - self.write(str(self.remaining)) + message = self.message % self + line = ''.join([message, str(self.remaining)]) + self.writeln(line) class Stack(Progress): @@ -34,7 +38,9 @@ class Stack(Progress): def update(self): nphases = len(self.phases) i = min(nphases - 1, int(self.progress * nphases)) - self.write(self.phases[i]) + message = self.message % self + line = ''.join([message, self.phases[i]]) + self.writeln(line) class Pie(Stack): diff --git a/venv/Lib/site-packages/pip/_vendor/progress/spinner.py b/venv/Lib/site-packages/pip/_vendor/progress/spinner.py index 4e100cabb9b8d7113111d1b09a7b84da2895e2a0..d593a203e088c22b7f9c1aefd893ff0b44157ef0 100644 --- a/venv/Lib/site-packages/pip/_vendor/progress/spinner.py +++ b/venv/Lib/site-packages/pip/_vendor/progress/spinner.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2012 Giorgos Verigakis +# Copyright (c) 2012 Georgios Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -24,7 +24,9 @@ class Spinner(Infinite): def update(self): i = self.index % len(self.phases) - self.write(self.phases[i]) + message = self.message % self + line = ''.join([message, self.phases[i]]) + self.writeln(line) class PieSpinner(Spinner): diff --git a/venv/Lib/site-packages/pip/_vendor/requests/__init__.py b/venv/Lib/site-packages/pip/_vendor/requests/__init__.py index e47bcb201492f0786fde572b75948488a03456a2..4f80e28fc7102230107ec101abcbd68192d47725 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/__init__.py @@ -41,12 +41,17 @@ is at . """ from pip._vendor import urllib3 -from pip._vendor import chardet import warnings from .exceptions import RequestsDependencyWarning +charset_normalizer_version = None -def check_compatibility(urllib3_version, chardet_version): +try: + from pip._vendor.chardet import __version__ as chardet_version +except ImportError: + chardet_version = None + +def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): urllib3_version = urllib3_version.split('.') assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git. @@ -57,19 +62,24 @@ def check_compatibility(urllib3_version, chardet_version): # Check urllib3 for compatibility. major, minor, patch = urllib3_version # noqa: F811 major, minor, patch = int(major), int(minor), int(patch) - # urllib3 >= 1.21.1, <= 1.25 + # urllib3 >= 1.21.1, <= 1.26 assert major == 1 assert minor >= 21 - assert minor <= 25 - - # Check chardet for compatibility. - major, minor, patch = chardet_version.split('.')[:3] - major, minor, patch = int(major), int(minor), int(patch) - # chardet >= 3.0.2, < 3.1.0 - assert major == 3 - assert minor < 1 - assert patch >= 2 - + assert minor <= 26 + + # Check charset_normalizer for compatibility. + if chardet_version: + major, minor, patch = chardet_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet_version >= 3.0.2, < 5.0.0 + assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0) + elif charset_normalizer_version: + major, minor, patch = charset_normalizer_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # charset_normalizer >= 2.0.0 < 3.0.0 + assert (2, 0, 0) <= (major, minor, patch) < (3, 0, 0) + else: + raise Exception("You need either charset_normalizer or chardet installed") def _check_cryptography(cryptography_version): # cryptography < 1.3.4 @@ -84,24 +94,35 @@ def _check_cryptography(cryptography_version): # Check imported dependencies for compatibility. try: - check_compatibility(urllib3.__version__, chardet.__version__) + check_compatibility(urllib3.__version__, chardet_version, charset_normalizer_version) except (AssertionError, ValueError): - warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported " - "version!".format(urllib3.__version__, chardet.__version__), + warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported " + "version!".format(urllib3.__version__, chardet_version, charset_normalizer_version), RequestsDependencyWarning) -# Attempt to enable urllib3's SNI support, if possible -from pip._internal.utils.compat import WINDOWS -if not WINDOWS: +# Attempt to enable urllib3's fallback for SNI support +# if the standard library doesn't support SNI or the +# 'ssl' library isn't available. +try: + # Note: This logic prevents upgrading cryptography on Windows, if imported + # as part of pip. + from pip._internal.utils.compat import WINDOWS + if not WINDOWS: + raise ImportError("pip internals: don't import cryptography on Windows") try: + import ssl + except ImportError: + ssl = None + + if not getattr(ssl, "HAS_SNI", False): from pip._vendor.urllib3.contrib import pyopenssl pyopenssl.inject_into_urllib3() # Check cryptography version from cryptography import __version__ as cryptography_version _check_cryptography(cryptography_version) - except ImportError: - pass +except ImportError: + pass # urllib3's DependencyWarnings should be silenced. from pip._vendor.urllib3.exceptions import DependencyWarning diff --git a/venv/Lib/site-packages/pip/_vendor/requests/__version__.py b/venv/Lib/site-packages/pip/_vendor/requests/__version__.py index b9e7df4881aa5813ce2ca0b10f43d313156368d7..0d7cde1dfe6553960118f2581a265fa1454c3c12 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/__version__.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/__version__.py @@ -5,8 +5,8 @@ __title__ = 'requests' __description__ = 'Python HTTP for Humans.' __url__ = 'https://requests.readthedocs.io' -__version__ = '2.23.0' -__build__ = 0x022300 +__version__ = '2.26.0' +__build__ = 0x022600 __author__ = 'Kenneth Reitz' __author_email__ = 'me@kennethreitz.org' __license__ = 'Apache 2.0' diff --git a/venv/Lib/site-packages/pip/_vendor/requests/api.py b/venv/Lib/site-packages/pip/_vendor/requests/api.py index e978e2031186f656bd96fc296773ec42572053d3..4cba90eefe8ef9188a821acf880f51a7058e342b 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/api.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/api.py @@ -72,7 +72,6 @@ def get(url, params=None, **kwargs): :rtype: requests.Response """ - kwargs.setdefault('allow_redirects', True) return request('get', url, params=params, **kwargs) @@ -85,7 +84,6 @@ def options(url, **kwargs): :rtype: requests.Response """ - kwargs.setdefault('allow_redirects', True) return request('options', url, **kwargs) diff --git a/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py b/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py index a91e1fd114ef372efa2d1f02d99f114db913b7e6..9f0ad778b51c9e8f4188797ef5c578ec418f9302 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/exceptions.py @@ -25,6 +25,10 @@ class RequestException(IOError): super(RequestException, self).__init__(*args, **kwargs) +class InvalidJSONError(RequestException): + """A JSON error occurred.""" + + class HTTPError(RequestException): """An HTTP error occurred.""" @@ -94,11 +98,11 @@ class ChunkedEncodingError(RequestException): class ContentDecodingError(RequestException, BaseHTTPError): - """Failed to decode response content""" + """Failed to decode response content.""" class StreamConsumedError(RequestException, TypeError): - """The content for this response was already consumed""" + """The content for this response was already consumed.""" class RetryError(RequestException): @@ -106,21 +110,18 @@ class RetryError(RequestException): class UnrewindableBodyError(RequestException): - """Requests encountered an error when trying to rewind a body""" + """Requests encountered an error when trying to rewind a body.""" # Warnings class RequestsWarning(Warning): """Base warning for Requests.""" - pass class FileModeWarning(RequestsWarning, DeprecationWarning): """A file was opened in text mode, but Requests determined its binary length.""" - pass class RequestsDependencyWarning(RequestsWarning): """An imported dependency doesn't match the expected version range.""" - pass diff --git a/venv/Lib/site-packages/pip/_vendor/requests/help.py b/venv/Lib/site-packages/pip/_vendor/requests/help.py index 3c3072ba1419b4e0f183c5de1d0c39d166f2472c..745f0d7b346a1aac57197ab99d3d7d8b207b890a 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/help.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/help.py @@ -8,10 +8,16 @@ import ssl from pip._vendor import idna from pip._vendor import urllib3 -from pip._vendor import chardet from . import __version__ as requests_version +charset_normalizer = None + +try: + from pip._vendor import chardet +except ImportError: + chardet = None + try: from pip._vendor.urllib3.contrib import pyopenssl except ImportError: @@ -71,7 +77,12 @@ def info(): implementation_info = _implementation() urllib3_info = {'version': urllib3.__version__} - chardet_info = {'version': chardet.__version__} + charset_normalizer_info = {'version': None} + chardet_info = {'version': None} + if charset_normalizer: + charset_normalizer_info = {'version': charset_normalizer.__version__} + if chardet: + chardet_info = {'version': chardet.__version__} pyopenssl_info = { 'version': None, @@ -99,9 +110,11 @@ def info(): 'implementation': implementation_info, 'system_ssl': system_ssl_info, 'using_pyopenssl': pyopenssl is not None, + 'using_charset_normalizer': chardet is None, 'pyOpenSSL': pyopenssl_info, 'urllib3': urllib3_info, 'chardet': chardet_info, + 'charset_normalizer': charset_normalizer_info, 'cryptography': cryptography_info, 'idna': idna_info, 'requests': { diff --git a/venv/Lib/site-packages/pip/_vendor/requests/models.py b/venv/Lib/site-packages/pip/_vendor/requests/models.py index 8a3085d37836c031de745fd0dfcd4bd711912190..c10c6011b8358ed6de7b8e904abb44f920fe984b 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/models.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/models.py @@ -29,7 +29,7 @@ from .auth import HTTPBasicAuth from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar from .exceptions import ( HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, - ContentDecodingError, ConnectionError, StreamConsumedError) + ContentDecodingError, ConnectionError, StreamConsumedError, InvalidJSONError) from ._internal_utils import to_native_string, unicode_is_ascii from .utils import ( guess_filename, get_auth_from_url, requote_uri, @@ -273,7 +273,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): """The fully mutable :class:`PreparedRequest ` object, containing the exact bytes that will be sent to the server. - Generated from either a :class:`Request ` object or manually. + Instances are generated from a :class:`Request ` object, and + should not be instantiated manually; doing so may produce undesirable + effects. Usage:: @@ -464,7 +466,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): # urllib3 requires a bytes-like body. Python 2's json.dumps # provides this natively, but Python 3 gives a Unicode string. content_type = 'application/json' - body = complexjson.dumps(json) + + try: + body = complexjson.dumps(json, allow_nan=False) + except ValueError as ve: + raise InvalidJSONError(ve, request=self) + if not isinstance(body, bytes): body = body.encode('utf-8') @@ -473,12 +480,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): not isinstance(data, (basestring, list, tuple, Mapping)) ]) - try: - length = super_len(data) - except (TypeError, AttributeError, UnsupportedOperation): - length = None - if is_stream: + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + body = data if getattr(body, 'tell', None) is not None: @@ -724,7 +731,7 @@ class Response(object): @property def apparent_encoding(self): - """The apparent encoding, provided by the chardet library.""" + """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" return chardet.detect(self.content)['encoding'] def iter_content(self, chunk_size=1, decode_unicode=False): @@ -838,7 +845,7 @@ class Response(object): """Content of the response, in unicode. If Response.encoding is None, encoding will be guessed using - ``chardet``. + ``charset_normalizer`` or ``chardet``. The encoding of the response content is determined based solely on HTTP headers, following RFC 2616 to the letter. If you can take advantage of @@ -875,13 +882,18 @@ class Response(object): r"""Returns the json-encoded content of a response, if any. :param \*\*kwargs: Optional arguments that ``json.loads`` takes. - :raises ValueError: If the response body does not contain valid json. + :raises simplejson.JSONDecodeError: If the response body does not + contain valid json and simplejson is installed. + :raises json.JSONDecodeError: If the response body does not contain + valid json and simplejson is not installed on Python 3. + :raises ValueError: If the response body does not contain valid + json and simplejson is not installed on Python 2. """ if not self.encoding and self.content and len(self.content) > 3: # No encoding set. JSON RFC 4627 section 3 states we should expect # UTF-8, -16 or -32. Detect which one to use; If the detection or - # decoding fails, fall back to `self.text` (using chardet to make + # decoding fails, fall back to `self.text` (using charset_normalizer to make # a best guess). encoding = guess_json_utf(self.content) if encoding is not None: @@ -916,7 +928,7 @@ class Response(object): return l def raise_for_status(self): - """Raises stored :class:`HTTPError`, if one occurred.""" + """Raises :class:`HTTPError`, if one occurred.""" http_error_msg = '' if isinstance(self.reason, bytes): diff --git a/venv/Lib/site-packages/pip/_vendor/requests/sessions.py b/venv/Lib/site-packages/pip/_vendor/requests/sessions.py index 2845880bf41eb35abcd026dbbeb9e3a63f143751..ae4bcc8e795eba3ef15d40124b47301480f10dc8 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/sessions.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/sessions.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """ -requests.session -~~~~~~~~~~~~~~~~ +requests.sessions +~~~~~~~~~~~~~~~~~ This module provides a Session object to manage and persist settings across requests (cookies, auth, proxies). @@ -387,6 +387,13 @@ class Session(SessionRedirectMixin): self.stream = False #: SSL Verification default. + #: Defaults to `True`, requiring requests to verify the TLS certificate at the + #: remote end. + #: If verify is set to `False`, requests will accept any TLS certificate + #: presented by the server, and will ignore hostname mismatches and/or + #: expired certificates, which will make your application vulnerable to + #: man-in-the-middle (MitM) attacks. + #: Only set this to `False` for testing. self.verify = True #: SSL client certificate default, if String, path to ssl client @@ -495,7 +502,12 @@ class Session(SessionRedirectMixin): content. Defaults to ``False``. :param verify: (optional) Either a boolean, in which case it controls whether we verify the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use. Defaults to ``True``. + to a CA bundle to use. Defaults to ``True``. When set to + ``False``, requests will accept any TLS certificate presented by + the server, and will ignore hostname mismatches and/or expired + certificates, which will make your application vulnerable to + man-in-the-middle (MitM) attacks. Setting verify to ``False`` + may be useful during local development or testing. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. :rtype: requests.Response @@ -621,7 +633,7 @@ class Session(SessionRedirectMixin): kwargs.setdefault('stream', self.stream) kwargs.setdefault('verify', self.verify) kwargs.setdefault('cert', self.cert) - kwargs.setdefault('proxies', self.proxies) + kwargs.setdefault('proxies', self.rebuild_proxies(request, self.proxies)) # It's possible that users might accidentally send a Request object. # Guard against that specific failure case. @@ -658,11 +670,13 @@ class Session(SessionRedirectMixin): extract_cookies_to_jar(self.cookies, request, r.raw) - # Redirect resolving generator. - gen = self.resolve_redirects(r, request, **kwargs) - # Resolve redirects if allowed. - history = [resp for resp in gen] if allow_redirects else [] + if allow_redirects: + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + history = [resp for resp in gen] + else: + history = [] # Shuffle things around if there's history. if history: diff --git a/venv/Lib/site-packages/pip/_vendor/requests/utils.py b/venv/Lib/site-packages/pip/_vendor/requests/utils.py index c1700d7fe85318b8d2b608b16bcfc350c7c4f317..fcb99669022b04ea81a63c3dd6ac41dab208cf10 100644 --- a/venv/Lib/site-packages/pip/_vendor/requests/utils.py +++ b/venv/Lib/site-packages/pip/_vendor/requests/utils.py @@ -20,6 +20,7 @@ import tempfile import warnings import zipfile from collections import OrderedDict +from pip._vendor.urllib3.util import make_headers from .__version__ import __version__ from . import certs @@ -41,6 +42,11 @@ DEFAULT_CA_BUNDLE_PATH = certs.where() DEFAULT_PORTS = {'http': 80, 'https': 443} +# Ensure that ', ' is used to preserve previous delimiter behavior. +DEFAULT_ACCEPT_ENCODING = ", ".join( + re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"]) +) + if sys.platform == 'win32': # provide a proxy_bypass version on Windows without DNS lookups @@ -169,14 +175,20 @@ def super_len(o): def get_netrc_auth(url, raise_errors=False): """Returns the Requests tuple auth for a given url from netrc.""" + netrc_file = os.environ.get('NETRC') + if netrc_file is not None: + netrc_locations = (netrc_file,) + else: + netrc_locations = ('~/{}'.format(f) for f in NETRC_FILES) + try: from netrc import netrc, NetrcParseError netrc_path = None - for f in NETRC_FILES: + for f in netrc_locations: try: - loc = os.path.expanduser('~/{}'.format(f)) + loc = os.path.expanduser(f) except KeyError: # os.path.expanduser can fail when $HOME is undefined and # getpwuid fails. See https://bugs.python.org/issue20164 & @@ -212,7 +224,7 @@ def get_netrc_auth(url, raise_errors=False): if raise_errors: raise - # AppEngine hackiness. + # App Engine hackiness. except (ImportError, AttributeError): pass @@ -250,13 +262,28 @@ def extract_zipped_paths(path): # we have a valid zip archive and a valid member of that archive tmp = tempfile.gettempdir() - extracted_path = os.path.join(tmp, *member.split('/')) + extracted_path = os.path.join(tmp, member.split('/')[-1]) if not os.path.exists(extracted_path): - extracted_path = zip_file.extract(member, path=tmp) - + # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition + with atomic_open(extracted_path) as file_handler: + file_handler.write(zip_file.read(member)) return extracted_path +@contextlib.contextmanager +def atomic_open(filename): + """Write a file to the disk in an atomic fashion""" + replacer = os.rename if sys.version_info[0] == 2 else os.replace + tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename)) + try: + with os.fdopen(tmp_descriptor, 'wb') as tmp_handler: + yield tmp_handler + replacer(tmp_name, filename) + except BaseException: + os.remove(tmp_name) + raise + + def from_key_val_list(value): """Take an object and test to see if it can be represented as a dictionary. Unless it can not be represented as such, return an @@ -497,6 +524,10 @@ def get_encoding_from_headers(headers): if 'text' in content_type: return 'ISO-8859-1' + if 'application/json' in content_type: + # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset + return 'utf-8' + def stream_decode_response_unicode(iterator, r): """Stream decodes a iterator.""" @@ -810,7 +841,7 @@ def default_headers(): """ return CaseInsensitiveDict({ 'User-Agent': default_user_agent(), - 'Accept-Encoding': ', '.join(('gzip', 'deflate')), + 'Accept-Encoding': DEFAULT_ACCEPT_ENCODING, 'Accept': '*/*', 'Connection': 'keep-alive', }) diff --git a/venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py b/venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py index aaba5b3a1202210701764982b669aa5f9313a7b3..af18ddc9712d972d4098357a8128b3281c75f8b2 100644 --- a/venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py @@ -11,7 +11,7 @@ __all__ = [ "ResolutionTooDeep", ] -__version__ = "0.3.0" +__version__ = "0.8.0" from .providers import AbstractProvider, AbstractResolver diff --git a/venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py b/venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py index db1682195e00e841fd21b08533b7c35b60b31833..7d0a9c22a4656951910a9fbb70af59a0706cadde 100644 --- a/venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py +++ b/venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py @@ -1,34 +1,46 @@ class AbstractProvider(object): - """Delegate class to provide requirement interface for the resolver. - """ + """Delegate class to provide requirement interface for the resolver.""" - def identify(self, dependency): - """Given a dependency, return an identifier for it. + def identify(self, requirement_or_candidate): + """Given a requirement, return an identifier for it. - This is used in many places to identify the dependency, e.g. whether - two requirements should have their specifier parts merged, whether - two specifications would conflict with each other (because they the - same name but different versions). + This is used to identify a requirement, e.g. whether two requirements + should have their specifier parts merged. """ raise NotImplementedError - def get_preference(self, resolution, candidates, information): - """Produce a sort key for given specification based on preference. + def get_preference( + self, + identifier, + resolutions, + candidates, + information, + backtrack_causes, + ): + """Produce a sort key for given requirement based on preference. The preference is defined as "I think this requirement should be resolved first". The lower the return value is, the more preferred this group of arguments is. - :param resolution: Currently pinned candidate, or `None`. - :param candidates: A list of possible candidates. - :param information: A list of requirement information. - - Each information instance is a named tuple with two entries: - - * `requirement` specifies a requirement contributing to the current - candidate list - * `parent` specifies the candidate that provids (dependend on) the - requirement, or `None` to indicate a root requirement. + :param identifier: An identifier as returned by ``identify()``. This + identifies the dependency matches of which should be returned. + :param resolutions: Mapping of candidates currently pinned by the + resolver. Each key is an identifier, and the value a candidate. + The candidate may conflict with requirements from ``information``. + :param candidates: Mapping of each dependency's possible candidates. + Each value is an iterator of candidates. + :param information: Mapping of requirement information of each package. + Each value is an iterator of *requirement information*. + :param backtrack_causes: Sequence of requirement information that were + the requirements that caused the resolver to most recently backtrack. + + A *requirement information* instance is a named tuple with two members: + + * ``requirement`` specifies a requirement contributing to the current + list of candidates. + * ``parent`` specifies the candidate that provides (dependend on) the + requirement, or ``None`` to indicate a root requirement. The preference could depend on a various of issues, including (not necessarily in this order): @@ -41,31 +53,49 @@ class AbstractProvider(object): * Are there any known conflicts for this requirement? We should probably work on those with the most known conflicts. - A sortable value should be returned (this will be used as the `key` + A sortable value should be returned (this will be used as the ``key`` parameter of the built-in sorting function). The smaller the value is, - the more preferred this specification is (i.e. the sorting function - is called with `reverse=False`). + the more preferred this requirement is (i.e. the sorting function + is called with ``reverse=False``). """ raise NotImplementedError - def find_matches(self, requirement): - """Find all possible candidates that satisfy a requirement. + def find_matches(self, identifier, requirements, incompatibilities): + """Find all possible candidates that satisfy given constraints. - This should try to get candidates based on the requirement's type. + :param identifier: An identifier as returned by ``identify()``. This + identifies the dependency matches of which should be returned. + :param requirements: A mapping of requirements that all returned + candidates must satisfy. Each key is an identifier, and the value + an iterator of requirements for that dependency. + :param incompatibilities: A mapping of known incompatibilities of + each dependency. Each key is an identifier, and the value an + iterator of incompatibilities known to the resolver. All + incompatibilities *must* be excluded from the return value. + + This should try to get candidates based on the requirements' types. For VCS, local, and archive requirements, the one-and-only match is returned, and for a "named" requirement, the index(es) should be consulted to find concrete candidates for this requirement. - The returned candidates should be sorted by reversed preference, e.g. - the most preferred should be LAST. This is done so list-popping can be - as efficient as possible. + The return value should produce candidates ordered by preference; the + most preferred candidate should come first. The return type may be one + of the following: + + * A callable that returns an iterator that yields candidates. + * An collection of candidates. + * An iterable of candidates. This will be consumed immediately into a + list of candidates. """ raise NotImplementedError def is_satisfied_by(self, requirement, candidate): """Whether the given requirement can be satisfied by a candidate. - A boolean should be returned to indicate whether `candidate` is a + The candidate is guarenteed to have been generated from the + requirement. + + A boolean should be returned to indicate whether ``candidate`` is a viable solution to the requirement. """ raise NotImplementedError @@ -80,8 +110,7 @@ class AbstractProvider(object): class AbstractResolver(object): - """The thing that performs the actual resolution work. - """ + """The thing that performs the actual resolution work.""" base_exception = Exception @@ -92,30 +121,13 @@ class AbstractResolver(object): def resolve(self, requirements, **kwargs): """Take a collection of constraints, spit out the resolution result. - Parameters - ---------- - requirements : Collection - A collection of constraints - kwargs : optional - Additional keyword arguments that subclasses may accept. - - Raises - ------ - self.base_exception - Any raised exception is guaranteed to be a subclass of - self.base_exception. The string representation of an exception - should be human readable and provide context for why it occurred. - - Returns - ------- - retval : object - A representation of the final resolution state. It can be any object - with a `mapping` attribute that is a Mapping. Other attributes can - be used to provide resolver-specific information. - - The `mapping` attribute MUST be key-value pair is an identifier of a - requirement (as returned by the provider's `identify` method) mapped - to the resolved candidate (chosen from the return value of the - provider's `find_matches` method). + This returns a representation of the final resolution state, with one + guarenteed attribute ``mapping`` that contains resolved candidates as + values. The keys are their respective identifiers. + + :param requirements: A collection of constraints. + :param kwargs: Additional keyword arguments that subclasses may accept. + + :raises: ``self.base_exception`` or its subclass. """ raise NotImplementedError diff --git a/venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py b/venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py index c7e9e88b832b9e446ece898a5f8578ae6f1a8ad2..563489e133b01e8162114e077ba9ea026dcad55b 100644 --- a/venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py +++ b/venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py @@ -1,10 +1,8 @@ class BaseReporter(object): - """Delegate class to provider progress reporting for the resolver. - """ + """Delegate class to provider progress reporting for the resolver.""" def starting(self): - """Called before the resolution actually starts. - """ + """Called before the resolution actually starts.""" def starting_round(self, index): """Called before each round of resolution starts. @@ -20,17 +18,20 @@ class BaseReporter(object): """ def ending(self, state): - """Called before the resolution ends successfully. - """ + """Called before the resolution ends successfully.""" + + def adding_requirement(self, requirement, parent): + """Called when adding a new requirement into the resolve criteria. - def adding_requirement(self, requirement): - """Called when the resolver adds a new requirement into the resolve criteria. + :param requirement: The additional requirement to be applied to filter + the available candidaites. + :param parent: The candidate that requires ``requirement`` as a + dependency, or None if ``requirement`` is one of the root + requirements passed in from ``Resolver.resolve()``. """ def backtracking(self, candidate): - """Called when the resolver rejects a candidate during backtracking. - """ + """Called when rejecting a candidate during backtracking.""" def pinning(self, candidate): - """Called when adding a candidate to the potential solution. - """ + """Called when adding a candidate to the potential solution.""" diff --git a/venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py b/venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py index b51d337d231c0cedbc9eb524255f36de513d8f1a..35e00fa90a18a84f1f7fc3f54b4f6455a41c2ec8 100644 --- a/venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py +++ b/venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py @@ -1,7 +1,8 @@ import collections +import operator from .providers import AbstractResolver -from .structs import DirectedGraph +from .structs import DirectedGraph, IteratorMapping, build_iter_view RequirementInformation = collections.namedtuple( @@ -68,24 +69,10 @@ class Criterion(object): def __repr__(self): requirements = ", ".join( - "{!r} from {!r}".format(req, parent) + "({!r}, via={!r})".format(req, parent) for req, parent in self.information ) - return "".format(requirements) - - @classmethod - def from_requirement(cls, provider, requirement, parent): - """Build an instance from a requirement. - """ - candidates = provider.find_matches(requirement) - criterion = cls( - candidates=candidates, - information=[RequirementInformation(requirement, parent)], - incompatibilities=[], - ) - if not candidates: - raise RequirementsConflicted(criterion) - return criterion + return "Criterion({})".format(requirements) def iter_requirement(self): return (i.requirement for i in self.information) @@ -93,34 +80,6 @@ class Criterion(object): def iter_parent(self): return (i.parent for i in self.information) - def merged_with(self, provider, requirement, parent): - """Build a new instance from this and a new requirement. - """ - infos = list(self.information) - infos.append(RequirementInformation(requirement, parent)) - candidates = [ - c - for c in self.candidates - if provider.is_satisfied_by(requirement, c) - ] - criterion = type(self)(candidates, infos, list(self.incompatibilities)) - if not candidates: - raise RequirementsConflicted(criterion) - return criterion - - def excluded_of(self, candidate): - """Build a new instance from this, but excluding specified candidate. - - Returns the new instance, or None if we still have no valid candidates. - """ - incompats = list(self.incompatibilities) - incompats.append(candidate) - candidates = [c for c in self.candidates if c != candidate] - if not candidates: - return None - criterion = type(self)(candidates, list(self.information), incompats) - return criterion - class ResolutionError(ResolverException): pass @@ -140,7 +99,7 @@ class ResolutionTooDeep(ResolutionError): # Resolution state in a round. -State = collections.namedtuple("State", "mapping criteria") +State = collections.namedtuple("State", "mapping criteria backtrack_causes") class Resolution(object): @@ -168,35 +127,66 @@ class Resolution(object): This new state will be used to hold resolution results of the next coming round. """ - try: - base = self._states[-1] - except IndexError: - state = State(mapping=collections.OrderedDict(), criteria={}) - else: - state = State( - mapping=base.mapping.copy(), criteria=base.criteria.copy(), - ) + base = self._states[-1] + state = State( + mapping=base.mapping.copy(), + criteria=base.criteria.copy(), + backtrack_causes=base.backtrack_causes[:], + ) self._states.append(state) - def _merge_into_criterion(self, requirement, parent): - self._r.adding_requirement(requirement) - name = self._p.identify(requirement) - try: - crit = self.state.criteria[name] - except KeyError: - crit = Criterion.from_requirement(self._p, requirement, parent) + def _add_to_criteria(self, criteria, requirement, parent): + self._r.adding_requirement(requirement=requirement, parent=parent) + + identifier = self._p.identify(requirement_or_candidate=requirement) + criterion = criteria.get(identifier) + if criterion: + incompatibilities = list(criterion.incompatibilities) else: - crit = crit.merged_with(self._p, requirement, parent) - return name, crit + incompatibilities = [] + + matches = self._p.find_matches( + identifier=identifier, + requirements=IteratorMapping( + criteria, + operator.methodcaller("iter_requirement"), + {identifier: [requirement]}, + ), + incompatibilities=IteratorMapping( + criteria, + operator.attrgetter("incompatibilities"), + {identifier: incompatibilities}, + ), + ) - def _get_criterion_item_preference(self, item): - name, criterion = item - try: - pinned = self.state.mapping[name] - except KeyError: - pinned = None + if criterion: + information = list(criterion.information) + information.append(RequirementInformation(requirement, parent)) + else: + information = [RequirementInformation(requirement, parent)] + + criterion = Criterion( + candidates=build_iter_view(matches), + information=information, + incompatibilities=incompatibilities, + ) + if not criterion.candidates: + raise RequirementsConflicted(criterion) + criteria[identifier] = criterion + + def _get_preference(self, name): return self._p.get_preference( - pinned, criterion.candidates, criterion.information, + identifier=name, + resolutions=self.state.mapping, + candidates=IteratorMapping( + self.state.criteria, + operator.attrgetter("candidates"), + ), + information=IteratorMapping( + self.state.criteria, + operator.attrgetter("information"), + ), + backtrack_causes=self.state.backtrack_causes, ) def _is_current_pin_satisfying(self, name, criterion): @@ -205,40 +195,46 @@ class Resolution(object): except KeyError: return False return all( - self._p.is_satisfied_by(r, current_pin) + self._p.is_satisfied_by(requirement=r, candidate=current_pin) for r in criterion.iter_requirement() ) - def _get_criteria_to_update(self, candidate): - criteria = {} - for r in self._p.get_dependencies(candidate): - name, crit = self._merge_into_criterion(r, parent=candidate) - criteria[name] = crit + def _get_updated_criteria(self, candidate): + criteria = self.state.criteria.copy() + for requirement in self._p.get_dependencies(candidate=candidate): + self._add_to_criteria(criteria, requirement, parent=candidate) return criteria - def _attempt_to_pin_criterion(self, name, criterion): + def _attempt_to_pin_criterion(self, name): + criterion = self.state.criteria[name] + causes = [] - for candidate in reversed(criterion.candidates): + for candidate in criterion.candidates: try: - criteria = self._get_criteria_to_update(candidate) + criteria = self._get_updated_criteria(candidate) except RequirementsConflicted as e: causes.append(e.criterion) continue - # Put newly-pinned candidate at the end. This is essential because - # backtracking looks at this mapping to get the last pin. - self._r.pinning(candidate) - self.state.mapping.pop(name, None) - self.state.mapping[name] = candidate - self.state.criteria.update(criteria) - # Check the newly-pinned candidate actually works. This should # always pass under normal circumstances, but in the case of a # faulty provider, we will raise an error to notify the implementer # to fix find_matches() and/or is_satisfied_by(). - if not self._is_current_pin_satisfying(name, criterion): + satisfied = all( + self._p.is_satisfied_by(requirement=r, candidate=candidate) + for r in criterion.iter_requirement() + ) + if not satisfied: raise InconsistentCandidate(candidate, criterion) + self._r.pinning(candidate=candidate) + self.state.criteria.update(criteria) + + # Put newly-pinned candidate at the end. This is essential because + # backtracking looks at this mapping to get the last pin. + self.state.mapping.pop(name, None) + self.state.mapping[name] = candidate + return [] # All candidates tried, nothing works. This criterion is a dead @@ -246,79 +242,153 @@ class Resolution(object): return causes def _backtrack(self): - # We need at least 3 states here: - # (a) One known not working, to drop. - # (b) One to backtrack to. - # (c) One to restore state (b) to its state prior to candidate-pinning, - # so we can pin another one instead. + """Perform backtracking. + + When we enter here, the stack is like this:: + + [ state Z ] + [ state Y ] + [ state X ] + .... earlier states are irrelevant. + + 1. No pins worked for Z, so it does not have a pin. + 2. We want to reset state Y to unpinned, and pin another candidate. + 3. State X holds what state Y was before the pin, but does not + have the incompatibility information gathered in state Y. + + Each iteration of the loop will: + + 1. Discard Z. + 2. Discard Y but remember its incompatibility information gathered + previously, and the failure we're dealing with right now. + 3. Push a new state Y' based on X, and apply the incompatibility + information from Y to Y'. + 4a. If this causes Y' to conflict, we need to backtrack again. Make Y' + the new Z and go back to step 2. + 4b. If the incompatibilities apply cleanly, end backtracking. + """ while len(self._states) >= 3: + # Remove the state that triggered backtracking. del self._states[-1] - # Retract the last candidate pin, and create a new (b). - name, candidate = self._states.pop().mapping.popitem() - self._r.backtracking(candidate) + # Retrieve the last candidate pin and known incompatibilities. + broken_state = self._states.pop() + name, candidate = broken_state.mapping.popitem() + incompatibilities_from_broken = [ + (k, list(v.incompatibilities)) + for k, v in broken_state.criteria.items() + ] + + # Also mark the newly known incompatibility. + incompatibilities_from_broken.append((name, [candidate])) + + self._r.backtracking(candidate=candidate) + + # Create a new state from the last known-to-work one, and apply + # the previously gathered incompatibility information. + def _patch_criteria(): + for k, incompatibilities in incompatibilities_from_broken: + if not incompatibilities: + continue + try: + criterion = self.state.criteria[k] + except KeyError: + continue + matches = self._p.find_matches( + identifier=k, + requirements=IteratorMapping( + self.state.criteria, + operator.methodcaller("iter_requirement"), + ), + incompatibilities=IteratorMapping( + self.state.criteria, + operator.attrgetter("incompatibilities"), + {k: incompatibilities}, + ), + ) + candidates = build_iter_view(matches) + if not candidates: + return False + incompatibilities.extend(criterion.incompatibilities) + self.state.criteria[k] = Criterion( + candidates=candidates, + information=list(criterion.information), + incompatibilities=incompatibilities, + ) + return True + self._push_new_state() + success = _patch_criteria() - # Mark the retracted candidate as incompatible. - criterion = self.state.criteria[name].excluded_of(candidate) - if criterion is None: - # This state still does not work. Try the still previous state. - continue - self.state.criteria[name] = criterion + # It works! Let's work on this new state. + if success: + return True - return True + # State does not work after applying known incompatibilities. + # Try the still previous state. + # No way to backtrack anymore. return False def resolve(self, requirements, max_rounds): if self._states: raise RuntimeError("already resolved") - self._push_new_state() + self._r.starting() + + # Initialize the root state. + self._states = [ + State( + mapping=collections.OrderedDict(), + criteria={}, + backtrack_causes=[], + ) + ] for r in requirements: try: - name, crit = self._merge_into_criterion(r, parent=None) + self._add_to_criteria(self.state.criteria, r, parent=None) except RequirementsConflicted as e: raise ResolutionImpossible(e.criterion.information) - self.state.criteria[name] = crit - self._r.starting() + # The root state is saved as a sentinel so the first ever pin can have + # something to backtrack to if it fails. The root state is basically + # pinning the virtual "root" package in the graph. + self._push_new_state() for round_index in range(max_rounds): - self._r.starting_round(round_index) - - self._push_new_state() - curr = self.state + self._r.starting_round(index=round_index) - unsatisfied_criterion_items = [ - item - for item in self.state.criteria.items() - if not self._is_current_pin_satisfying(*item) + unsatisfied_names = [ + key + for key, criterion in self.state.criteria.items() + if not self._is_current_pin_satisfying(key, criterion) ] # All criteria are accounted for. Nothing more to pin, we are done! - if not unsatisfied_criterion_items: - del self._states[-1] - self._r.ending(curr) + if not unsatisfied_names: + self._r.ending(state=self.state) return self.state # Choose the most preferred unpinned criterion to try. - name, criterion = min( - unsatisfied_criterion_items, - key=self._get_criterion_item_preference, - ) - failure_causes = self._attempt_to_pin_criterion(name, criterion) + name = min(unsatisfied_names, key=self._get_preference) + failure_causes = self._attempt_to_pin_criterion(name) - # Backtrack if pinning fails. if failure_causes: - result = self._backtrack() - if not result: - causes = [ - i for crit in failure_causes for i in crit.information - ] - raise ResolutionImpossible(causes) - - self._r.ending_round(round_index, curr) + # Backtrack if pinning fails. The backtrack process puts us in + # an unpinned state, so we can work on it in the next round. + success = self._backtrack() + self.state.backtrack_causes[:] = [ + i for c in failure_causes for i in c.information + ] + + # Dead ends everywhere. Give up. + if not success: + raise ResolutionImpossible(self.state.backtrack_causes) + else: + # Pinning was successful. Push a new state to do another pin. + self._push_new_state() + + self._r.ending_round(index=round_index, state=self.state) raise ResolutionTooDeep(max_rounds) @@ -376,8 +446,7 @@ def _build_result(state): class Resolver(AbstractResolver): - """The thing that performs the actual resolution work. - """ + """The thing that performs the actual resolution work.""" base_exception = ResolverException diff --git a/venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py b/venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py index 1eee08b383a8a66f72da7260924f73699226ded5..93d1568bd4d5d63281b798b09cedbafdfaa158ee 100644 --- a/venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py +++ b/venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py @@ -1,6 +1,10 @@ +import itertools + +from .compat import collections_abc + + class DirectedGraph(object): - """A graph structure with directed edges. - """ + """A graph structure with directed edges.""" def __init__(self): self._vertices = set() @@ -17,8 +21,7 @@ class DirectedGraph(object): return key in self._vertices def copy(self): - """Return a shallow copy of this graph. - """ + """Return a shallow copy of this graph.""" other = DirectedGraph() other._vertices = set(self._vertices) other._forwards = {k: set(v) for k, v in self._forwards.items()} @@ -26,8 +29,7 @@ class DirectedGraph(object): return other def add(self, key): - """Add a new vertex to the graph. - """ + """Add a new vertex to the graph.""" if key in self._vertices: raise ValueError("vertex exists") self._vertices.add(key) @@ -35,8 +37,7 @@ class DirectedGraph(object): self._backwards[key] = set() def remove(self, key): - """Remove a vertex from the graph, disconnecting all edges from/to it. - """ + """Remove a vertex from the graph, disconnecting all edges from/to it.""" self._vertices.remove(key) for f in self._forwards.pop(key): self._backwards[f].remove(key) @@ -66,3 +67,99 @@ class DirectedGraph(object): def iter_parents(self, key): return iter(self._backwards[key]) + + +class IteratorMapping(collections_abc.Mapping): + def __init__(self, mapping, accessor, appends=None): + self._mapping = mapping + self._accessor = accessor + self._appends = appends or {} + + def __repr__(self): + return "IteratorMapping({!r}, {!r}, {!r})".format( + self._mapping, + self._accessor, + self._appends, + ) + + def __bool__(self): + return bool(self._mapping or self._appends) + + __nonzero__ = __bool__ # XXX: Python 2. + + def __contains__(self, key): + return key in self._mapping or key in self._appends + + def __getitem__(self, k): + try: + v = self._mapping[k] + except KeyError: + return iter(self._appends[k]) + return itertools.chain(self._accessor(v), self._appends.get(k, ())) + + def __iter__(self): + more = (k for k in self._appends if k not in self._mapping) + return itertools.chain(self._mapping, more) + + def __len__(self): + more = sum(1 for k in self._appends if k not in self._mapping) + return len(self._mapping) + more + + +class _FactoryIterableView(object): + """Wrap an iterator factory returned by `find_matches()`. + + Calling `iter()` on this class would invoke the underlying iterator + factory, making it a "collection with ordering" that can be iterated + through multiple times, but lacks random access methods presented in + built-in Python sequence types. + """ + + def __init__(self, factory): + self._factory = factory + + def __repr__(self): + return "{}({})".format(type(self).__name__, list(self._factory())) + + def __bool__(self): + try: + next(self._factory()) + except StopIteration: + return False + return True + + __nonzero__ = __bool__ # XXX: Python 2. + + def __iter__(self): + return self._factory() + + +class _SequenceIterableView(object): + """Wrap an iterable returned by find_matches(). + + This is essentially just a proxy to the underlying sequence that provides + the same interface as `_FactoryIterableView`. + """ + + def __init__(self, sequence): + self._sequence = sequence + + def __repr__(self): + return "{}({})".format(type(self).__name__, self._sequence) + + def __bool__(self): + return bool(self._sequence) + + __nonzero__ = __bool__ # XXX: Python 2. + + def __iter__(self): + return iter(self._sequence) + + +def build_iter_view(matches): + """Build an iterable view from the value returned by `find_matches()`.""" + if callable(matches): + return _FactoryIterableView(matches) + if not isinstance(matches, collections_abc.Sequence): + matches = list(matches) + return _SequenceIterableView(matches) diff --git a/venv/Lib/site-packages/pip/_vendor/retrying.py b/venv/Lib/site-packages/pip/_vendor/retrying.py deleted file mode 100644 index 6d1e627aae8130f76781f9a2861a90a22329c930..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/retrying.py +++ /dev/null @@ -1,267 +0,0 @@ -## Copyright 2013-2014 Ray Holder -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. - -import random -from pip._vendor import six -import sys -import time -import traceback - - -# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint... -MAX_WAIT = 1073741823 - - -def retry(*dargs, **dkw): - """ - Decorator function that instantiates the Retrying object - @param *dargs: positional arguments passed to Retrying object - @param **dkw: keyword arguments passed to the Retrying object - """ - # support both @retry and @retry() as valid syntax - if len(dargs) == 1 and callable(dargs[0]): - def wrap_simple(f): - - @six.wraps(f) - def wrapped_f(*args, **kw): - return Retrying().call(f, *args, **kw) - - return wrapped_f - - return wrap_simple(dargs[0]) - - else: - def wrap(f): - - @six.wraps(f) - def wrapped_f(*args, **kw): - return Retrying(*dargs, **dkw).call(f, *args, **kw) - - return wrapped_f - - return wrap - - -class Retrying(object): - - def __init__(self, - stop=None, wait=None, - stop_max_attempt_number=None, - stop_max_delay=None, - wait_fixed=None, - wait_random_min=None, wait_random_max=None, - wait_incrementing_start=None, wait_incrementing_increment=None, - wait_exponential_multiplier=None, wait_exponential_max=None, - retry_on_exception=None, - retry_on_result=None, - wrap_exception=False, - stop_func=None, - wait_func=None, - wait_jitter_max=None): - - self._stop_max_attempt_number = 5 if stop_max_attempt_number is None else stop_max_attempt_number - self._stop_max_delay = 100 if stop_max_delay is None else stop_max_delay - self._wait_fixed = 1000 if wait_fixed is None else wait_fixed - self._wait_random_min = 0 if wait_random_min is None else wait_random_min - self._wait_random_max = 1000 if wait_random_max is None else wait_random_max - self._wait_incrementing_start = 0 if wait_incrementing_start is None else wait_incrementing_start - self._wait_incrementing_increment = 100 if wait_incrementing_increment is None else wait_incrementing_increment - self._wait_exponential_multiplier = 1 if wait_exponential_multiplier is None else wait_exponential_multiplier - self._wait_exponential_max = MAX_WAIT if wait_exponential_max is None else wait_exponential_max - self._wait_jitter_max = 0 if wait_jitter_max is None else wait_jitter_max - - # TODO add chaining of stop behaviors - # stop behavior - stop_funcs = [] - if stop_max_attempt_number is not None: - stop_funcs.append(self.stop_after_attempt) - - if stop_max_delay is not None: - stop_funcs.append(self.stop_after_delay) - - if stop_func is not None: - self.stop = stop_func - - elif stop is None: - self.stop = lambda attempts, delay: any(f(attempts, delay) for f in stop_funcs) - - else: - self.stop = getattr(self, stop) - - # TODO add chaining of wait behaviors - # wait behavior - wait_funcs = [lambda *args, **kwargs: 0] - if wait_fixed is not None: - wait_funcs.append(self.fixed_sleep) - - if wait_random_min is not None or wait_random_max is not None: - wait_funcs.append(self.random_sleep) - - if wait_incrementing_start is not None or wait_incrementing_increment is not None: - wait_funcs.append(self.incrementing_sleep) - - if wait_exponential_multiplier is not None or wait_exponential_max is not None: - wait_funcs.append(self.exponential_sleep) - - if wait_func is not None: - self.wait = wait_func - - elif wait is None: - self.wait = lambda attempts, delay: max(f(attempts, delay) for f in wait_funcs) - - else: - self.wait = getattr(self, wait) - - # retry on exception filter - if retry_on_exception is None: - self._retry_on_exception = self.always_reject - else: - self._retry_on_exception = retry_on_exception - - # TODO simplify retrying by Exception types - # retry on result filter - if retry_on_result is None: - self._retry_on_result = self.never_reject - else: - self._retry_on_result = retry_on_result - - self._wrap_exception = wrap_exception - - def stop_after_attempt(self, previous_attempt_number, delay_since_first_attempt_ms): - """Stop after the previous attempt >= stop_max_attempt_number.""" - return previous_attempt_number >= self._stop_max_attempt_number - - def stop_after_delay(self, previous_attempt_number, delay_since_first_attempt_ms): - """Stop after the time from the first attempt >= stop_max_delay.""" - return delay_since_first_attempt_ms >= self._stop_max_delay - - def no_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): - """Don't sleep at all before retrying.""" - return 0 - - def fixed_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): - """Sleep a fixed amount of time between each retry.""" - return self._wait_fixed - - def random_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): - """Sleep a random amount of time between wait_random_min and wait_random_max""" - return random.randint(self._wait_random_min, self._wait_random_max) - - def incrementing_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): - """ - Sleep an incremental amount of time after each attempt, starting at - wait_incrementing_start and incrementing by wait_incrementing_increment - """ - result = self._wait_incrementing_start + (self._wait_incrementing_increment * (previous_attempt_number - 1)) - if result < 0: - result = 0 - return result - - def exponential_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): - exp = 2 ** previous_attempt_number - result = self._wait_exponential_multiplier * exp - if result > self._wait_exponential_max: - result = self._wait_exponential_max - if result < 0: - result = 0 - return result - - def never_reject(self, result): - return False - - def always_reject(self, result): - return True - - def should_reject(self, attempt): - reject = False - if attempt.has_exception: - reject |= self._retry_on_exception(attempt.value[1]) - else: - reject |= self._retry_on_result(attempt.value) - - return reject - - def call(self, fn, *args, **kwargs): - start_time = int(round(time.time() * 1000)) - attempt_number = 1 - while True: - try: - attempt = Attempt(fn(*args, **kwargs), attempt_number, False) - except: - tb = sys.exc_info() - attempt = Attempt(tb, attempt_number, True) - - if not self.should_reject(attempt): - return attempt.get(self._wrap_exception) - - delay_since_first_attempt_ms = int(round(time.time() * 1000)) - start_time - if self.stop(attempt_number, delay_since_first_attempt_ms): - if not self._wrap_exception and attempt.has_exception: - # get() on an attempt with an exception should cause it to be raised, but raise just in case - raise attempt.get() - else: - raise RetryError(attempt) - else: - sleep = self.wait(attempt_number, delay_since_first_attempt_ms) - if self._wait_jitter_max: - jitter = random.random() * self._wait_jitter_max - sleep = sleep + max(0, jitter) - time.sleep(sleep / 1000.0) - - attempt_number += 1 - - -class Attempt(object): - """ - An Attempt encapsulates a call to a target function that may end as a - normal return value from the function or an Exception depending on what - occurred during the execution. - """ - - def __init__(self, value, attempt_number, has_exception): - self.value = value - self.attempt_number = attempt_number - self.has_exception = has_exception - - def get(self, wrap_exception=False): - """ - Return the return value of this Attempt instance or raise an Exception. - If wrap_exception is true, this Attempt is wrapped inside of a - RetryError before being raised. - """ - if self.has_exception: - if wrap_exception: - raise RetryError(self) - else: - six.reraise(self.value[0], self.value[1], self.value[2]) - else: - return self.value - - def __repr__(self): - if self.has_exception: - return "Attempts: {0}, Error:\n{1}".format(self.attempt_number, "".join(traceback.format_tb(self.value[2]))) - else: - return "Attempts: {0}, Value: {1}".format(self.attempt_number, self.value) - - -class RetryError(Exception): - """ - A RetryError encapsulates the last Attempt instance right before giving up. - """ - - def __init__(self, last_attempt): - self.last_attempt = last_attempt - - def __str__(self): - return "RetryError[{0}]".format(self.last_attempt) diff --git a/venv/Lib/site-packages/pip/_vendor/six.py b/venv/Lib/site-packages/pip/_vendor/six.py index 5fe9f8e141ee13756fec536415adc446bfc109d7..4e15675d8b5caa33255fe37271700f587bd26671 100644 --- a/venv/Lib/site-packages/pip/_vendor/six.py +++ b/venv/Lib/site-packages/pip/_vendor/six.py @@ -29,7 +29,7 @@ import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.14.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -71,6 +71,11 @@ else: MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -186,6 +191,11 @@ class _SixMetaPathImporter(object): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -223,6 +233,12 @@ class _SixMetaPathImporter(object): return None get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + _importer = _SixMetaPathImporter(__name__) @@ -890,12 +906,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) def ensure_str(s, encoding='utf-8', errors='strict'): @@ -909,12 +924,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s diff --git a/venv/Lib/site-packages/pip/_vendor/toml.py b/venv/Lib/site-packages/pip/_vendor/toml.py deleted file mode 100644 index dac398837b3144790b63d1556dd0073bcf7f24a5..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/toml.py +++ /dev/null @@ -1,1039 +0,0 @@ -"""Python module which parses and emits TOML. - -Released under the MIT license. -""" -import re -import io -import datetime -from os import linesep -import sys - -__version__ = "0.9.6" -_spec_ = "0.4.0" - - -class TomlDecodeError(Exception): - """Base toml Exception / Error.""" - pass - - -class TomlTz(datetime.tzinfo): - def __init__(self, toml_offset): - if toml_offset == "Z": - self._raw_offset = "+00:00" - else: - self._raw_offset = toml_offset - self._sign = -1 if self._raw_offset[0] == '-' else 1 - self._hours = int(self._raw_offset[1:3]) - self._minutes = int(self._raw_offset[4:6]) - - def tzname(self, dt): - return "UTC" + self._raw_offset - - def utcoffset(self, dt): - return self._sign * datetime.timedelta(hours=self._hours, - minutes=self._minutes) - - def dst(self, dt): - return datetime.timedelta(0) - - -class InlineTableDict(object): - """Sentinel subclass of dict for inline tables.""" - - -def _get_empty_inline_table(_dict): - class DynamicInlineTableDict(_dict, InlineTableDict): - """Concrete sentinel subclass for inline tables. - It is a subclass of _dict which is passed in dynamically at load time - It is also a subclass of InlineTableDict - """ - - return DynamicInlineTableDict() - - -try: - _range = xrange -except NameError: - unicode = str - _range = range - basestring = str - unichr = chr - -try: - FNFError = FileNotFoundError -except NameError: - FNFError = IOError - - -def load(f, _dict=dict): - """Parses named file or files as toml and returns a dictionary - - Args: - f: Path to the file to open, array of files to read into single dict - or a file descriptor - _dict: (optional) Specifies the class of the returned toml dictionary - - Returns: - Parsed toml file represented as a dictionary - - Raises: - TypeError -- When f is invalid type - TomlDecodeError: Error while decoding toml - IOError / FileNotFoundError -- When an array with no valid (existing) - (Python 2 / Python 3) file paths is passed - """ - - if isinstance(f, basestring): - with io.open(f, encoding='utf-8') as ffile: - return loads(ffile.read(), _dict) - elif isinstance(f, list): - from os import path as op - from warnings import warn - if not [path for path in f if op.exists(path)]: - error_msg = "Load expects a list to contain filenames only." - error_msg += linesep - error_msg += ("The list needs to contain the path of at least one " - "existing file.") - raise FNFError(error_msg) - d = _dict() - for l in f: - if op.exists(l): - d.update(load(l)) - else: - warn("Non-existent filename in list with at least one valid " - "filename") - return d - else: - try: - return loads(f.read(), _dict) - except AttributeError: - raise TypeError("You can only load a file descriptor, filename or " - "list") - - -_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$') - - -def loads(s, _dict=dict): - """Parses string as toml - - Args: - s: String to be parsed - _dict: (optional) Specifies the class of the returned toml dictionary - - Returns: - Parsed toml file represented as a dictionary - - Raises: - TypeError: When a non-string is passed - TomlDecodeError: Error while decoding toml - """ - - implicitgroups = [] - retval = _dict() - currentlevel = retval - if not isinstance(s, basestring): - raise TypeError("Expecting something like a string") - - if not isinstance(s, unicode): - s = s.decode('utf8') - - sl = list(s) - openarr = 0 - openstring = False - openstrchar = "" - multilinestr = False - arrayoftables = False - beginline = True - keygroup = False - keyname = 0 - for i, item in enumerate(sl): - if item == '\r' and sl[i + 1] == '\n': - sl[i] = ' ' - continue - if keyname: - if item == '\n': - raise TomlDecodeError("Key name found without value." - " Reached end of line.") - if openstring: - if item == openstrchar: - keyname = 2 - openstring = False - openstrchar = "" - continue - elif keyname == 1: - if item.isspace(): - keyname = 2 - continue - elif item.isalnum() or item == '_' or item == '-': - continue - elif keyname == 2 and item.isspace(): - continue - if item == '=': - keyname = 0 - else: - raise TomlDecodeError("Found invalid character in key name: '" + - item + "'. Try quoting the key name.") - if item == "'" and openstrchar != '"': - k = 1 - try: - while sl[i - k] == "'": - k += 1 - if k == 3: - break - except IndexError: - pass - if k == 3: - multilinestr = not multilinestr - openstring = multilinestr - else: - openstring = not openstring - if openstring: - openstrchar = "'" - else: - openstrchar = "" - if item == '"' and openstrchar != "'": - oddbackslash = False - k = 1 - tripquote = False - try: - while sl[i - k] == '"': - k += 1 - if k == 3: - tripquote = True - break - if k == 1 or (k == 3 and tripquote): - while sl[i - k] == '\\': - oddbackslash = not oddbackslash - k += 1 - except IndexError: - pass - if not oddbackslash: - if tripquote: - multilinestr = not multilinestr - openstring = multilinestr - else: - openstring = not openstring - if openstring: - openstrchar = '"' - else: - openstrchar = "" - if item == '#' and (not openstring and not keygroup and - not arrayoftables): - j = i - try: - while sl[j] != '\n': - sl[j] = ' ' - j += 1 - except IndexError: - break - if item == '[' and (not openstring and not keygroup and - not arrayoftables): - if beginline: - if len(sl) > i + 1 and sl[i + 1] == '[': - arrayoftables = True - else: - keygroup = True - else: - openarr += 1 - if item == ']' and not openstring: - if keygroup: - keygroup = False - elif arrayoftables: - if sl[i - 1] == ']': - arrayoftables = False - else: - openarr -= 1 - if item == '\n': - if openstring or multilinestr: - if not multilinestr: - raise TomlDecodeError("Unbalanced quotes") - if ((sl[i - 1] == "'" or sl[i - 1] == '"') and ( - sl[i - 2] == sl[i - 1])): - sl[i] = sl[i - 1] - if sl[i - 3] == sl[i - 1]: - sl[i - 3] = ' ' - elif openarr: - sl[i] = ' ' - else: - beginline = True - elif beginline and sl[i] != ' ' and sl[i] != '\t': - beginline = False - if not keygroup and not arrayoftables: - if sl[i] == '=': - raise TomlDecodeError("Found empty keyname. ") - keyname = 1 - s = ''.join(sl) - s = s.split('\n') - multikey = None - multilinestr = "" - multibackslash = False - for line in s: - if not multilinestr or multibackslash or '\n' not in multilinestr: - line = line.strip() - if line == "" and (not multikey or multibackslash): - continue - if multikey: - if multibackslash: - multilinestr += line - else: - multilinestr += line - multibackslash = False - if len(line) > 2 and (line[-1] == multilinestr[0] and - line[-2] == multilinestr[0] and - line[-3] == multilinestr[0]): - try: - value, vtype = _load_value(multilinestr, _dict) - except ValueError as err: - raise TomlDecodeError(str(err)) - currentlevel[multikey] = value - multikey = None - multilinestr = "" - else: - k = len(multilinestr) - 1 - while k > -1 and multilinestr[k] == '\\': - multibackslash = not multibackslash - k -= 1 - if multibackslash: - multilinestr = multilinestr[:-1] - else: - multilinestr += "\n" - continue - if line[0] == '[': - arrayoftables = False - if len(line) == 1: - raise TomlDecodeError("Opening key group bracket on line by " - "itself.") - if line[1] == '[': - arrayoftables = True - line = line[2:] - splitstr = ']]' - else: - line = line[1:] - splitstr = ']' - i = 1 - quotesplits = _get_split_on_quotes(line) - quoted = False - for quotesplit in quotesplits: - if not quoted and splitstr in quotesplit: - break - i += quotesplit.count(splitstr) - quoted = not quoted - line = line.split(splitstr, i) - if len(line) < i + 1 or line[-1].strip() != "": - raise TomlDecodeError("Key group not on a line by itself.") - groups = splitstr.join(line[:-1]).split('.') - i = 0 - while i < len(groups): - groups[i] = groups[i].strip() - if len(groups[i]) > 0 and (groups[i][0] == '"' or - groups[i][0] == "'"): - groupstr = groups[i] - j = i + 1 - while not groupstr[0] == groupstr[-1]: - j += 1 - if j > len(groups) + 2: - raise TomlDecodeError("Invalid group name '" + - groupstr + "' Something " + - "went wrong.") - groupstr = '.'.join(groups[i:j]).strip() - groups[i] = groupstr[1:-1] - groups[i + 1:j] = [] - else: - if not _groupname_re.match(groups[i]): - raise TomlDecodeError("Invalid group name '" + - groups[i] + "'. Try quoting it.") - i += 1 - currentlevel = retval - for i in _range(len(groups)): - group = groups[i] - if group == "": - raise TomlDecodeError("Can't have a keygroup with an empty " - "name") - try: - currentlevel[group] - if i == len(groups) - 1: - if group in implicitgroups: - implicitgroups.remove(group) - if arrayoftables: - raise TomlDecodeError("An implicitly defined " - "table can't be an array") - elif arrayoftables: - currentlevel[group].append(_dict()) - else: - raise TomlDecodeError("What? " + group + - " already exists?" + - str(currentlevel)) - except TypeError: - currentlevel = currentlevel[-1] - try: - currentlevel[group] - except KeyError: - currentlevel[group] = _dict() - if i == len(groups) - 1 and arrayoftables: - currentlevel[group] = [_dict()] - except KeyError: - if i != len(groups) - 1: - implicitgroups.append(group) - currentlevel[group] = _dict() - if i == len(groups) - 1 and arrayoftables: - currentlevel[group] = [_dict()] - currentlevel = currentlevel[group] - if arrayoftables: - try: - currentlevel = currentlevel[-1] - except KeyError: - pass - elif line[0] == "{": - if line[-1] != "}": - raise TomlDecodeError("Line breaks are not allowed in inline" - "objects") - try: - _load_inline_object(line, currentlevel, _dict, multikey, - multibackslash) - except ValueError as err: - raise TomlDecodeError(str(err)) - elif "=" in line: - try: - ret = _load_line(line, currentlevel, _dict, multikey, - multibackslash) - except ValueError as err: - raise TomlDecodeError(str(err)) - if ret is not None: - multikey, multilinestr, multibackslash = ret - return retval - - -def _load_inline_object(line, currentlevel, _dict, multikey=False, - multibackslash=False): - candidate_groups = line[1:-1].split(",") - groups = [] - if len(candidate_groups) == 1 and not candidate_groups[0].strip(): - candidate_groups.pop() - while len(candidate_groups) > 0: - candidate_group = candidate_groups.pop(0) - try: - _, value = candidate_group.split('=', 1) - except ValueError: - raise ValueError("Invalid inline table encountered") - value = value.strip() - if ((value[0] == value[-1] and value[0] in ('"', "'")) or ( - value[0] in '-0123456789' or - value in ('true', 'false') or - (value[0] == "[" and value[-1] == "]") or - (value[0] == '{' and value[-1] == '}'))): - groups.append(candidate_group) - elif len(candidate_groups) > 0: - candidate_groups[0] = candidate_group + "," + candidate_groups[0] - else: - raise ValueError("Invalid inline table value encountered") - for group in groups: - status = _load_line(group, currentlevel, _dict, multikey, - multibackslash) - if status is not None: - break - - -# Matches a TOML number, which allows underscores for readability -_number_with_underscores = re.compile('([0-9])(_([0-9]))*') - - -def _strictly_valid_num(n): - n = n.strip() - if not n: - return False - if n[0] == '_': - return False - if n[-1] == '_': - return False - if "_." in n or "._" in n: - return False - if len(n) == 1: - return True - if n[0] == '0' and n[1] != '.': - return False - if n[0] == '+' or n[0] == '-': - n = n[1:] - if n[0] == '0' and n[1] != '.': - return False - if '__' in n: - return False - return True - - -def _get_split_on_quotes(line): - doublequotesplits = line.split('"') - quoted = False - quotesplits = [] - if len(doublequotesplits) > 1 and "'" in doublequotesplits[0]: - singlequotesplits = doublequotesplits[0].split("'") - doublequotesplits = doublequotesplits[1:] - while len(singlequotesplits) % 2 == 0 and len(doublequotesplits): - singlequotesplits[-1] += '"' + doublequotesplits[0] - doublequotesplits = doublequotesplits[1:] - if "'" in singlequotesplits[-1]: - singlequotesplits = (singlequotesplits[:-1] + - singlequotesplits[-1].split("'")) - quotesplits += singlequotesplits - for doublequotesplit in doublequotesplits: - if quoted: - quotesplits.append(doublequotesplit) - else: - quotesplits += doublequotesplit.split("'") - quoted = not quoted - return quotesplits - - -def _load_line(line, currentlevel, _dict, multikey, multibackslash): - i = 1 - quotesplits = _get_split_on_quotes(line) - quoted = False - for quotesplit in quotesplits: - if not quoted and '=' in quotesplit: - break - i += quotesplit.count('=') - quoted = not quoted - pair = line.split('=', i) - strictly_valid = _strictly_valid_num(pair[-1]) - if _number_with_underscores.match(pair[-1]): - pair[-1] = pair[-1].replace('_', '') - while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and - pair[-1][0] != "'" and pair[-1][0] != '"' and - pair[-1][0] != '[' and pair[-1][0] != '{' and - pair[-1] != 'true' and pair[-1] != 'false'): - try: - float(pair[-1]) - break - except ValueError: - pass - if _load_date(pair[-1]) is not None: - break - i += 1 - prev_val = pair[-1] - pair = line.split('=', i) - if prev_val == pair[-1]: - raise ValueError("Invalid date or number") - if strictly_valid: - strictly_valid = _strictly_valid_num(pair[-1]) - pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()] - if (pair[0][0] == '"' or pair[0][0] == "'") and \ - (pair[0][-1] == '"' or pair[0][-1] == "'"): - pair[0] = pair[0][1:-1] - if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and - pair[1][1] == pair[1][0] and - pair[1][2] == pair[1][0] and - not (len(pair[1]) > 5 and - pair[1][-1] == pair[1][0] and - pair[1][-2] == pair[1][0] and - pair[1][-3] == pair[1][0])): - k = len(pair[1]) - 1 - while k > -1 and pair[1][k] == '\\': - multibackslash = not multibackslash - k -= 1 - if multibackslash: - multilinestr = pair[1][:-1] - else: - multilinestr = pair[1] + "\n" - multikey = pair[0] - else: - value, vtype = _load_value(pair[1], _dict, strictly_valid) - try: - currentlevel[pair[0]] - raise ValueError("Duplicate keys!") - except KeyError: - if multikey: - return multikey, multilinestr, multibackslash - else: - currentlevel[pair[0]] = value - - -def _load_date(val): - microsecond = 0 - tz = None - try: - if len(val) > 19: - if val[19] == '.': - if val[-1].upper() == 'Z': - subsecondval = val[20:-1] - tzval = "Z" - else: - subsecondvalandtz = val[20:] - if '+' in subsecondvalandtz: - splitpoint = subsecondvalandtz.index('+') - subsecondval = subsecondvalandtz[:splitpoint] - tzval = subsecondvalandtz[splitpoint:] - elif '-' in subsecondvalandtz: - splitpoint = subsecondvalandtz.index('-') - subsecondval = subsecondvalandtz[:splitpoint] - tzval = subsecondvalandtz[splitpoint:] - tz = TomlTz(tzval) - microsecond = int(int(subsecondval) * - (10 ** (6 - len(subsecondval)))) - else: - tz = TomlTz(val[19:]) - except ValueError: - tz = None - if "-" not in val[1:]: - return None - try: - d = datetime.datetime( - int(val[:4]), int(val[5:7]), - int(val[8:10]), int(val[11:13]), - int(val[14:16]), int(val[17:19]), microsecond, tz) - except ValueError: - return None - return d - - -def _load_unicode_escapes(v, hexbytes, prefix): - skip = False - i = len(v) - 1 - while i > -1 and v[i] == '\\': - skip = not skip - i -= 1 - for hx in hexbytes: - if skip: - skip = False - i = len(hx) - 1 - while i > -1 and hx[i] == '\\': - skip = not skip - i -= 1 - v += prefix - v += hx - continue - hxb = "" - i = 0 - hxblen = 4 - if prefix == "\\U": - hxblen = 8 - hxb = ''.join(hx[i:i + hxblen]).lower() - if hxb.strip('0123456789abcdef'): - raise ValueError("Invalid escape sequence: " + hxb) - if hxb[0] == "d" and hxb[1].strip('01234567'): - raise ValueError("Invalid escape sequence: " + hxb + - ". Only scalar unicode points are allowed.") - v += unichr(int(hxb, 16)) - v += unicode(hx[len(hxb):]) - return v - - -# Unescape TOML string values. - -# content after the \ -_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] -# What it should be replaced by -_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] -# Used for substitution -_escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) - - -def _unescape(v): - """Unescape characters in a TOML string.""" - i = 0 - backslash = False - while i < len(v): - if backslash: - backslash = False - if v[i] in _escapes: - v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:] - elif v[i] == '\\': - v = v[:i - 1] + v[i:] - elif v[i] == 'u' or v[i] == 'U': - i += 1 - else: - raise ValueError("Reserved escape sequence used") - continue - elif v[i] == '\\': - backslash = True - i += 1 - return v - - -def _load_value(v, _dict, strictly_valid=True): - if not v: - raise ValueError("Empty value is invalid") - if v == 'true': - return (True, "bool") - elif v == 'false': - return (False, "bool") - elif v[0] == '"': - testv = v[1:].split('"') - triplequote = False - triplequotecount = 0 - if len(testv) > 1 and testv[0] == '' and testv[1] == '': - testv = testv[2:] - triplequote = True - closed = False - for tv in testv: - if tv == '': - if triplequote: - triplequotecount += 1 - else: - closed = True - else: - oddbackslash = False - try: - i = -1 - j = tv[i] - while j == '\\': - oddbackslash = not oddbackslash - i -= 1 - j = tv[i] - except IndexError: - pass - if not oddbackslash: - if closed: - raise ValueError("Stuff after closed string. WTF?") - else: - if not triplequote or triplequotecount > 1: - closed = True - else: - triplequotecount = 0 - escapeseqs = v.split('\\')[1:] - backslash = False - for i in escapeseqs: - if i == '': - backslash = not backslash - else: - if i[0] not in _escapes and (i[0] != 'u' and i[0] != 'U' and - not backslash): - raise ValueError("Reserved escape sequence used") - if backslash: - backslash = False - for prefix in ["\\u", "\\U"]: - if prefix in v: - hexbytes = v.split(prefix) - v = _load_unicode_escapes(hexbytes[0], hexbytes[1:], prefix) - v = _unescape(v) - if len(v) > 1 and v[1] == '"' and (len(v) < 3 or v[1] == v[2]): - v = v[2:-2] - return (v[1:-1], "str") - elif v[0] == "'": - if v[1] == "'" and (len(v) < 3 or v[1] == v[2]): - v = v[2:-2] - return (v[1:-1], "str") - elif v[0] == '[': - return (_load_array(v, _dict), "array") - elif v[0] == '{': - inline_object = _get_empty_inline_table(_dict) - _load_inline_object(v, inline_object, _dict) - return (inline_object, "inline_object") - else: - parsed_date = _load_date(v) - if parsed_date is not None: - return (parsed_date, "date") - if not strictly_valid: - raise ValueError("Weirdness with leading zeroes or " - "underscores in your number.") - itype = "int" - neg = False - if v[0] == '-': - neg = True - v = v[1:] - elif v[0] == '+': - v = v[1:] - v = v.replace('_', '') - if '.' in v or 'e' in v or 'E' in v: - if '.' in v and v.split('.', 1)[1] == '': - raise ValueError("This float is missing digits after " - "the point") - if v[0] not in '0123456789': - raise ValueError("This float doesn't have a leading digit") - v = float(v) - itype = "float" - else: - v = int(v) - if neg: - return (0 - v, itype) - return (v, itype) - - -def _bounded_string(s): - if len(s) == 0: - return True - if s[-1] != s[0]: - return False - i = -2 - backslash = False - while len(s) + i > 0: - if s[i] == "\\": - backslash = not backslash - i -= 1 - else: - break - return not backslash - - -def _load_array(a, _dict): - atype = None - retval = [] - a = a.strip() - if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip(): - strarray = False - tmpa = a[1:-1].strip() - if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"): - strarray = True - if not a[1:-1].strip().startswith('{'): - a = a[1:-1].split(',') - else: - # a is an inline object, we must find the matching parenthesis - # to define groups - new_a = [] - start_group_index = 1 - end_group_index = 2 - in_str = False - while end_group_index < len(a[1:]): - if a[end_group_index] == '"' or a[end_group_index] == "'": - if in_str: - backslash_index = end_group_index - 1 - while (backslash_index > -1 and - a[backslash_index] == '\\'): - in_str = not in_str - backslash_index -= 1 - in_str = not in_str - if in_str or a[end_group_index] != '}': - end_group_index += 1 - continue - - # Increase end_group_index by 1 to get the closing bracket - end_group_index += 1 - new_a.append(a[start_group_index:end_group_index]) - - # The next start index is at least after the closing bracket, a - # closing bracket can be followed by a comma since we are in - # an array. - start_group_index = end_group_index + 1 - while (start_group_index < len(a[1:]) and - a[start_group_index] != '{'): - start_group_index += 1 - end_group_index = start_group_index + 1 - a = new_a - b = 0 - if strarray: - while b < len(a) - 1: - ab = a[b].strip() - while (not _bounded_string(ab) or - (len(ab) > 2 and - ab[0] == ab[1] == ab[2] and - ab[-2] != ab[0] and - ab[-3] != ab[0])): - a[b] = a[b] + ',' + a[b + 1] - ab = a[b].strip() - if b < len(a) - 2: - a = a[:b + 1] + a[b + 2:] - else: - a = a[:b + 1] - b += 1 - else: - al = list(a[1:-1]) - a = [] - openarr = 0 - j = 0 - for i in _range(len(al)): - if al[i] == '[': - openarr += 1 - elif al[i] == ']': - openarr -= 1 - elif al[i] == ',' and not openarr: - a.append(''.join(al[j:i])) - j = i + 1 - a.append(''.join(al[j:])) - for i in _range(len(a)): - a[i] = a[i].strip() - if a[i] != '': - nval, ntype = _load_value(a[i], _dict) - if atype: - if ntype != atype: - raise ValueError("Not a homogeneous array") - else: - atype = ntype - retval.append(nval) - return retval - - -def dump(o, f): - """Writes out dict as toml to a file - - Args: - o: Object to dump into toml - f: File descriptor where the toml should be stored - - Returns: - String containing the toml corresponding to dictionary - - Raises: - TypeError: When anything other than file descriptor is passed - """ - - if not f.write: - raise TypeError("You can only dump an object to a file descriptor") - d = dumps(o) - f.write(d) - return d - - -def dumps(o, preserve=False): - """Stringifies input dict as toml - - Args: - o: Object to dump into toml - - preserve: Boolean parameter. If true, preserve inline tables. - - Returns: - String containing the toml corresponding to dict - """ - - retval = "" - addtoretval, sections = _dump_sections(o, "") - retval += addtoretval - while sections != {}: - newsections = {} - for section in sections: - addtoretval, addtosections = _dump_sections(sections[section], - section, preserve) - if addtoretval or (not addtoretval and not addtosections): - if retval and retval[-2:] != "\n\n": - retval += "\n" - retval += "[" + section + "]\n" - if addtoretval: - retval += addtoretval - for s in addtosections: - newsections[section + "." + s] = addtosections[s] - sections = newsections - return retval - - -def _dump_sections(o, sup, preserve=False): - retstr = "" - if sup != "" and sup[-1] != ".": - sup += '.' - retdict = o.__class__() - arraystr = "" - for section in o: - section = unicode(section) - qsection = section - if not re.match(r'^[A-Za-z0-9_-]+$', section): - if '"' in section: - qsection = "'" + section + "'" - else: - qsection = '"' + section + '"' - if not isinstance(o[section], dict): - arrayoftables = False - if isinstance(o[section], list): - for a in o[section]: - if isinstance(a, dict): - arrayoftables = True - if arrayoftables: - for a in o[section]: - arraytabstr = "\n" - arraystr += "[[" + sup + qsection + "]]\n" - s, d = _dump_sections(a, sup + qsection) - if s: - if s[0] == "[": - arraytabstr += s - else: - arraystr += s - while d != {}: - newd = {} - for dsec in d: - s1, d1 = _dump_sections(d[dsec], sup + qsection + - "." + dsec) - if s1: - arraytabstr += ("[" + sup + qsection + "." + - dsec + "]\n") - arraytabstr += s1 - for s1 in d1: - newd[dsec + "." + s1] = d1[s1] - d = newd - arraystr += arraytabstr - else: - if o[section] is not None: - retstr += (qsection + " = " + - unicode(_dump_value(o[section])) + '\n') - elif preserve and isinstance(o[section], InlineTableDict): - retstr += (qsection + " = " + _dump_inline_table(o[section])) - else: - retdict[qsection] = o[section] - retstr += arraystr - return (retstr, retdict) - - -def _dump_inline_table(section): - """Preserve inline table in its compact syntax instead of expanding - into subsection. - - https://github.com/toml-lang/toml#user-content-inline-table - """ - retval = "" - if isinstance(section, dict): - val_list = [] - for k, v in section.items(): - val = _dump_inline_table(v) - val_list.append(k + " = " + val) - retval += "{ " + ", ".join(val_list) + " }\n" - return retval - else: - return unicode(_dump_value(section)) - - -def _dump_value(v): - dump_funcs = { - str: _dump_str, - unicode: _dump_str, - list: _dump_list, - int: lambda v: v, - bool: lambda v: unicode(v).lower(), - float: _dump_float, - datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), - } - # Lookup function corresponding to v's type - dump_fn = dump_funcs.get(type(v)) - if dump_fn is None and hasattr(v, '__iter__'): - dump_fn = dump_funcs[list] - # Evaluate function (if it exists) else return v - return dump_fn(v) if dump_fn is not None else dump_funcs[str](v) - - -def _dump_str(v): - if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str): - v = v.decode('utf-8') - v = "%r" % v - if v[0] == 'u': - v = v[1:] - singlequote = v.startswith("'") - if singlequote or v.startswith('"'): - v = v[1:-1] - if singlequote: - v = v.replace("\\'", "'") - v = v.replace('"', '\\"') - v = v.split("\\x") - while len(v) > 1: - i = -1 - if not v[0]: - v = v[1:] - v[0] = v[0].replace("\\\\", "\\") - # No, I don't know why != works and == breaks - joinx = v[0][i] != "\\" - while v[0][:i] and v[0][i] == "\\": - joinx = not joinx - i -= 1 - if joinx: - joiner = "x" - else: - joiner = "u00" - v = [v[0] + joiner + v[1]] + v[2:] - return unicode('"' + v[0] + '"') - - -def _dump_list(v): - retval = "[" - for u in v: - retval += " " + unicode(_dump_value(u)) + "," - retval += "]" - return retval - - -def _dump_float(v): - return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-") diff --git a/venv/Lib/site-packages/pip/_vendor/toml/__init__.py b/venv/Lib/site-packages/pip/_vendor/toml/__init__.py deleted file mode 100644 index 015d73cbe4edb3ae422be0a0c11bc4641389d227..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/toml/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Python module which parses and emits TOML. - -Released under the MIT license. -""" - -from pip._vendor.toml import encoder -from pip._vendor.toml import decoder - -__version__ = "0.10.0" -_spec_ = "0.5.0" - -load = decoder.load -loads = decoder.loads -TomlDecoder = decoder.TomlDecoder -TomlDecodeError = decoder.TomlDecodeError - -dump = encoder.dump -dumps = encoder.dumps -TomlEncoder = encoder.TomlEncoder -TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder -TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder diff --git a/venv/Lib/site-packages/pip/_vendor/toml/decoder.py b/venv/Lib/site-packages/pip/_vendor/toml/decoder.py deleted file mode 100644 index 20be459122dfc93ab8d844fd6dc964b1931a33c9..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/toml/decoder.py +++ /dev/null @@ -1,945 +0,0 @@ -import datetime -import io -from os import linesep -import re -import sys - -from pip._vendor.toml.tz import TomlTz - -if sys.version_info < (3,): - _range = xrange # noqa: F821 -else: - unicode = str - _range = range - basestring = str - unichr = chr - - -def _detect_pathlib_path(p): - if (3, 4) <= sys.version_info: - import pathlib - if isinstance(p, pathlib.PurePath): - return True - return False - - -def _ispath(p): - if isinstance(p, basestring): - return True - return _detect_pathlib_path(p) - - -def _getpath(p): - if (3, 6) <= sys.version_info: - import os - return os.fspath(p) - if _detect_pathlib_path(p): - return str(p) - return p - - -try: - FNFError = FileNotFoundError -except NameError: - FNFError = IOError - - -TIME_RE = re.compile("([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]{3,6}))?") - - -class TomlDecodeError(ValueError): - """Base toml Exception / Error.""" - - def __init__(self, msg, doc, pos): - lineno = doc.count('\n', 0, pos) + 1 - colno = pos - doc.rfind('\n', 0, pos) - emsg = '{} (line {} column {} char {})'.format(msg, lineno, colno, pos) - ValueError.__init__(self, emsg) - self.msg = msg - self.doc = doc - self.pos = pos - self.lineno = lineno - self.colno = colno - - -# Matches a TOML number, which allows underscores for readability -_number_with_underscores = re.compile('([0-9])(_([0-9]))*') - - -def _strictly_valid_num(n): - n = n.strip() - if not n: - return False - if n[0] == '_': - return False - if n[-1] == '_': - return False - if "_." in n or "._" in n: - return False - if len(n) == 1: - return True - if n[0] == '0' and n[1] not in ['.', 'o', 'b', 'x']: - return False - if n[0] == '+' or n[0] == '-': - n = n[1:] - if len(n) > 1 and n[0] == '0' and n[1] != '.': - return False - if '__' in n: - return False - return True - - -def load(f, _dict=dict, decoder=None): - """Parses named file or files as toml and returns a dictionary - - Args: - f: Path to the file to open, array of files to read into single dict - or a file descriptor - _dict: (optional) Specifies the class of the returned toml dictionary - - Returns: - Parsed toml file represented as a dictionary - - Raises: - TypeError -- When f is invalid type - TomlDecodeError: Error while decoding toml - IOError / FileNotFoundError -- When an array with no valid (existing) - (Python 2 / Python 3) file paths is passed - """ - - if _ispath(f): - with io.open(_getpath(f), encoding='utf-8') as ffile: - return loads(ffile.read(), _dict, decoder) - elif isinstance(f, list): - from os import path as op - from warnings import warn - if not [path for path in f if op.exists(path)]: - error_msg = "Load expects a list to contain filenames only." - error_msg += linesep - error_msg += ("The list needs to contain the path of at least one " - "existing file.") - raise FNFError(error_msg) - if decoder is None: - decoder = TomlDecoder() - d = decoder.get_empty_table() - for l in f: - if op.exists(l): - d.update(load(l, _dict, decoder)) - else: - warn("Non-existent filename in list with at least one valid " - "filename") - return d - else: - try: - return loads(f.read(), _dict, decoder) - except AttributeError: - raise TypeError("You can only load a file descriptor, filename or " - "list") - - -_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$') - - -def loads(s, _dict=dict, decoder=None): - """Parses string as toml - - Args: - s: String to be parsed - _dict: (optional) Specifies the class of the returned toml dictionary - - Returns: - Parsed toml file represented as a dictionary - - Raises: - TypeError: When a non-string is passed - TomlDecodeError: Error while decoding toml - """ - - implicitgroups = [] - if decoder is None: - decoder = TomlDecoder(_dict) - retval = decoder.get_empty_table() - currentlevel = retval - if not isinstance(s, basestring): - raise TypeError("Expecting something like a string") - - if not isinstance(s, unicode): - s = s.decode('utf8') - - original = s - sl = list(s) - openarr = 0 - openstring = False - openstrchar = "" - multilinestr = False - arrayoftables = False - beginline = True - keygroup = False - dottedkey = False - keyname = 0 - for i, item in enumerate(sl): - if item == '\r' and sl[i + 1] == '\n': - sl[i] = ' ' - continue - if keyname: - if item == '\n': - raise TomlDecodeError("Key name found without value." - " Reached end of line.", original, i) - if openstring: - if item == openstrchar: - keyname = 2 - openstring = False - openstrchar = "" - continue - elif keyname == 1: - if item.isspace(): - keyname = 2 - continue - elif item == '.': - dottedkey = True - continue - elif item.isalnum() or item == '_' or item == '-': - continue - elif (dottedkey and sl[i - 1] == '.' and - (item == '"' or item == "'")): - openstring = True - openstrchar = item - continue - elif keyname == 2: - if item.isspace(): - if dottedkey: - nextitem = sl[i + 1] - if not nextitem.isspace() and nextitem != '.': - keyname = 1 - continue - if item == '.': - dottedkey = True - nextitem = sl[i + 1] - if not nextitem.isspace() and nextitem != '.': - keyname = 1 - continue - if item == '=': - keyname = 0 - dottedkey = False - else: - raise TomlDecodeError("Found invalid character in key name: '" + - item + "'. Try quoting the key name.", - original, i) - if item == "'" and openstrchar != '"': - k = 1 - try: - while sl[i - k] == "'": - k += 1 - if k == 3: - break - except IndexError: - pass - if k == 3: - multilinestr = not multilinestr - openstring = multilinestr - else: - openstring = not openstring - if openstring: - openstrchar = "'" - else: - openstrchar = "" - if item == '"' and openstrchar != "'": - oddbackslash = False - k = 1 - tripquote = False - try: - while sl[i - k] == '"': - k += 1 - if k == 3: - tripquote = True - break - if k == 1 or (k == 3 and tripquote): - while sl[i - k] == '\\': - oddbackslash = not oddbackslash - k += 1 - except IndexError: - pass - if not oddbackslash: - if tripquote: - multilinestr = not multilinestr - openstring = multilinestr - else: - openstring = not openstring - if openstring: - openstrchar = '"' - else: - openstrchar = "" - if item == '#' and (not openstring and not keygroup and - not arrayoftables): - j = i - try: - while sl[j] != '\n': - sl[j] = ' ' - j += 1 - except IndexError: - break - if item == '[' and (not openstring and not keygroup and - not arrayoftables): - if beginline: - if len(sl) > i + 1 and sl[i + 1] == '[': - arrayoftables = True - else: - keygroup = True - else: - openarr += 1 - if item == ']' and not openstring: - if keygroup: - keygroup = False - elif arrayoftables: - if sl[i - 1] == ']': - arrayoftables = False - else: - openarr -= 1 - if item == '\n': - if openstring or multilinestr: - if not multilinestr: - raise TomlDecodeError("Unbalanced quotes", original, i) - if ((sl[i - 1] == "'" or sl[i - 1] == '"') and ( - sl[i - 2] == sl[i - 1])): - sl[i] = sl[i - 1] - if sl[i - 3] == sl[i - 1]: - sl[i - 3] = ' ' - elif openarr: - sl[i] = ' ' - else: - beginline = True - elif beginline and sl[i] != ' ' and sl[i] != '\t': - beginline = False - if not keygroup and not arrayoftables: - if sl[i] == '=': - raise TomlDecodeError("Found empty keyname. ", original, i) - keyname = 1 - s = ''.join(sl) - s = s.split('\n') - multikey = None - multilinestr = "" - multibackslash = False - pos = 0 - for idx, line in enumerate(s): - if idx > 0: - pos += len(s[idx - 1]) + 1 - if not multilinestr or multibackslash or '\n' not in multilinestr: - line = line.strip() - if line == "" and (not multikey or multibackslash): - continue - if multikey: - if multibackslash: - multilinestr += line - else: - multilinestr += line - multibackslash = False - if len(line) > 2 and (line[-1] == multilinestr[0] and - line[-2] == multilinestr[0] and - line[-3] == multilinestr[0]): - try: - value, vtype = decoder.load_value(multilinestr) - except ValueError as err: - raise TomlDecodeError(str(err), original, pos) - currentlevel[multikey] = value - multikey = None - multilinestr = "" - else: - k = len(multilinestr) - 1 - while k > -1 and multilinestr[k] == '\\': - multibackslash = not multibackslash - k -= 1 - if multibackslash: - multilinestr = multilinestr[:-1] - else: - multilinestr += "\n" - continue - if line[0] == '[': - arrayoftables = False - if len(line) == 1: - raise TomlDecodeError("Opening key group bracket on line by " - "itself.", original, pos) - if line[1] == '[': - arrayoftables = True - line = line[2:] - splitstr = ']]' - else: - line = line[1:] - splitstr = ']' - i = 1 - quotesplits = decoder._get_split_on_quotes(line) - quoted = False - for quotesplit in quotesplits: - if not quoted and splitstr in quotesplit: - break - i += quotesplit.count(splitstr) - quoted = not quoted - line = line.split(splitstr, i) - if len(line) < i + 1 or line[-1].strip() != "": - raise TomlDecodeError("Key group not on a line by itself.", - original, pos) - groups = splitstr.join(line[:-1]).split('.') - i = 0 - while i < len(groups): - groups[i] = groups[i].strip() - if len(groups[i]) > 0 and (groups[i][0] == '"' or - groups[i][0] == "'"): - groupstr = groups[i] - j = i + 1 - while not groupstr[0] == groupstr[-1]: - j += 1 - if j > len(groups) + 2: - raise TomlDecodeError("Invalid group name '" + - groupstr + "' Something " + - "went wrong.", original, pos) - groupstr = '.'.join(groups[i:j]).strip() - groups[i] = groupstr[1:-1] - groups[i + 1:j] = [] - else: - if not _groupname_re.match(groups[i]): - raise TomlDecodeError("Invalid group name '" + - groups[i] + "'. Try quoting it.", - original, pos) - i += 1 - currentlevel = retval - for i in _range(len(groups)): - group = groups[i] - if group == "": - raise TomlDecodeError("Can't have a keygroup with an empty " - "name", original, pos) - try: - currentlevel[group] - if i == len(groups) - 1: - if group in implicitgroups: - implicitgroups.remove(group) - if arrayoftables: - raise TomlDecodeError("An implicitly defined " - "table can't be an array", - original, pos) - elif arrayoftables: - currentlevel[group].append(decoder.get_empty_table() - ) - else: - raise TomlDecodeError("What? " + group + - " already exists?" + - str(currentlevel), - original, pos) - except TypeError: - currentlevel = currentlevel[-1] - if group not in currentlevel: - currentlevel[group] = decoder.get_empty_table() - if i == len(groups) - 1 and arrayoftables: - currentlevel[group] = [decoder.get_empty_table()] - except KeyError: - if i != len(groups) - 1: - implicitgroups.append(group) - currentlevel[group] = decoder.get_empty_table() - if i == len(groups) - 1 and arrayoftables: - currentlevel[group] = [decoder.get_empty_table()] - currentlevel = currentlevel[group] - if arrayoftables: - try: - currentlevel = currentlevel[-1] - except KeyError: - pass - elif line[0] == "{": - if line[-1] != "}": - raise TomlDecodeError("Line breaks are not allowed in inline" - "objects", original, pos) - try: - decoder.load_inline_object(line, currentlevel, multikey, - multibackslash) - except ValueError as err: - raise TomlDecodeError(str(err), original, pos) - elif "=" in line: - try: - ret = decoder.load_line(line, currentlevel, multikey, - multibackslash) - except ValueError as err: - raise TomlDecodeError(str(err), original, pos) - if ret is not None: - multikey, multilinestr, multibackslash = ret - return retval - - -def _load_date(val): - microsecond = 0 - tz = None - try: - if len(val) > 19: - if val[19] == '.': - if val[-1].upper() == 'Z': - subsecondval = val[20:-1] - tzval = "Z" - else: - subsecondvalandtz = val[20:] - if '+' in subsecondvalandtz: - splitpoint = subsecondvalandtz.index('+') - subsecondval = subsecondvalandtz[:splitpoint] - tzval = subsecondvalandtz[splitpoint:] - elif '-' in subsecondvalandtz: - splitpoint = subsecondvalandtz.index('-') - subsecondval = subsecondvalandtz[:splitpoint] - tzval = subsecondvalandtz[splitpoint:] - else: - tzval = None - subsecondval = subsecondvalandtz - if tzval is not None: - tz = TomlTz(tzval) - microsecond = int(int(subsecondval) * - (10 ** (6 - len(subsecondval)))) - else: - tz = TomlTz(val[19:]) - except ValueError: - tz = None - if "-" not in val[1:]: - return None - try: - if len(val) == 10: - d = datetime.date( - int(val[:4]), int(val[5:7]), - int(val[8:10])) - else: - d = datetime.datetime( - int(val[:4]), int(val[5:7]), - int(val[8:10]), int(val[11:13]), - int(val[14:16]), int(val[17:19]), microsecond, tz) - except ValueError: - return None - return d - - -def _load_unicode_escapes(v, hexbytes, prefix): - skip = False - i = len(v) - 1 - while i > -1 and v[i] == '\\': - skip = not skip - i -= 1 - for hx in hexbytes: - if skip: - skip = False - i = len(hx) - 1 - while i > -1 and hx[i] == '\\': - skip = not skip - i -= 1 - v += prefix - v += hx - continue - hxb = "" - i = 0 - hxblen = 4 - if prefix == "\\U": - hxblen = 8 - hxb = ''.join(hx[i:i + hxblen]).lower() - if hxb.strip('0123456789abcdef'): - raise ValueError("Invalid escape sequence: " + hxb) - if hxb[0] == "d" and hxb[1].strip('01234567'): - raise ValueError("Invalid escape sequence: " + hxb + - ". Only scalar unicode points are allowed.") - v += unichr(int(hxb, 16)) - v += unicode(hx[len(hxb):]) - return v - - -# Unescape TOML string values. - -# content after the \ -_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] -# What it should be replaced by -_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] -# Used for substitution -_escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) - - -def _unescape(v): - """Unescape characters in a TOML string.""" - i = 0 - backslash = False - while i < len(v): - if backslash: - backslash = False - if v[i] in _escapes: - v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:] - elif v[i] == '\\': - v = v[:i - 1] + v[i:] - elif v[i] == 'u' or v[i] == 'U': - i += 1 - else: - raise ValueError("Reserved escape sequence used") - continue - elif v[i] == '\\': - backslash = True - i += 1 - return v - - -class InlineTableDict(object): - """Sentinel subclass of dict for inline tables.""" - - -class TomlDecoder(object): - - def __init__(self, _dict=dict): - self._dict = _dict - - def get_empty_table(self): - return self._dict() - - def get_empty_inline_table(self): - class DynamicInlineTableDict(self._dict, InlineTableDict): - """Concrete sentinel subclass for inline tables. - It is a subclass of _dict which is passed in dynamically at load - time - - It is also a subclass of InlineTableDict - """ - - return DynamicInlineTableDict() - - def load_inline_object(self, line, currentlevel, multikey=False, - multibackslash=False): - candidate_groups = line[1:-1].split(",") - groups = [] - if len(candidate_groups) == 1 and not candidate_groups[0].strip(): - candidate_groups.pop() - while len(candidate_groups) > 0: - candidate_group = candidate_groups.pop(0) - try: - _, value = candidate_group.split('=', 1) - except ValueError: - raise ValueError("Invalid inline table encountered") - value = value.strip() - if ((value[0] == value[-1] and value[0] in ('"', "'")) or ( - value[0] in '-0123456789' or - value in ('true', 'false') or - (value[0] == "[" and value[-1] == "]") or - (value[0] == '{' and value[-1] == '}'))): - groups.append(candidate_group) - elif len(candidate_groups) > 0: - candidate_groups[0] = (candidate_group + "," + - candidate_groups[0]) - else: - raise ValueError("Invalid inline table value encountered") - for group in groups: - status = self.load_line(group, currentlevel, multikey, - multibackslash) - if status is not None: - break - - def _get_split_on_quotes(self, line): - doublequotesplits = line.split('"') - quoted = False - quotesplits = [] - if len(doublequotesplits) > 1 and "'" in doublequotesplits[0]: - singlequotesplits = doublequotesplits[0].split("'") - doublequotesplits = doublequotesplits[1:] - while len(singlequotesplits) % 2 == 0 and len(doublequotesplits): - singlequotesplits[-1] += '"' + doublequotesplits[0] - doublequotesplits = doublequotesplits[1:] - if "'" in singlequotesplits[-1]: - singlequotesplits = (singlequotesplits[:-1] + - singlequotesplits[-1].split("'")) - quotesplits += singlequotesplits - for doublequotesplit in doublequotesplits: - if quoted: - quotesplits.append(doublequotesplit) - else: - quotesplits += doublequotesplit.split("'") - quoted = not quoted - return quotesplits - - def load_line(self, line, currentlevel, multikey, multibackslash): - i = 1 - quotesplits = self._get_split_on_quotes(line) - quoted = False - for quotesplit in quotesplits: - if not quoted and '=' in quotesplit: - break - i += quotesplit.count('=') - quoted = not quoted - pair = line.split('=', i) - strictly_valid = _strictly_valid_num(pair[-1]) - if _number_with_underscores.match(pair[-1]): - pair[-1] = pair[-1].replace('_', '') - while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and - pair[-1][0] != "'" and pair[-1][0] != '"' and - pair[-1][0] != '[' and pair[-1][0] != '{' and - pair[-1] != 'true' and pair[-1] != 'false'): - try: - float(pair[-1]) - break - except ValueError: - pass - if _load_date(pair[-1]) is not None: - break - i += 1 - prev_val = pair[-1] - pair = line.split('=', i) - if prev_val == pair[-1]: - raise ValueError("Invalid date or number") - if strictly_valid: - strictly_valid = _strictly_valid_num(pair[-1]) - pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()] - if '.' in pair[0]: - if '"' in pair[0] or "'" in pair[0]: - quotesplits = self._get_split_on_quotes(pair[0]) - quoted = False - levels = [] - for quotesplit in quotesplits: - if quoted: - levels.append(quotesplit) - else: - levels += [level.strip() for level in - quotesplit.split('.')] - quoted = not quoted - else: - levels = pair[0].split('.') - while levels[-1] == "": - levels = levels[:-1] - for level in levels[:-1]: - if level == "": - continue - if level not in currentlevel: - currentlevel[level] = self.get_empty_table() - currentlevel = currentlevel[level] - pair[0] = levels[-1].strip() - elif (pair[0][0] == '"' or pair[0][0] == "'") and \ - (pair[0][-1] == pair[0][0]): - pair[0] = pair[0][1:-1] - if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and - pair[1][1] == pair[1][0] and - pair[1][2] == pair[1][0] and - not (len(pair[1]) > 5 and - pair[1][-1] == pair[1][0] and - pair[1][-2] == pair[1][0] and - pair[1][-3] == pair[1][0])): - k = len(pair[1]) - 1 - while k > -1 and pair[1][k] == '\\': - multibackslash = not multibackslash - k -= 1 - if multibackslash: - multilinestr = pair[1][:-1] - else: - multilinestr = pair[1] + "\n" - multikey = pair[0] - else: - value, vtype = self.load_value(pair[1], strictly_valid) - try: - currentlevel[pair[0]] - raise ValueError("Duplicate keys!") - except TypeError: - raise ValueError("Duplicate keys!") - except KeyError: - if multikey: - return multikey, multilinestr, multibackslash - else: - currentlevel[pair[0]] = value - - def load_value(self, v, strictly_valid=True): - if not v: - raise ValueError("Empty value is invalid") - if v == 'true': - return (True, "bool") - elif v == 'false': - return (False, "bool") - elif v[0] == '"' or v[0] == "'": - quotechar = v[0] - testv = v[1:].split(quotechar) - triplequote = False - triplequotecount = 0 - if len(testv) > 1 and testv[0] == '' and testv[1] == '': - testv = testv[2:] - triplequote = True - closed = False - for tv in testv: - if tv == '': - if triplequote: - triplequotecount += 1 - else: - closed = True - else: - oddbackslash = False - try: - i = -1 - j = tv[i] - while j == '\\': - oddbackslash = not oddbackslash - i -= 1 - j = tv[i] - except IndexError: - pass - if not oddbackslash: - if closed: - raise ValueError("Stuff after closed string. WTF?") - else: - if not triplequote or triplequotecount > 1: - closed = True - else: - triplequotecount = 0 - if quotechar == '"': - escapeseqs = v.split('\\')[1:] - backslash = False - for i in escapeseqs: - if i == '': - backslash = not backslash - else: - if i[0] not in _escapes and (i[0] != 'u' and - i[0] != 'U' and - not backslash): - raise ValueError("Reserved escape sequence used") - if backslash: - backslash = False - for prefix in ["\\u", "\\U"]: - if prefix in v: - hexbytes = v.split(prefix) - v = _load_unicode_escapes(hexbytes[0], hexbytes[1:], - prefix) - v = _unescape(v) - if len(v) > 1 and v[1] == quotechar and (len(v) < 3 or - v[1] == v[2]): - v = v[2:-2] - return (v[1:-1], "str") - elif v[0] == '[': - return (self.load_array(v), "array") - elif v[0] == '{': - inline_object = self.get_empty_inline_table() - self.load_inline_object(v, inline_object) - return (inline_object, "inline_object") - elif TIME_RE.match(v): - h, m, s, _, ms = TIME_RE.match(v).groups() - time = datetime.time(int(h), int(m), int(s), int(ms) if ms else 0) - return (time, "time") - else: - parsed_date = _load_date(v) - if parsed_date is not None: - return (parsed_date, "date") - if not strictly_valid: - raise ValueError("Weirdness with leading zeroes or " - "underscores in your number.") - itype = "int" - neg = False - if v[0] == '-': - neg = True - v = v[1:] - elif v[0] == '+': - v = v[1:] - v = v.replace('_', '') - lowerv = v.lower() - if '.' in v or ('x' not in v and ('e' in v or 'E' in v)): - if '.' in v and v.split('.', 1)[1] == '': - raise ValueError("This float is missing digits after " - "the point") - if v[0] not in '0123456789': - raise ValueError("This float doesn't have a leading " - "digit") - v = float(v) - itype = "float" - elif len(lowerv) == 3 and (lowerv == 'inf' or lowerv == 'nan'): - v = float(v) - itype = "float" - if itype == "int": - v = int(v, 0) - if neg: - return (0 - v, itype) - return (v, itype) - - def bounded_string(self, s): - if len(s) == 0: - return True - if s[-1] != s[0]: - return False - i = -2 - backslash = False - while len(s) + i > 0: - if s[i] == "\\": - backslash = not backslash - i -= 1 - else: - break - return not backslash - - def load_array(self, a): - atype = None - retval = [] - a = a.strip() - if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip(): - strarray = False - tmpa = a[1:-1].strip() - if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"): - strarray = True - if not a[1:-1].strip().startswith('{'): - a = a[1:-1].split(',') - else: - # a is an inline object, we must find the matching parenthesis - # to define groups - new_a = [] - start_group_index = 1 - end_group_index = 2 - in_str = False - while end_group_index < len(a[1:]): - if a[end_group_index] == '"' or a[end_group_index] == "'": - if in_str: - backslash_index = end_group_index - 1 - while (backslash_index > -1 and - a[backslash_index] == '\\'): - in_str = not in_str - backslash_index -= 1 - in_str = not in_str - if in_str or a[end_group_index] != '}': - end_group_index += 1 - continue - - # Increase end_group_index by 1 to get the closing bracket - end_group_index += 1 - - new_a.append(a[start_group_index:end_group_index]) - - # The next start index is at least after the closing - # bracket, a closing bracket can be followed by a comma - # since we are in an array. - start_group_index = end_group_index + 1 - while (start_group_index < len(a[1:]) and - a[start_group_index] != '{'): - start_group_index += 1 - end_group_index = start_group_index + 1 - a = new_a - b = 0 - if strarray: - while b < len(a) - 1: - ab = a[b].strip() - while (not self.bounded_string(ab) or - (len(ab) > 2 and - ab[0] == ab[1] == ab[2] and - ab[-2] != ab[0] and - ab[-3] != ab[0])): - a[b] = a[b] + ',' + a[b + 1] - ab = a[b].strip() - if b < len(a) - 2: - a = a[:b + 1] + a[b + 2:] - else: - a = a[:b + 1] - b += 1 - else: - al = list(a[1:-1]) - a = [] - openarr = 0 - j = 0 - for i in _range(len(al)): - if al[i] == '[': - openarr += 1 - elif al[i] == ']': - openarr -= 1 - elif al[i] == ',' and not openarr: - a.append(''.join(al[j:i])) - j = i + 1 - a.append(''.join(al[j:])) - for i in _range(len(a)): - a[i] = a[i].strip() - if a[i] != '': - nval, ntype = self.load_value(a[i]) - if atype: - if ntype != atype: - raise ValueError("Not a homogeneous array") - else: - atype = ntype - retval.append(nval) - return retval diff --git a/venv/Lib/site-packages/pip/_vendor/toml/encoder.py b/venv/Lib/site-packages/pip/_vendor/toml/encoder.py deleted file mode 100644 index 53b0bd5ace51d2adb5cf913cf0c9a870873edcf7..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/toml/encoder.py +++ /dev/null @@ -1,250 +0,0 @@ -import datetime -import re -import sys - -from pip._vendor.toml.decoder import InlineTableDict - -if sys.version_info >= (3,): - unicode = str - - -def dump(o, f): - """Writes out dict as toml to a file - - Args: - o: Object to dump into toml - f: File descriptor where the toml should be stored - - Returns: - String containing the toml corresponding to dictionary - - Raises: - TypeError: When anything other than file descriptor is passed - """ - - if not f.write: - raise TypeError("You can only dump an object to a file descriptor") - d = dumps(o) - f.write(d) - return d - - -def dumps(o, encoder=None): - """Stringifies input dict as toml - - Args: - o: Object to dump into toml - - preserve: Boolean parameter. If true, preserve inline tables. - - Returns: - String containing the toml corresponding to dict - """ - - retval = "" - if encoder is None: - encoder = TomlEncoder(o.__class__) - addtoretval, sections = encoder.dump_sections(o, "") - retval += addtoretval - while sections: - newsections = encoder.get_empty_table() - for section in sections: - addtoretval, addtosections = encoder.dump_sections( - sections[section], section) - - if addtoretval or (not addtoretval and not addtosections): - if retval and retval[-2:] != "\n\n": - retval += "\n" - retval += "[" + section + "]\n" - if addtoretval: - retval += addtoretval - for s in addtosections: - newsections[section + "." + s] = addtosections[s] - sections = newsections - return retval - - -def _dump_str(v): - if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str): - v = v.decode('utf-8') - v = "%r" % v - if v[0] == 'u': - v = v[1:] - singlequote = v.startswith("'") - if singlequote or v.startswith('"'): - v = v[1:-1] - if singlequote: - v = v.replace("\\'", "'") - v = v.replace('"', '\\"') - v = v.split("\\x") - while len(v) > 1: - i = -1 - if not v[0]: - v = v[1:] - v[0] = v[0].replace("\\\\", "\\") - # No, I don't know why != works and == breaks - joinx = v[0][i] != "\\" - while v[0][:i] and v[0][i] == "\\": - joinx = not joinx - i -= 1 - if joinx: - joiner = "x" - else: - joiner = "u00" - v = [v[0] + joiner + v[1]] + v[2:] - return unicode('"' + v[0] + '"') - - -def _dump_float(v): - return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-") - - -def _dump_time(v): - utcoffset = v.utcoffset() - if utcoffset is None: - return v.isoformat() - # The TOML norm specifies that it's local time thus we drop the offset - return v.isoformat()[:-6] - - -class TomlEncoder(object): - - def __init__(self, _dict=dict, preserve=False): - self._dict = _dict - self.preserve = preserve - self.dump_funcs = { - str: _dump_str, - unicode: _dump_str, - list: self.dump_list, - bool: lambda v: unicode(v).lower(), - int: lambda v: v, - float: _dump_float, - datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), - datetime.time: _dump_time, - datetime.date: lambda v: v.isoformat() - } - - def get_empty_table(self): - return self._dict() - - def dump_list(self, v): - retval = "[" - for u in v: - retval += " " + unicode(self.dump_value(u)) + "," - retval += "]" - return retval - - def dump_inline_table(self, section): - """Preserve inline table in its compact syntax instead of expanding - into subsection. - - https://github.com/toml-lang/toml#user-content-inline-table - """ - retval = "" - if isinstance(section, dict): - val_list = [] - for k, v in section.items(): - val = self.dump_inline_table(v) - val_list.append(k + " = " + val) - retval += "{ " + ", ".join(val_list) + " }\n" - return retval - else: - return unicode(self.dump_value(section)) - - def dump_value(self, v): - # Lookup function corresponding to v's type - dump_fn = self.dump_funcs.get(type(v)) - if dump_fn is None and hasattr(v, '__iter__'): - dump_fn = self.dump_funcs[list] - # Evaluate function (if it exists) else return v - return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v) - - def dump_sections(self, o, sup): - retstr = "" - if sup != "" and sup[-1] != ".": - sup += '.' - retdict = self._dict() - arraystr = "" - for section in o: - section = unicode(section) - qsection = section - if not re.match(r'^[A-Za-z0-9_-]+$', section): - if '"' in section: - qsection = "'" + section + "'" - else: - qsection = '"' + section + '"' - if not isinstance(o[section], dict): - arrayoftables = False - if isinstance(o[section], list): - for a in o[section]: - if isinstance(a, dict): - arrayoftables = True - if arrayoftables: - for a in o[section]: - arraytabstr = "\n" - arraystr += "[[" + sup + qsection + "]]\n" - s, d = self.dump_sections(a, sup + qsection) - if s: - if s[0] == "[": - arraytabstr += s - else: - arraystr += s - while d: - newd = self._dict() - for dsec in d: - s1, d1 = self.dump_sections(d[dsec], sup + - qsection + "." + - dsec) - if s1: - arraytabstr += ("[" + sup + qsection + - "." + dsec + "]\n") - arraytabstr += s1 - for s1 in d1: - newd[dsec + "." + s1] = d1[s1] - d = newd - arraystr += arraytabstr - else: - if o[section] is not None: - retstr += (qsection + " = " + - unicode(self.dump_value(o[section])) + '\n') - elif self.preserve and isinstance(o[section], InlineTableDict): - retstr += (qsection + " = " + - self.dump_inline_table(o[section])) - else: - retdict[qsection] = o[section] - retstr += arraystr - return (retstr, retdict) - - -class TomlPreserveInlineDictEncoder(TomlEncoder): - - def __init__(self, _dict=dict): - super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True) - - -class TomlArraySeparatorEncoder(TomlEncoder): - - def __init__(self, _dict=dict, preserve=False, separator=","): - super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve) - if separator.strip() == "": - separator = "," + separator - elif separator.strip(' \t\n\r,'): - raise ValueError("Invalid separator for arrays") - self.separator = separator - - def dump_list(self, v): - t = [] - retval = "[" - for u in v: - t.append(self.dump_value(u)) - while t != []: - s = [] - for u in t: - if isinstance(u, list): - for r in u: - s.append(r) - else: - retval += " " + unicode(u) + self.separator - t = s - retval += "]" - return retval diff --git a/venv/Lib/site-packages/pip/_vendor/toml/ordered.py b/venv/Lib/site-packages/pip/_vendor/toml/ordered.py deleted file mode 100644 index 6052016e8e6536a8cf58fbbc72b9042f3e070fce..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/toml/ordered.py +++ /dev/null @@ -1,15 +0,0 @@ -from collections import OrderedDict -from pip._vendor.toml import TomlEncoder -from pip._vendor.toml import TomlDecoder - - -class TomlOrderedDecoder(TomlDecoder): - - def __init__(self): - super(self.__class__, self).__init__(_dict=OrderedDict) - - -class TomlOrderedEncoder(TomlEncoder): - - def __init__(self): - super(self.__class__, self).__init__(_dict=OrderedDict) diff --git a/venv/Lib/site-packages/pip/_vendor/toml/tz.py b/venv/Lib/site-packages/pip/_vendor/toml/tz.py deleted file mode 100644 index 93c3c8ad262fb2a024dc08ea5fd44040efd5a7c7..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pip/_vendor/toml/tz.py +++ /dev/null @@ -1,21 +0,0 @@ -from datetime import tzinfo, timedelta - - -class TomlTz(tzinfo): - def __init__(self, toml_offset): - if toml_offset == "Z": - self._raw_offset = "+00:00" - else: - self._raw_offset = toml_offset - self._sign = -1 if self._raw_offset[0] == '-' else 1 - self._hours = int(self._raw_offset[1:3]) - self._minutes = int(self._raw_offset[4:6]) - - def tzname(self, dt): - return "UTC" + self._raw_offset - - def utcoffset(self, dt): - return self._sign * timedelta(hours=self._hours, minutes=self._minutes) - - def dst(self, dt): - return timedelta(0) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py index 9bd8323f91e8daf72b841633f4706a0167c37330..fe86b59d782bdb09d70aa44f80370be95a667c83 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py @@ -1,28 +1,27 @@ """ -urllib3 - Thread-safe connection pooling and re-using. +Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more """ from __future__ import absolute_import -import warnings -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url +# Set default logging handler to avoid "No handler found" warnings. +import logging +import warnings +from logging import NullHandler from . import exceptions +from ._version import __version__ +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url from .filepost import encode_multipart_formdata from .poolmanager import PoolManager, ProxyManager, proxy_from_url from .response import HTTPResponse from .util.request import make_headers -from .util.url import get_host -from .util.timeout import Timeout from .util.retry import Retry - - -# Set default logging handler to avoid "No handler found" warnings. -import logging -from logging import NullHandler +from .util.timeout import Timeout +from .util.url import get_host __author__ = "Andrey Petrov (andrey.petrov@shazow.net)" __license__ = "MIT" -__version__ = "1.25.8" +__version__ = __version__ __all__ = ( "HTTPConnectionPool", diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py b/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py index 019d1511d56b977ac3b396100d2d3ce353acea45..da9857e986d89acac3ba05a6735dc08c249bde1a 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py @@ -17,9 +17,10 @@ except ImportError: # Platform-specific: No threads available from collections import OrderedDict -from .exceptions import InvalidHeader -from .packages.six import iterkeys, itervalues, PY3 +from .exceptions import InvalidHeader +from .packages import six +from .packages.six import iterkeys, itervalues __all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"] @@ -174,7 +175,7 @@ class HTTPHeaderDict(MutableMapping): def __ne__(self, other): return not self.__eq__(other) - if not PY3: # Python 2 + if six.PY2: # Python 2 iterkeys = MutableMapping.iterkeys itervalues = MutableMapping.itervalues @@ -190,7 +191,7 @@ class HTTPHeaderDict(MutableMapping): def pop(self, key, default=__marker): """D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. + If key is not found, d is returned if given, otherwise KeyError is raised. """ # Using the MutableMapping function directly fails due to the private marker. # Using ordinary dict.pop would expose the internal structures. diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py b/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py index 71e6790b1b937beed6a37382a6794b0905939dde..60f70f794a11d0458a110f7d3e21a161a325258a 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/connection.py @@ -1,13 +1,18 @@ from __future__ import absolute_import + import datetime import logging import os +import re import socket -from socket import error as SocketError, timeout as SocketTimeout import warnings +from socket import error as SocketError +from socket import timeout as SocketTimeout + from .packages import six from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection from .packages.six.moves.http_client import HTTPException # noqa: F401 +from .util.proxy import create_proxy_ssl_context try: # Compiled with SSL? import ssl @@ -29,64 +34,69 @@ except NameError: pass +try: # Python 3: + # Not a no-op, we're adding this to the namespace so it can be imported. + BrokenPipeError = BrokenPipeError +except NameError: # Python 2: + + class BrokenPipeError(Exception): + pass + + +from ._collections import HTTPHeaderDict # noqa (historical, removed in v2) +from ._version import __version__ from .exceptions import ( - NewConnectionError, ConnectTimeoutError, + NewConnectionError, SubjectAltNameWarning, SystemTimeWarning, ) -from .packages.ssl_match_hostname import match_hostname, CertificateError - +from .packages.ssl_match_hostname import CertificateError, match_hostname +from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection from .util.ssl_ import ( - resolve_cert_reqs, - resolve_ssl_version, assert_fingerprint, create_urllib3_context, + is_ipaddress, + resolve_cert_reqs, + resolve_ssl_version, ssl_wrap_socket, ) - -from .util import connection - -from ._collections import HTTPHeaderDict - log = logging.getLogger(__name__) port_by_scheme = {"http": 80, "https": 443} # When it comes time to update this value as a part of regular maintenance # (ie test_recent_date is failing) update it to ~6 months before the current date. -RECENT_DATE = datetime.date(2019, 1, 1) - - -class DummyConnection(object): - """Used to detect a failed ConnectionCls import.""" +RECENT_DATE = datetime.date(2020, 7, 1) - pass +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") class HTTPConnection(_HTTPConnection, object): """ - Based on httplib.HTTPConnection but provides an extra constructor + Based on :class:`http.client.HTTPConnection` but provides an extra constructor backwards-compatibility layer between older and newer Pythons. Additional keyword parameters are used to configure attributes of the connection. Accepted parameters include: - - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` - - ``source_address``: Set the source address for the current connection. - - ``socket_options``: Set specific options on the underlying socket. If not specified, then - defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling - Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. - For example, if you wish to enable TCP Keep Alive in addition to the defaults, - you might pass:: + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass: - HTTPConnection.default_socket_options + [ - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), - ] + .. code-block:: python - Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). """ default_port = port_by_scheme["http"] @@ -98,6 +108,10 @@ class HTTPConnection(_HTTPConnection, object): #: Whether this connection verifies the host's certificate. is_verified = False + #: Whether this proxy connection (if used) verifies the proxy host's + #: certificate. + proxy_is_verified = None + def __init__(self, *args, **kw): if not six.PY2: kw.pop("strict", None) @@ -109,6 +123,10 @@ class HTTPConnection(_HTTPConnection, object): #: provided, we use the default options. self.socket_options = kw.pop("socket_options", self.default_socket_options) + # Proxy options provided by the user. + self.proxy = kw.pop("proxy", None) + self.proxy_config = kw.pop("proxy_config", None) + _HTTPConnection.__init__(self, *args, **kw) @property @@ -141,7 +159,7 @@ class HTTPConnection(_HTTPConnection, object): self._dns_host = value def _new_conn(self): - """ Establish a socket connection and set nodelay settings on it. + """Establish a socket connection and set nodelay settings on it. :return: New socket connection. """ @@ -171,10 +189,13 @@ class HTTPConnection(_HTTPConnection, object): return conn + def _is_using_tunnel(self): + # Google App Engine's httplib does not define _tunnel_host + return getattr(self, "_tunnel_host", None) + def _prepare_conn(self, conn): self.sock = conn - # Google App Engine's httplib does not define _tunnel_host - if getattr(self, "_tunnel_host", None): + if self._is_using_tunnel(): # TODO: Fix tunnel so it doesn't depend on self.sock state. self._tunnel() # Mark this connection as not reusable @@ -184,20 +205,56 @@ class HTTPConnection(_HTTPConnection, object): conn = self._new_conn() self._prepare_conn(conn) + def putrequest(self, method, url, *args, **kwargs): + """ """ + # Empty docstring because the indentation of CPython's implementation + # is broken but we don't want this method in our documentation. + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + "Method cannot contain non-token characters %r (found at least %r)" + % (method, match.group()) + ) + + return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) + + def putheader(self, header, *values): + """ """ + if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): + _HTTPConnection.putheader(self, header, *values) + elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS: + raise ValueError( + "urllib3.util.SKIP_HEADER only supports '%s'" + % ("', '".join(map(str.title, sorted(SKIPPABLE_HEADERS))),) + ) + + def request(self, method, url, body=None, headers=None): + if headers is None: + headers = {} + else: + # Avoid modifying the headers passed into .request() + headers = headers.copy() + if "user-agent" not in (six.ensure_str(k.lower()) for k in headers): + headers["User-Agent"] = _get_default_user_agent() + super(HTTPConnection, self).request(method, url, body=body, headers=headers) + def request_chunked(self, method, url, body=None, headers=None): """ Alternative to the common request method, which sends the body with chunked encoding and not as one block """ - headers = HTTPHeaderDict(headers if headers is not None else {}) - skip_accept_encoding = "accept-encoding" in headers - skip_host = "host" in headers + headers = headers or {} + header_keys = set([six.ensure_str(k.lower()) for k in headers]) + skip_accept_encoding = "accept-encoding" in header_keys + skip_host = "host" in header_keys self.putrequest( method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host ) + if "user-agent" not in header_keys: + self.putheader("User-Agent", _get_default_user_agent()) for header, value in headers.items(): self.putheader(header, value) - if "transfer-encoding" not in headers: + if "transfer-encoding" not in header_keys: self.putheader("Transfer-Encoding", "chunked") self.endheaders() @@ -211,19 +268,31 @@ class HTTPConnection(_HTTPConnection, object): if not isinstance(chunk, bytes): chunk = chunk.encode("utf8") len_str = hex(len(chunk))[2:] - self.send(len_str.encode("utf-8")) - self.send(b"\r\n") - self.send(chunk) - self.send(b"\r\n") + to_send = bytearray(len_str.encode()) + to_send += b"\r\n" + to_send += chunk + to_send += b"\r\n" + self.send(to_send) # After the if clause, to always have a closed body self.send(b"0\r\n\r\n") class HTTPSConnection(HTTPConnection): + """ + Many of the parameters to this constructor are passed to the underlying SSL + socket by means of :py:func:`urllib3.util.ssl_wrap_socket`. + """ + default_port = port_by_scheme["https"] + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ca_cert_data = None ssl_version = None + assert_fingerprint = None + tls_in_tls_required = False def __init__( self, @@ -251,19 +320,6 @@ class HTTPSConnection(HTTPConnection): # HTTPS requests to go out as HTTP. (See Issue #356) self._protocol = "https" - -class VerifiedHTTPSConnection(HTTPSConnection): - """ - Based on httplib.HTTPSConnection but wraps the socket with - SSL certification. - """ - - cert_reqs = None - ca_certs = None - ca_cert_dir = None - ssl_version = None - assert_fingerprint = None - def set_cert( self, key_file=None, @@ -274,6 +330,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): assert_hostname=None, assert_fingerprint=None, ca_cert_dir=None, + ca_cert_data=None, ): """ This method should only be called once, before the connection is used. @@ -294,15 +351,21 @@ class VerifiedHTTPSConnection(HTTPSConnection): self.assert_fingerprint = assert_fingerprint self.ca_certs = ca_certs and os.path.expanduser(ca_certs) self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data def connect(self): # Add certificate verification conn = self._new_conn() hostname = self.host + tls_in_tls = False + + if self._is_using_tunnel(): + if self.tls_in_tls_required: + conn = self._connect_tls_proxy(hostname, conn) + tls_in_tls = True - # Google App Engine's httplib does not define _tunnel_host - if getattr(self, "_tunnel_host", None): self.sock = conn + # Calls self._set_hostport(), so self.host is # self._tunnel_host below. self._tunnel() @@ -344,6 +407,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): if ( not self.ca_certs and not self.ca_cert_dir + and not self.ca_cert_data and default_ssl_context and hasattr(context, "load_default_certs") ): @@ -356,10 +420,29 @@ class VerifiedHTTPSConnection(HTTPSConnection): key_password=self.key_password, ca_certs=self.ca_certs, ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, server_hostname=server_hostname, ssl_context=context, + tls_in_tls=tls_in_tls, ) + # If we're using all defaults and the connection + # is TLSv1 or TLSv1.1 we throw a DeprecationWarning + # for the host. + if ( + default_ssl_context + and self.ssl_version is None + and hasattr(self.sock, "version") + and self.sock.version() in {"TLSv1", "TLSv1.1"} + ): + warnings.warn( + "Negotiating TLSv1/TLSv1.1 by default is deprecated " + "and will be disabled in urllib3 v2.0.0. Connecting to " + "'%s' with '%s' can be enabled by explicitly opting-in " + "with 'ssl_version'" % (self.host, self.sock.version()), + DeprecationWarning, + ) + if self.assert_fingerprint: assert_fingerprint( self.sock.getpeercert(binary_form=True), self.assert_fingerprint @@ -390,8 +473,71 @@ class VerifiedHTTPSConnection(HTTPSConnection): or self.assert_fingerprint is not None ) + def _connect_tls_proxy(self, hostname, conn): + """ + Establish a TLS connection to the proxy using the provided SSL context. + """ + proxy_config = self.proxy_config + ssl_context = proxy_config.ssl_context + if ssl_context: + # If the user provided a proxy context, we assume CA and client + # certificates have already been set + return ssl_wrap_socket( + sock=conn, + server_hostname=hostname, + ssl_context=ssl_context, + ) + + ssl_context = create_proxy_ssl_context( + self.ssl_version, + self.cert_reqs, + self.ca_certs, + self.ca_cert_dir, + self.ca_cert_data, + ) + + # If no cert was provided, use only the default options for server + # certificate validation + socket = ssl_wrap_socket( + sock=conn, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + server_hostname=hostname, + ssl_context=ssl_context, + ) + + if ssl_context.verify_mode != ssl.CERT_NONE and not getattr( + ssl_context, "check_hostname", False + ): + # While urllib3 attempts to always turn off hostname matching from + # the TLS library, this cannot always be done. So we check whether + # the TLS Library still thinks it's matching hostnames. + cert = socket.getpeercert() + if not cert.get("subjectAltName", ()): + warnings.warn( + ( + "Certificate for {0} has no `subjectAltName`, falling back to check for a " + "`commonName` for now. This feature is being removed by major browsers and " + "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " + "for details.)".format(hostname) + ), + SubjectAltNameWarning, + ) + _match_hostname(cert, hostname) + + self.proxy_is_verified = ssl_context.verify_mode == ssl.CERT_REQUIRED + return socket + def _match_hostname(cert, asserted_hostname): + # Our upstream implementation of ssl.match_hostname() + # only applies this normalization to IP addresses so it doesn't + # match DNS SANs so we do the same thing! + stripped_hostname = asserted_hostname.strip("u[]") + if is_ipaddress(stripped_hostname): + asserted_hostname = stripped_hostname + try: match_hostname(cert, asserted_hostname) except CertificateError as e: @@ -406,9 +552,18 @@ def _match_hostname(cert, asserted_hostname): raise -if ssl: - # Make a copy for testing. - UnverifiedHTTPSConnection = HTTPSConnection - HTTPSConnection = VerifiedHTTPSConnection -else: - HTTPSConnection = DummyConnection +def _get_default_user_agent(): + return "python-urllib3/%s" % __version__ + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + + pass + + +if not ssl: + HTTPSConnection = DummyConnection # noqa: F811 + + +VerifiedHTTPSConnection = HTTPSConnection diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py b/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py index d42eb7be67371c56dbd811708fc3049b38258dd4..8dccf4bc2a8d0cb9bf27ad51a5fc4ad24808412a 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py @@ -1,57 +1,53 @@ from __future__ import absolute_import + import errno import logging +import socket import sys import warnings +from socket import error as SocketError +from socket import timeout as SocketTimeout -from socket import error as SocketError, timeout as SocketTimeout -import socket - - +from .connection import ( + BaseSSLError, + BrokenPipeError, + DummyConnection, + HTTPConnection, + HTTPException, + HTTPSConnection, + VerifiedHTTPSConnection, + port_by_scheme, +) from .exceptions import ( ClosedPoolError, - ProtocolError, EmptyPoolError, HeaderParsingError, HostChangedError, + InsecureRequestWarning, LocationValueError, MaxRetryError, + NewConnectionError, + ProtocolError, ProxyError, ReadTimeoutError, SSLError, TimeoutError, - InsecureRequestWarning, - NewConnectionError, ) -from .packages.ssl_match_hostname import CertificateError from .packages import six from .packages.six.moves import queue -from .connection import ( - port_by_scheme, - DummyConnection, - HTTPConnection, - HTTPSConnection, - VerifiedHTTPSConnection, - HTTPException, - BaseSSLError, -) +from .packages.ssl_match_hostname import CertificateError from .request import RequestMethods from .response import HTTPResponse - from .util.connection import is_connection_dropped +from .util.proxy import connection_requires_http_tunnel +from .util.queue import LifoQueue from .util.request import set_file_position from .util.response import assert_header_parsing from .util.retry import Retry from .util.timeout import Timeout -from .util.url import ( - get_host, - parse_url, - Url, - _normalize_host as normalize_host, - _encode_target, -) -from .util.queue import LifoQueue - +from .util.url import Url, _encode_target +from .util.url import _normalize_host as normalize_host +from .util.url import get_host, parse_url xrange = six.moves.xrange @@ -65,6 +61,11 @@ class ConnectionPool(object): """ Base class for all connection pools, such as :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + + .. note:: + ConnectionPool.urlopen() does not normalize or percent-encode target URIs + which is useful if your target server doesn't support percent-encoded + target URIs. """ scheme = None @@ -106,16 +107,16 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param host: Host used for this HTTP Connection (e.g. "localhost"), passed into - :class:`httplib.HTTPConnection`. + :class:`http.client.HTTPConnection`. :param port: Port used for this HTTP Connection (None is equivalent to 80), passed - into :class:`httplib.HTTPConnection`. + into :class:`http.client.HTTPConnection`. :param strict: Causes BadStatusLine to be raised if the status line can't be parsed as a valid HTTP/1.0 or 1.1 status line, passed into - :class:`httplib.HTTPConnection`. + :class:`http.client.HTTPConnection`. .. note:: Only works in Python 2. This parameter is ignored in Python 3. @@ -149,11 +150,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param _proxy: Parsed proxy URL, should not be used directly, instead, see - :class:`urllib3.connectionpool.ProxyManager`" + :class:`urllib3.ProxyManager` :param _proxy_headers: A dictionary with proxy headers, should not be used directly, - instead, see :class:`urllib3.connectionpool.ProxyManager`" + instead, see :class:`urllib3.ProxyManager` :param \\**conn_kw: Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, @@ -176,6 +177,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): retries=None, _proxy=None, _proxy_headers=None, + _proxy_config=None, **conn_kw ): ConnectionPool.__init__(self, host, port) @@ -197,6 +199,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): self.proxy = _proxy self.proxy_headers = _proxy_headers or {} + self.proxy_config = _proxy_config # Fill the queue up so that doing get() on it will block properly for _ in xrange(maxsize): @@ -213,6 +216,9 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # list. self.conn_kw.setdefault("socket_options", []) + self.conn_kw["proxy"] = self.proxy + self.conn_kw["proxy_config"] = self.proxy_config + def _new_conn(self): """ Return a fresh :class:`HTTPConnection`. @@ -267,7 +273,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): conn.close() if getattr(conn, "auto_open", 1) == 0: # This is a proxied connection that has been mutated by - # httplib._tunnel() and cannot be reused (since it would + # http.client._tunnel() and cannot be reused (since it would # attempt to bypass the proxy) conn = None @@ -312,7 +318,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): pass def _get_timeout(self, timeout): - """ Helper that always returns a :class:`urllib3.util.Timeout` """ + """Helper that always returns a :class:`urllib3.util.Timeout`""" if timeout is _Default: return self.timeout.clone() @@ -379,12 +385,30 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) raise - # conn.request() calls httplib.*.request, not the method in + # conn.request() calls http.client.*.request, not the method in # urllib3.request. It also calls makefile (recv) on the socket. - if chunked: - conn.request_chunked(method, url, **httplib_request_kw) - else: - conn.request(method, url, **httplib_request_kw) + try: + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: + conn.request(method, url, **httplib_request_kw) + + # We are swallowing BrokenPipeError (errno.EPIPE) since the server is + # legitimately able to close the connection after sending a valid response. + # With this behaviour, the received response is still readable. + except BrokenPipeError: + # Python 3 + pass + except IOError as e: + # Python 2 and macOS/Linux + # EPIPE and ESHUTDOWN are BrokenPipeError on Python 2, and EPROTOTYPE is needed on macOS + # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ + if e.errno not in { + errno.EPIPE, + errno.ESHUTDOWN, + errno.EPROTOTYPE, + }: + raise # Reset the timeout for the recv() on the socket read_timeout = timeout_obj.read_timeout @@ -527,10 +551,12 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param method: HTTP request method (such as GET, POST, PUT, etc.) + :param url: + The URL to perform the request on. + :param body: - Data to send in the request body (useful for creating - POST requests, see HTTPConnectionPool.post_url for - more convenience). + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. :param headers: Dictionary of custom headers to send, such as User-Agent, @@ -560,7 +586,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param assert_same_host: If ``True``, will make sure that the host of the pool requests is - consistent else will raise HostChangedError. When False, you can + consistent else will raise HostChangedError. When ``False``, you can use the pool on an HTTP proxy and request foreign hosts. :param timeout: @@ -597,6 +623,10 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): Additional parameters are passed to :meth:`urllib3.response.HTTPResponse.from_httplib` """ + + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + if headers is None: headers = self.headers @@ -614,7 +644,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): if url.startswith("/"): url = six.ensure_str(_encode_target(url)) else: - url = six.ensure_str(parse_url(url).url) + url = six.ensure_str(parsed_url.url) conn = None @@ -629,10 +659,14 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # [1] release_this_conn = release_conn - # Merge the proxy headers. Only do this in HTTP. We have to copy the - # headers dict so we can safely change it without those changes being - # reflected in anyone else's copy. - if self.scheme == "http": + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: headers = headers.copy() headers.update(self.proxy_headers) @@ -658,7 +692,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): is_new_proxy_conn = self.proxy is not None and not getattr( conn, "sock", None ) - if is_new_proxy_conn: + if is_new_proxy_conn and http_tunnel_required: self._prepare_proxy(conn) # Make the request on the httplib connection object. @@ -693,9 +727,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # Everything went great! clean_exit = True - except queue.Empty: - # Timed out by queue. - raise EmptyPoolError(self, "No pool connections are available.") + except EmptyPoolError: + # Didn't get a connection from the pool, no need to clean up + clean_exit = True + release_this_conn = False + raise except ( TimeoutError, @@ -760,21 +796,6 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): **response_kw ) - def drain_and_release_conn(response): - try: - # discard any remaining response body, the connection will be - # released back to the pool once the entire response is read - response.read() - except ( - TimeoutError, - HTTPException, - SocketError, - ProtocolError, - BaseSSLError, - SSLError, - ): - pass - # Handle redirect? redirect_location = redirect and response.get_redirect_location() if redirect_location: @@ -785,15 +806,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): retries = retries.increment(method, url, response=response, _pool=self) except MaxRetryError: if retries.raise_on_redirect: - # Drain and release the connection for this response, since - # we're not returning it to be released manually. - drain_and_release_conn(response) + response.drain_conn() raise return response - # drain and return the connection to the pool before recursing - drain_and_release_conn(response) - + response.drain_conn() retries.sleep_for_retry(response) log.debug("Redirecting %s -> %s", url, redirect_location) return self.urlopen( @@ -819,15 +836,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): retries = retries.increment(method, url, response=response, _pool=self) except MaxRetryError: if retries.raise_on_status: - # Drain and release the connection for this response, since - # we're not returning it to be released manually. - drain_and_release_conn(response) + response.drain_conn() raise return response - # drain and return the connection to the pool before recursing - drain_and_release_conn(response) - + response.drain_conn() retries.sleep(response) log.debug("Retry: %s", url) return self.urlopen( @@ -853,11 +866,7 @@ class HTTPSConnectionPool(HTTPConnectionPool): """ Same as :class:`.HTTPConnectionPool`, but HTTPS. - When Python is compiled with the :mod:`ssl` module, then - :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, - instead of :class:`.HTTPSConnection`. - - :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, + :class:`.HTTPSConnection` uses one of ``assert_fingerprint``, ``assert_hostname`` and ``host`` in this order to verify connections. If ``assert_hostname`` is False, no verification is done. @@ -941,15 +950,22 @@ class HTTPSConnectionPool(HTTPConnectionPool): def _prepare_proxy(self, conn): """ - Establish tunnel connection early, because otherwise httplib - would improperly set Host: header to proxy's IP:port. + Establishes a tunnel connection through HTTP CONNECT. + + Tunnel connection is established early because otherwise httplib would + improperly set Host: header to proxy's IP:port. """ + conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers) + + if self.proxy.scheme == "https": + conn.tls_in_tls_required = True + conn.connect() def _new_conn(self): """ - Return a fresh :class:`httplib.HTTPSConnection`. + Return a fresh :class:`http.client.HTTPSConnection`. """ self.num_connections += 1 log.debug( @@ -998,12 +1014,23 @@ class HTTPSConnectionPool(HTTPConnectionPool): ( "Unverified HTTPS request is being made to host '%s'. " "Adding certificate verification is strongly advised. See: " - "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" "#ssl-warnings" % conn.host ), InsecureRequestWarning, ) + if getattr(conn, "proxy_is_verified", None) is False: + warnings.warn( + ( + "Unverified HTTPS connection done to an HTTPS proxy. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#ssl-warnings" + ), + InsecureRequestWarning, + ) + def connection_from_url(url, **kw): """ diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py index d9b6733318812d4e72b601c770beff4d4ecd6923..42526be7f55fa9640a4f7ccc245f7299f9483e6b 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py @@ -32,30 +32,26 @@ license and by oscrypto's: from __future__ import absolute_import import platform -from ctypes.util import find_library from ctypes import ( - c_void_p, - c_int32, + CDLL, + CFUNCTYPE, + POINTER, + c_bool, + c_byte, c_char_p, + c_int32, + c_long, c_size_t, - c_byte, c_uint32, c_ulong, - c_long, - c_bool, + c_void_p, ) -from ctypes import CDLL, POINTER, CFUNCTYPE - - -security_path = find_library("Security") -if not security_path: - raise ImportError("The library Security could not be found") - +from ctypes.util import find_library -core_foundation_path = find_library("CoreFoundation") -if not core_foundation_path: - raise ImportError("The library CoreFoundation could not be found") +from pip._vendor.urllib3.packages.six import raise_from +if platform.system() != "Darwin": + raise ImportError("Only macOS is supported") version = platform.mac_ver()[0] version_info = tuple(map(int, version.split("."))) @@ -65,8 +61,31 @@ if version_info < (10, 8): % (version_info[0], version_info[1]) ) -Security = CDLL(security_path, use_errno=True) -CoreFoundation = CDLL(core_foundation_path, use_errno=True) + +def load_cdll(name, macos10_16_path): + """Loads a CDLL by name, falling back to known path on 10.16+""" + try: + # Big Sur is technically 11 but we use 10.16 due to the Big Sur + # beta being labeled as 10.16. + if version_info >= (10, 16): + path = macos10_16_path + else: + path = find_library(name) + if not path: + raise OSError # Caught and reraised as 'ImportError' + return CDLL(path, use_errno=True) + except OSError: + raise_from(ImportError("The library %s failed to load" % name), None) + + +Security = load_cdll( + "Security", "/System/Library/Frameworks/Security.framework/Security" +) +CoreFoundation = load_cdll( + "CoreFoundation", + "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", +) + Boolean = c_bool CFIndex = c_long @@ -276,6 +295,13 @@ try: Security.SSLSetProtocolVersionMax.argtypes = [SSLContextRef, SSLProtocol] Security.SSLSetProtocolVersionMax.restype = OSStatus + try: + Security.SSLSetALPNProtocols.argtypes = [SSLContextRef, CFArrayRef] + Security.SSLSetALPNProtocols.restype = OSStatus + except AttributeError: + # Supported only in 10.12+ + pass + Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] Security.SecCopyErrorMessageString.restype = CFStringRef diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py index e60168cac14d080b45ac693b2ceda1ec59f712b5..fa0b245d279e96724d5610f93bc3b3c8c22ca032 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py @@ -10,13 +10,13 @@ appropriate and useful assistance to the higher-level code. import base64 import ctypes import itertools -import re import os +import re import ssl +import struct import tempfile -from .bindings import Security, CoreFoundation, CFConst - +from .bindings import CFConst, CoreFoundation, Security # This regular expression is used to grab PEM data out of a PEM bundle. _PEM_CERTS_RE = re.compile( @@ -56,6 +56,51 @@ def _cf_dictionary_from_tuples(tuples): ) +def _cfstr(py_bstr): + """ + Given a Python binary data, create a CFString. + The string must be CFReleased by the caller. + """ + c_str = ctypes.c_char_p(py_bstr) + cf_str = CoreFoundation.CFStringCreateWithCString( + CoreFoundation.kCFAllocatorDefault, + c_str, + CFConst.kCFStringEncodingUTF8, + ) + return cf_str + + +def _create_cfstring_array(lst): + """ + Given a list of Python binary data, create an associated CFMutableArray. + The array must be CFReleased by the caller. + + Raises an ssl.SSLError on failure. + """ + cf_arr = None + try: + cf_arr = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), + ) + if not cf_arr: + raise MemoryError("Unable to allocate memory!") + for item in lst: + cf_str = _cfstr(item) + if not cf_str: + raise MemoryError("Unable to allocate memory!") + try: + CoreFoundation.CFArrayAppendValue(cf_arr, cf_str) + finally: + CoreFoundation.CFRelease(cf_str) + except BaseException as e: + if cf_arr: + CoreFoundation.CFRelease(cf_arr) + raise ssl.SSLError("Unable to allocate array: %s" % (e,)) + return cf_arr + + def _cf_string_to_unicode(value): """ Creates a Unicode string from a CFString object. Used entirely for error @@ -143,6 +188,7 @@ def _cert_array_from_pem(pem_bundle): # We only want to do that if an error occurs: otherwise, the caller # should free. CoreFoundation.CFRelease(cert_array) + raise return cert_array @@ -326,3 +372,26 @@ def _load_client_cert_chain(keychain, *paths): finally: for obj in itertools.chain(identities, certificates): CoreFoundation.CFRelease(obj) + + +TLS_PROTOCOL_VERSIONS = { + "SSLv2": (0, 2), + "SSLv3": (3, 0), + "TLSv1": (3, 1), + "TLSv1.1": (3, 2), + "TLSv1.2": (3, 3), +} + + +def _build_tls_unknown_ca_alert(version): + """ + Builds a TLS alert record for an unknown CA. + """ + ver_maj, ver_min = TLS_PROTOCOL_VERSIONS[version] + severity_fatal = 0x02 + description_unknown_ca = 0x30 + msg = struct.pack(">BB", severity_fatal, description_unknown_ca) + msg_len = len(msg) + record_type_alert = 0x15 + record = struct.pack(">BBBH", record_type_alert, ver_maj, ver_min, msg_len) + msg + return record diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py index d09d2be645a4690b75e795b309e63d78b8ce8e6d..668538695f96d7eccf8bc83f551aa5808efab1f9 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py @@ -39,24 +39,24 @@ urllib3 on Google App Engine: """ from __future__ import absolute_import + import io import logging import warnings -from ..packages.six.moves.urllib.parse import urljoin from ..exceptions import ( HTTPError, HTTPWarning, MaxRetryError, ProtocolError, - TimeoutError, SSLError, + TimeoutError, ) - +from ..packages.six.moves.urllib.parse import urljoin from ..request import RequestMethods from ..response import HTTPResponse -from ..util.timeout import Timeout from ..util.retry import Retry +from ..util.timeout import Timeout from . import _appengine_environ try: @@ -90,7 +90,7 @@ class AppEngineManager(RequestMethods): * If you attempt to use this on App Engine Flexible, as full socket support is available. * If a request size is more than 10 megabytes. - * If a response size is more than 32 megabtyes. + * If a response size is more than 32 megabytes. * If you use an unsupported request method such as OPTIONS. Beyond those cases, it will raise normal urllib3 errors. @@ -111,7 +111,7 @@ class AppEngineManager(RequestMethods): warnings.warn( "urllib3 is using URLFetch on Google App Engine sandbox instead " "of sockets. To use sockets directly instead of URLFetch see " - "https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", + "https://urllib3.readthedocs.io/en/1.26.x/reference/urllib3.contrib.html.", AppEnginePlatformWarning, ) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py index 1fd242a6e0dcd9dda69b2520a85b9687e1333edb..41a8fd174cbc556d495aca1d58af8e2197ace913 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py @@ -5,12 +5,21 @@ Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 """ from __future__ import absolute_import +import warnings from logging import getLogger + from ntlm import ntlm from .. import HTTPSConnectionPool from ..packages.six.moves.http_client import HTTPSConnection +warnings.warn( + "The 'urllib3.contrib.ntlmpool' module is deprecated and will be removed " + "in urllib3 v2.0 release, urllib3 is not able to support it properly due " + "to reasons listed in issue: https://github.com/urllib3/urllib3/issues/2282. " + "If you are a user of this module please comment in the mentioned issue.", + DeprecationWarning, +) log = getLogger(__name__) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py index fc99d34bd4cd771decb344145f7e71dc20655738..3130f51ac060cd5da6083fc96c3ab2c6c56a7342 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py @@ -1,31 +1,35 @@ """ -SSL with SNI_-support for Python 2. Follow these instructions if you would -like to verify SSL certificates in Python 2. Note, the default libraries do +TLS with SNI_-support for Python 2. Follow these instructions if you would +like to verify TLS certificates in Python 2. Note, the default libraries do *not* do certificate checking; you need to do additional work to validate certificates yourself. This needs the following packages installed: -* pyOpenSSL (tested with 16.0.0) -* cryptography (minimum 1.3.4, from pyopenssl) -* idna (minimum 2.0, from cryptography) +* `pyOpenSSL`_ (tested with 16.0.0) +* `cryptography`_ (minimum 1.3.4, from pyopenssl) +* `idna`_ (minimum 2.0, from cryptography) However, pyopenssl depends on cryptography, which depends on idna, so while we use all three directly here we end up having relatively few packages required. You can install them with the following command: - pip install pyopenssl cryptography idna +.. code-block:: bash + + $ python -m pip install pyopenssl cryptography idna To activate certificate checking, call :func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code before you begin making HTTP requests. This can be done in a ``sitecustomize`` module, or at any other time before your application begins using ``urllib3``, -like this:: +like this: + +.. code-block:: python try: - import urllib3.contrib.pyopenssl - urllib3.contrib.pyopenssl.inject_into_urllib3() + import pip._vendor.urllib3.contrib.pyopenssl as pyopenssl + pyopenssl.inject_into_urllib3() except ImportError: pass @@ -35,11 +39,11 @@ when the required modules are installed. Activating this module also has the positive side effect of disabling SSL/TLS compression in Python 2 (see `CRIME attack`_). -If you want to configure the default list of supported cipher suites, you can -set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. - .. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication .. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) +.. _pyopenssl: https://www.pyopenssl.org +.. _cryptography: https://cryptography.io +.. _idna: https://github.com/kjd/idna """ from __future__ import absolute_import @@ -56,8 +60,9 @@ except ImportError: pass -from socket import timeout, error as SocketError from io import BytesIO +from socket import error as SocketError +from socket import timeout try: # Platform-specific: Python 2 from socket import _fileobject @@ -67,11 +72,11 @@ except ImportError: # Platform-specific: Python 3 import logging import ssl -from ..packages import six import sys from .. import util - +from ..packages import six +from ..util.ssl_ import PROTOCOL_TLS_CLIENT __all__ = ["inject_into_urllib3", "extract_from_urllib3"] @@ -81,6 +86,7 @@ HAS_SNI = True # Map from urllib3 to PyOpenSSL compatible parameter-values. _openssl_versions = { util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, + PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, } @@ -450,9 +456,12 @@ class PyOpenSSLContext(object): cafile = cafile.encode("utf-8") if capath is not None: capath = capath.encode("utf-8") - self._ctx.load_verify_locations(cafile, capath) - if cadata is not None: - self._ctx.load_verify_locations(BytesIO(cadata)) + try: + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("unable to load trusted certificates: %r" % e) def load_cert_chain(self, certfile, keyfile=None, password=None): self._ctx.use_certificate_chain_file(certfile) @@ -462,6 +471,10 @@ class PyOpenSSLContext(object): self._ctx.set_passwd_cb(lambda *_: password) self._ctx.use_privatekey_file(keyfile or certfile) + def set_alpn_protocols(self, protocols): + protocols = [six.ensure_binary(p) for p in protocols] + return self._ctx.set_alpn_protos(protocols) + def wrap_socket( self, sock, diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py index 87d844afa78769f0304e4f5953cd3ff8733d0290..b4ca80b88be37143f2dbe9d8db3bf6911af78e60 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py @@ -19,8 +19,8 @@ contrib module. So...here we are. To use this module, simply import and inject it:: - import urllib3.contrib.securetransport - urllib3.contrib.securetransport.inject_into_urllib3() + import pip._vendor.urllib3.contrib.securetransport as securetransport + securetransport.inject_into_urllib3() Happy TLSing! @@ -29,6 +29,8 @@ library. An enormous debt is owed to him for blazing this trail for us. For that reason, this code should be considered to be covered both by urllib3's license and by oscrypto's: +.. code-block:: + Copyright (c) 2015-2016 Will Bond Permission is hereby granted, free of charge, to any person obtaining a @@ -58,16 +60,22 @@ import os.path import shutil import socket import ssl +import struct import threading import weakref +from pip._vendor import six + from .. import util -from ._securetransport.bindings import Security, SecurityConst, CoreFoundation +from ..util.ssl_ import PROTOCOL_TLS_CLIENT +from ._securetransport.bindings import CoreFoundation, Security, SecurityConst from ._securetransport.low_level import ( _assert_no_error, + _build_tls_unknown_ca_alert, _cert_array_from_pem, - _temporary_keychain, + _create_cfstring_array, _load_client_cert_chain, + _temporary_keychain, ) try: # Platform-specific: Python 2 @@ -147,7 +155,8 @@ CIPHER_SUITES = [ # TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. # TLSv1 to 1.2 are supported on macOS 10.8+ _protocol_to_min_max = { - util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12) + util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), + PROTOCOL_TLS_CLIENT: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), } if hasattr(ssl, "PROTOCOL_SSLv2"): @@ -374,16 +383,55 @@ class WrappedSocket(object): ) _assert_no_error(result) + def _set_alpn_protocols(self, protocols): + """ + Sets up the ALPN protocols on the context. + """ + if not protocols: + return + protocols_arr = _create_cfstring_array(protocols) + try: + result = Security.SSLSetALPNProtocols(self.context, protocols_arr) + _assert_no_error(result) + finally: + CoreFoundation.CFRelease(protocols_arr) + def _custom_validate(self, verify, trust_bundle): """ Called when we have set custom validation. We do this in two cases: first, when cert validation is entirely disabled; and second, when using a custom trust DB. + Raises an SSLError if the connection is not trusted. """ # If we disabled cert validation, just say: cool. if not verify: return + successes = ( + SecurityConst.kSecTrustResultUnspecified, + SecurityConst.kSecTrustResultProceed, + ) + try: + trust_result = self._evaluate_trust(trust_bundle) + if trust_result in successes: + return + reason = "error code: %d" % (trust_result,) + except Exception as e: + # Do not trust on error + reason = "exception: %r" % (e,) + + # SecureTransport does not send an alert nor shuts down the connection. + rec = _build_tls_unknown_ca_alert(self.version()) + self.socket.sendall(rec) + # close the connection immediately + # l_onoff = 1, activate linger + # l_linger = 0, linger for 0 seoncds + opts = struct.pack("ii", 1, 0) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, opts) + self.close() + raise ssl.SSLError("certificate verify failed, %s" % reason) + + def _evaluate_trust(self, trust_bundle): # We want data in memory, so load it up. if os.path.isfile(trust_bundle): with open(trust_bundle, "rb") as f: @@ -421,15 +469,7 @@ class WrappedSocket(object): if cert_array is not None: CoreFoundation.CFRelease(cert_array) - # Ok, now we can look at what the result was. - successes = ( - SecurityConst.kSecTrustResultUnspecified, - SecurityConst.kSecTrustResultProceed, - ) - if trust_result.value not in successes: - raise ssl.SSLError( - "certificate verify failed, error code: %d" % trust_result.value - ) + return trust_result.value def handshake( self, @@ -441,6 +481,7 @@ class WrappedSocket(object): client_cert, client_key, client_key_passphrase, + alpn_protocols, ): """ Actually performs the TLS handshake. This is run automatically by @@ -481,6 +522,9 @@ class WrappedSocket(object): # Setup the ciphers. self._set_ciphers() + # Setup the ALPN protocols. + self._set_alpn_protocols(alpn_protocols) + # Set the minimum and maximum TLS versions. result = Security.SSLSetProtocolVersionMin(self.context, min_version) _assert_no_error(result) @@ -754,6 +798,7 @@ class SecureTransportContext(object): self._client_cert = None self._client_key = None self._client_key_passphrase = None + self._alpn_protocols = None @property def check_hostname(self): @@ -819,6 +864,11 @@ class SecureTransportContext(object): if capath is not None: raise ValueError("SecureTransport does not support cert directories") + # Raise if cafile does not exist. + if cafile is not None: + with open(cafile): + pass + self._trust_bundle = cafile or cadata def load_cert_chain(self, certfile, keyfile=None, password=None): @@ -826,6 +876,18 @@ class SecureTransportContext(object): self._client_key = keyfile self._client_cert_passphrase = password + def set_alpn_protocols(self, protocols): + """ + Sets the ALPN protocols that will later be set on the context. + + Raises a NotImplementedError if ALPN is not supported. + """ + if not hasattr(Security, "SSLSetALPNProtocols"): + raise NotImplementedError( + "SecureTransport supports ALPN only in macOS 10.12+" + ) + self._alpn_protocols = [six.ensure_binary(p) for p in protocols] + def wrap_socket( self, sock, @@ -855,5 +917,6 @@ class SecureTransportContext(object): self._client_cert, self._client_key, self._client_key_passphrase, + self._alpn_protocols, ) return wrapped_socket diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py index 9e97f7aa98f4ccf380af4b9fc32999d01be8af8c..c326e80dd117458ff6e71741ca57359629b05ae4 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py @@ -14,22 +14,26 @@ supports the following SOCKS features: - SOCKS5 with local DNS (``proxy_url='socks5://...``) - Usernames and passwords for the SOCKS proxy - .. note:: - It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in - your ``proxy_url`` to ensure that DNS resolution is done from the remote - server instead of client-side when connecting to a domain name. +.. note:: + It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in + your ``proxy_url`` to ensure that DNS resolution is done from the remote + server instead of client-side when connecting to a domain name. SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 supports IPv4, IPv6, and domain names. When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` -will be sent as the ``userid`` section of the SOCKS request:: +will be sent as the ``userid`` section of the SOCKS request: + +.. code-block:: python proxy_url="socks4a://@proxy-host" When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion of the ``proxy_url`` will be sent as the username/password to authenticate -with the proxy:: +with the proxy: + +.. code-block:: python proxy_url="socks5h://:@proxy-host" @@ -40,19 +44,21 @@ try: import socks except ImportError: import warnings + from ..exceptions import DependencyWarning warnings.warn( ( "SOCKS support in urllib3 requires the installation of optional " "dependencies: specifically, PySocks. For more information, see " - "https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies" + "https://urllib3.readthedocs.io/en/1.26.x/contrib.html#socks-proxies" ), DependencyWarning, ) raise -from socket import error as SocketError, timeout as SocketTimeout +from socket import error as SocketError +from socket import timeout as SocketTimeout from ..connection import HTTPConnection, HTTPSConnection from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py b/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py index 0a74c79b5eaa972c895d3e857f02ced555c5d792..cba6f3f560f71b3b15ab6aaf21dde4f1bba1bd00 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py @@ -1,21 +1,24 @@ from __future__ import absolute_import + from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead # Base Exceptions class HTTPError(Exception): - "Base exception used by this module." + """Base exception used by this module.""" + pass class HTTPWarning(Warning): - "Base warning used by this module." + """Base warning used by this module.""" + pass class PoolError(HTTPError): - "Base exception for errors caused within a pool." + """Base exception for errors caused within a pool.""" def __init__(self, pool, message): self.pool = pool @@ -27,7 +30,7 @@ class PoolError(HTTPError): class RequestError(PoolError): - "Base exception for PoolErrors that have associated URLs." + """Base exception for PoolErrors that have associated URLs.""" def __init__(self, pool, url, message): self.url = url @@ -39,22 +42,28 @@ class RequestError(PoolError): class SSLError(HTTPError): - "Raised when SSL certificate fails in an HTTPS connection." + """Raised when SSL certificate fails in an HTTPS connection.""" + pass class ProxyError(HTTPError): - "Raised when the connection to a proxy fails." - pass + """Raised when the connection to a proxy fails.""" + + def __init__(self, message, error, *args): + super(ProxyError, self).__init__(message, error, *args) + self.original_error = error class DecodeError(HTTPError): - "Raised when automatic decoding based on Content-Type fails." + """Raised when automatic decoding based on Content-Type fails.""" + pass class ProtocolError(HTTPError): - "Raised when something unexpected happens mid-request/response." + """Raised when something unexpected happens mid-request/response.""" + pass @@ -84,7 +93,7 @@ class MaxRetryError(RequestError): class HostChangedError(RequestError): - "Raised when an existing pool gets a request for a foreign host." + """Raised when an existing pool gets a request for a foreign host.""" def __init__(self, pool, url, retries=3): message = "Tried to open a foreign host with url: %s" % url @@ -93,13 +102,13 @@ class HostChangedError(RequestError): class TimeoutStateError(HTTPError): - """ Raised when passing an invalid state to a timeout """ + """Raised when passing an invalid state to a timeout""" pass class TimeoutError(HTTPError): - """ Raised when a socket timeout error occurs. + """Raised when a socket timeout error occurs. Catching this error will catch both :exc:`ReadTimeoutErrors ` and :exc:`ConnectTimeoutErrors `. @@ -109,39 +118,45 @@ class TimeoutError(HTTPError): class ReadTimeoutError(TimeoutError, RequestError): - "Raised when a socket timeout occurs while receiving data from a server" + """Raised when a socket timeout occurs while receiving data from a server""" + pass # This timeout error does not have a URL attached and needs to inherit from the # base HTTPError class ConnectTimeoutError(TimeoutError): - "Raised when a socket timeout occurs while connecting to a server" + """Raised when a socket timeout occurs while connecting to a server""" + pass class NewConnectionError(ConnectTimeoutError, PoolError): - "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + """Raised when we fail to establish a new connection. Usually ECONNREFUSED.""" + pass class EmptyPoolError(PoolError): - "Raised when a pool runs out of connections and no more are allowed." + """Raised when a pool runs out of connections and no more are allowed.""" + pass class ClosedPoolError(PoolError): - "Raised when a request enters a pool after the pool has been closed." + """Raised when a request enters a pool after the pool has been closed.""" + pass class LocationValueError(ValueError, HTTPError): - "Raised when there is something wrong with a given URL input." + """Raised when there is something wrong with a given URL input.""" + pass class LocationParseError(LocationValueError): - "Raised when get_host or similar fails to parse the URL input." + """Raised when get_host or similar fails to parse the URL input.""" def __init__(self, location): message = "Failed to parse: %s" % location @@ -150,39 +165,56 @@ class LocationParseError(LocationValueError): self.location = location +class URLSchemeUnknown(LocationValueError): + """Raised when a URL input has an unsupported scheme.""" + + def __init__(self, scheme): + message = "Not supported URL scheme %s" % scheme + super(URLSchemeUnknown, self).__init__(message) + + self.scheme = scheme + + class ResponseError(HTTPError): - "Used as a container for an error reason supplied in a MaxRetryError." + """Used as a container for an error reason supplied in a MaxRetryError.""" + GENERIC_ERROR = "too many error responses" SPECIFIC_ERROR = "too many {status_code} error responses" class SecurityWarning(HTTPWarning): - "Warned when performing security reducing actions" + """Warned when performing security reducing actions""" + pass class SubjectAltNameWarning(SecurityWarning): - "Warned when connecting to a host with a certificate missing a SAN." + """Warned when connecting to a host with a certificate missing a SAN.""" + pass class InsecureRequestWarning(SecurityWarning): - "Warned when making an unverified HTTPS request." + """Warned when making an unverified HTTPS request.""" + pass class SystemTimeWarning(SecurityWarning): - "Warned when system time is suspected to be wrong" + """Warned when system time is suspected to be wrong""" + pass class InsecurePlatformWarning(SecurityWarning): - "Warned when certain SSL configuration is not available on a platform." + """Warned when certain TLS/SSL configuration is not available on a platform.""" + pass class SNIMissingWarning(HTTPWarning): - "Warned when making a HTTPS request without SNI available." + """Warned when making a HTTPS request without SNI available.""" + pass @@ -196,14 +228,15 @@ class DependencyWarning(HTTPWarning): class ResponseNotChunked(ProtocolError, ValueError): - "Response needs to be chunked in order to read it as chunks." + """Response needs to be chunked in order to read it as chunks.""" + pass class BodyNotHttplibCompatible(HTTPError): """ - Body should be httplib.HTTPResponse like (have an fp attribute which - returns raw chunks) for read_chunked(). + Body should be :class:`http.client.HTTPResponse` like + (have an fp attribute which returns raw chunks) for read_chunked(). """ pass @@ -213,9 +246,8 @@ class IncompleteRead(HTTPError, httplib_IncompleteRead): """ Response length doesn't match expected Content-Length - Subclass of http_client.IncompleteRead to allow int value - for `partial` to avoid creating large objects on streamed - reads. + Subclass of :class:`http.client.IncompleteRead` to allow int value + for ``partial`` to avoid creating large objects on streamed reads. """ def __init__(self, partial, expected): @@ -228,22 +260,57 @@ class IncompleteRead(HTTPError, httplib_IncompleteRead): ) +class InvalidChunkLength(HTTPError, httplib_IncompleteRead): + """Invalid chunk length in a chunked response.""" + + def __init__(self, response, length): + super(InvalidChunkLength, self).__init__( + response.tell(), response.length_remaining + ) + self.response = response + self.length = length + + def __repr__(self): + return "InvalidChunkLength(got length %r, %i bytes read)" % ( + self.length, + self.partial, + ) + + class InvalidHeader(HTTPError): - "The header provided was somehow invalid." + """The header provided was somehow invalid.""" + pass -class ProxySchemeUnknown(AssertionError, ValueError): - "ProxyManager does not support the supplied scheme" +class ProxySchemeUnknown(AssertionError, URLSchemeUnknown): + """ProxyManager does not support the supplied scheme""" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. def __init__(self, scheme): - message = "Not supported proxy scheme %s" % scheme + # 'localhost' is here because our URL parser parses + # localhost:8080 -> scheme=localhost, remove if we fix this. + if scheme == "localhost": + scheme = None + if scheme is None: + message = "Proxy URL had no scheme, should start with http:// or https://" + else: + message = ( + "Proxy URL had unsupported scheme %s, should use http:// or https://" + % scheme + ) super(ProxySchemeUnknown, self).__init__(message) +class ProxySchemeUnsupported(ValueError): + """Fetching HTTPS resources through HTTPS proxies is unsupported""" + + pass + + class HeaderParsingError(HTTPError): - "Raised by assert_header_parsing, but we convert it to a log.warning statement." + """Raised by assert_header_parsing, but we convert it to a log.warning statement.""" def __init__(self, defects, unparsed_data): message = "%s, unparsed data: %r" % (defects or "Unknown", unparsed_data) @@ -251,5 +318,6 @@ class HeaderParsingError(HTTPError): class UnrewindableBodyError(HTTPError): - "urllib3 encountered an error when trying to rewind a body" + """urllib3 encountered an error when trying to rewind a body""" + pass diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py b/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py index 8715b2202b0fcc8da2dc0f0e13f2eb0a95373de5..9d630f491d9a39644ae65564dac88eb51f0bbe78 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/fields.py @@ -1,4 +1,5 @@ from __future__ import absolute_import + import email.utils import mimetypes import re @@ -26,7 +27,8 @@ def format_header_param_rfc2231(name, value): strategy defined in RFC 2231. Particularly useful for header parameters which might contain - non-ASCII values, like file names. This follows RFC 2388 Section 4.4. + non-ASCII values, like file names. This follows + `RFC 2388 Section 4.4 `_. :param name: The name of the parameter, a string expected to be ASCII only. @@ -65,7 +67,6 @@ _HTML5_REPLACEMENTS = { u"\u0022": u"%22", # Replace "\" with "\\". u"\u005C": u"\u005C\u005C", - u"\u005C": u"\u005C\u005C", } # All control characters from 0x00 to 0x1F *except* 0x1B. diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py b/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py index b7b00992c652af19b101335a116317139c6cb996..36c9252c647e67bc7353c523152568b993c1331f 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py @@ -1,13 +1,13 @@ from __future__ import absolute_import + import binascii import codecs import os - from io import BytesIO +from .fields import RequestField from .packages import six from .packages.six import b -from .fields import RequestField writer = codecs.lookup("utf-8")[3] diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py index a3156a69c08d635f9969df59cdd9b3d898882c73..b8fb2154b6d0618b62281578e5e947bca487cee4 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py @@ -7,7 +7,6 @@ Backports the Python 3 ``socket.makefile`` method for use with anything that wants to create a "fake" socket object. """ import io - from socket import SocketIO diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py index 314424099f6240c4679d3b62685e4a38fe900449..ba50acb0624e1da7d613d8a700e313b46e76624e 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py @@ -1,4 +1,4 @@ -# Copyright (c) 2010-2019 Benjamin Peterson +# Copyright (c) 2010-2020 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.12.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -71,6 +71,11 @@ else: MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -182,6 +187,11 @@ class _SixMetaPathImporter(object): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -220,6 +230,12 @@ class _SixMetaPathImporter(object): get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + _importer = _SixMetaPathImporter(__name__) @@ -260,9 +276,19 @@ _moved_attributes = [ ), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), + MovedModule( + "collections_abc", + "collections", + "collections.abc" if sys.version_info >= (3, 3) else "collections", + ), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule( + "_dummy_thread", + "dummy_thread", + "_dummy_thread" if sys.version_info < (3, 9) else "_thread", + ), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), @@ -307,7 +333,9 @@ _moved_attributes = [ ] # Add windows specific modules. if sys.platform == "win32": - _moved_attributes += [MovedModule("winreg", "_winreg")] + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) @@ -476,7 +504,7 @@ class Module_six_moves_urllib_robotparser(_LazyModule): _urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser") + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), ] for attr in _urllib_robotparser_moved_attributes: setattr(Module_six_moves_urllib_robotparser, attr.name, attr) @@ -678,9 +706,11 @@ if PY3: if sys.version_info[1] <= 1: _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" else: _assertRaisesRegex = "assertRaisesRegex" _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" else: def b(s): @@ -707,6 +737,7 @@ else: _assertCountEqual = "assertItemsEqual" _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") @@ -723,6 +754,10 @@ def assertRegex(self, *args, **kwargs): return getattr(self, _assertRegex)(*args, **kwargs) +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") @@ -750,7 +785,7 @@ else: del frame elif _locs_ is None: _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") + exec ("""exec _code_ in _globs_, _locs_""") exec_( """def reraise(tp, value, tb=None): @@ -762,18 +797,7 @@ else: ) -if sys.version_info[:2] == (3, 2): - exec_( - """def raise_from(value, from_value): - try: - if from_value is None: - raise value - raise value from from_value - finally: - value = None -""" - ) -elif sys.version_info[:2] > (3, 2): +if sys.version_info[:2] > (3,): exec_( """def raise_from(value, from_value): try: @@ -863,19 +887,41 @@ if sys.version_info[:2] < (3, 3): _add_doc(reraise, """Reraise an exception.""") if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper( + wrapper, + wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES, + ): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ def wraps( wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES, ): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - - return wrapper + return functools.partial( + _update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated + ) + wraps.__doc__ = functools.wraps.__doc__ else: wraps = functools.wraps @@ -888,7 +934,15 @@ def with_metaclass(meta, *bases): # the actual metaclass. class metaclass(type): def __new__(cls, name, this_bases, d): - return meta(name, bases, d) + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d["__orig_bases__"] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) @classmethod def __prepare__(cls, name, this_bases): @@ -928,12 +982,11 @@ def ensure_binary(s, encoding="utf-8", errors="strict"): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) def ensure_str(s, encoding="utf-8", errors="strict"): @@ -947,12 +1000,15 @@ def ensure_str(s, encoding="utf-8", errors="strict"): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s @@ -977,7 +1033,7 @@ def ensure_text(s, encoding="utf-8", errors="strict"): def python_2_unicode_compatible(klass): """ - A decorator that defines __unicode__ and __str__ methods under Python 2. + A class decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py index 75b6bb1cf0d4f0925410dbaf04f3b2cabd8ade2f..ef3fde5206524962f0d4fd434d8101abde84bb14 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py @@ -1,19 +1,24 @@ import sys try: - # Our match_hostname function is the same as 3.5's, so we only want to + # Our match_hostname function is the same as 3.10's, so we only want to # import the match_hostname function if it's at least that good. - if sys.version_info < (3, 5): + # We also fallback on Python 3.10+ because our code doesn't emit + # deprecation warnings and is the same as Python 3.10 otherwise. + if sys.version_info < (3, 5) or sys.version_info >= (3, 10): raise ImportError("Fallback to vendored code") from ssl import CertificateError, match_hostname except ImportError: try: # Backport of the function from a pypi module - from backports.ssl_match_hostname import CertificateError, match_hostname + from backports.ssl_match_hostname import ( # type: ignore + CertificateError, + match_hostname, + ) except ImportError: # Our vendored copy - from ._implementation import CertificateError, match_hostname + from ._implementation import CertificateError, match_hostname # type: ignore # Not needed, but documenting what we provide. __all__ = ("CertificateError", "match_hostname") diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py index 5831c2e01d625a92f9fbd3e8e05ffadc166c512d..689208d3c63f1318e3a9a084a90e6b4532fa49bb 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py @@ -11,7 +11,7 @@ import sys # python-3.5) otherwise only do DNS matching. This allows # backports.ssl_match_hostname to continue to be used in Python 2.7. try: - from pip._vendor import ipaddress + import ipaddress except ImportError: ipaddress = None diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py b/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py index 242a2f8203fb56c428a63a5ee2726fff68649e57..3a31a285bf64816fba65d2cb76b7aea66abaf534 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py @@ -1,18 +1,24 @@ from __future__ import absolute_import + import collections import functools import logging from ._collections import RecentlyUsedContainer -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool -from .connectionpool import port_by_scheme -from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme +from .exceptions import ( + LocationValueError, + MaxRetryError, + ProxySchemeUnknown, + ProxySchemeUnsupported, + URLSchemeUnknown, +) from .packages import six from .packages.six.moves.urllib.parse import urljoin from .request import RequestMethods -from .util.url import parse_url +from .util.proxy import connection_requires_http_tunnel from .util.retry import Retry - +from .util.url import parse_url __all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] @@ -53,6 +59,7 @@ _key_fields = ( "key_headers", # dict "key__proxy", # parsed proxy url "key__proxy_headers", # dict + "key__proxy_config", # class "key_socket_options", # list of (level (int), optname (int), value (int or str)) tuples "key__socks_options", # dict "key_assert_hostname", # bool or string @@ -64,6 +71,9 @@ _key_fields = ( #: All custom key schemes should include the fields in this key at a minimum. PoolKey = collections.namedtuple("PoolKey", _key_fields) +_proxy_config_fields = ("ssl_context", "use_forwarding_for_https") +ProxyConfig = collections.namedtuple("ProxyConfig", _proxy_config_fields) + def _default_key_normalizer(key_class, request_context): """ @@ -155,6 +165,7 @@ class PoolManager(RequestMethods): """ proxy = None + proxy_config = None def __init__(self, num_pools=10, headers=None, **connection_pool_kw): RequestMethods.__init__(self, headers) @@ -176,7 +187,7 @@ class PoolManager(RequestMethods): def _new_pool(self, scheme, host, port, request_context=None): """ - Create a new :class:`ConnectionPool` based on host, port, scheme, and + Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and any additional pool keyword arguments. If ``request_context`` is provided, it is provided as keyword arguments @@ -212,7 +223,7 @@ class PoolManager(RequestMethods): def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): """ - Get a :class:`ConnectionPool` based on the host, port, and scheme. + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme. If ``port`` isn't given, it will be derived from the ``scheme`` using ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is @@ -235,20 +246,22 @@ class PoolManager(RequestMethods): def connection_from_context(self, request_context): """ - Get a :class:`ConnectionPool` based on the request context. + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context. ``request_context`` must at least contain the ``scheme`` key and its value must be a key in ``key_fn_by_scheme`` instance variable. """ scheme = request_context["scheme"].lower() - pool_key_constructor = self.key_fn_by_scheme[scheme] + pool_key_constructor = self.key_fn_by_scheme.get(scheme) + if not pool_key_constructor: + raise URLSchemeUnknown(scheme) pool_key = pool_key_constructor(request_context) return self.connection_from_pool_key(pool_key, request_context=request_context) def connection_from_pool_key(self, pool_key, request_context=None): """ - Get a :class:`ConnectionPool` based on the provided pool key. + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key. ``pool_key`` should be a namedtuple that only contains immutable objects. At a minimum it must have the ``scheme``, ``host``, and @@ -306,9 +319,39 @@ class PoolManager(RequestMethods): base_pool_kwargs[key] = value return base_pool_kwargs + def _proxy_requires_url_absolute_form(self, parsed_url): + """ + Indicates if the proxy requires the complete destination URL in the + request. Normally this is only needed when not using an HTTP CONNECT + tunnel. + """ + if self.proxy is None: + return False + + return not connection_requires_http_tunnel( + self.proxy, self.proxy_config, parsed_url.scheme + ) + + def _validate_proxy_scheme_url_selection(self, url_scheme): + """ + Validates that were not attempting to do TLS in TLS connections on + Python2 or with unsupported SSL implementations. + """ + if self.proxy is None or url_scheme != "https": + return + + if self.proxy.scheme != "https": + return + + if six.PY2 and not self.proxy_config.use_forwarding_for_https: + raise ProxySchemeUnsupported( + "Contacting HTTPS destinations through HTTPS proxies " + "'via CONNECT tunnels' is not supported in Python 2" + ) + def urlopen(self, method, url, redirect=True, **kw): """ - Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` + Same as :meth:`urllib3.HTTPConnectionPool.urlopen` with custom cross-host redirect logic and only sends the request-uri portion of the ``url``. @@ -316,6 +359,8 @@ class PoolManager(RequestMethods): :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. """ u = parse_url(url) + self._validate_proxy_scheme_url_selection(u.scheme) + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) kw["assert_same_host"] = False @@ -324,7 +369,7 @@ class PoolManager(RequestMethods): if "headers" not in kw: kw["headers"] = self.headers.copy() - if self.proxy is not None and u.scheme == "http": + if self._proxy_requires_url_absolute_form(u): response = conn.urlopen(method, url, **kw) else: response = conn.urlopen(method, u.request_uri, **kw) @@ -359,6 +404,7 @@ class PoolManager(RequestMethods): retries = retries.increment(method, url, response=response, _pool=conn) except MaxRetryError: if retries.raise_on_redirect: + response.drain_conn() raise return response @@ -366,6 +412,8 @@ class PoolManager(RequestMethods): kw["redirect"] = redirect log.info("Redirecting %s -> %s", url, redirect_location) + + response.drain_conn() return self.urlopen(method, redirect_location, **kw) @@ -383,6 +431,19 @@ class ProxyManager(PoolManager): HTTPS/CONNECT case they are sent only once. Could be used for proxy authentication. + :param proxy_ssl_context: + The proxy SSL context is used to establish the TLS connection to the + proxy when using HTTPS proxies. + + :param use_forwarding_for_https: + (Defaults to False) If set to True will forward requests to the HTTPS + proxy to be made on behalf of the client instead of creating a TLS + tunnel via the CONNECT method. **Enabling this flag means that request + and response headers and content will be visible from the HTTPS proxy** + whereas tunneling keeps request and response headers and content + private. IP address, target hostname, SNI, and port are always visible + to an HTTPS proxy even when this flag is disabled. + Example: >>> proxy = urllib3.ProxyManager('http://localhost:3128/') >>> r1 = proxy.request('GET', 'http://google.com/') @@ -402,6 +463,8 @@ class ProxyManager(PoolManager): num_pools=10, headers=None, proxy_headers=None, + proxy_ssl_context=None, + use_forwarding_for_https=False, **connection_pool_kw ): @@ -412,18 +475,22 @@ class ProxyManager(PoolManager): proxy_url.port, ) proxy = parse_url(proxy_url) - if not proxy.port: - port = port_by_scheme.get(proxy.scheme, 80) - proxy = proxy._replace(port=port) if proxy.scheme not in ("http", "https"): raise ProxySchemeUnknown(proxy.scheme) + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + self.proxy = proxy self.proxy_headers = proxy_headers or {} + self.proxy_ssl_context = proxy_ssl_context + self.proxy_config = ProxyConfig(proxy_ssl_context, use_forwarding_for_https) connection_pool_kw["_proxy"] = self.proxy connection_pool_kw["_proxy_headers"] = self.proxy_headers + connection_pool_kw["_proxy_config"] = self.proxy_config super(ProxyManager, self).__init__(num_pools, headers, **connection_pool_kw) @@ -455,11 +522,10 @@ class ProxyManager(PoolManager): def urlopen(self, method, url, redirect=True, **kw): "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." u = parse_url(url) - - if u.scheme == "http": - # For proxied HTTPS requests, httplib sets the necessary headers - # on the CONNECT to the proxy. For HTTP, we'll definitely - # need to set 'Host' at the very least. + if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme): + # For connections using HTTP CONNECT, httplib sets the necessary + # headers on the CONNECT to the proxy. If we're not using CONNECT, + # we'll definitely need to set 'Host' at the very least. headers = kw.get("headers", self.headers) kw["headers"] = self._set_proxy_headers(url, headers) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/request.py b/venv/Lib/site-packages/pip/_vendor/urllib3/request.py index 55f160bbf10f6856ce8909fc57532618118272f3..398386a5b9f61c13be314e256e671a37d28e3623 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/request.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/request.py @@ -3,15 +3,14 @@ from __future__ import absolute_import from .filepost import encode_multipart_formdata from .packages.six.moves.urllib.parse import urlencode - __all__ = ["RequestMethods"] class RequestMethods(object): """ Convenience mixin for classes who implement a :meth:`urlopen` method, such - as :class:`~urllib3.connectionpool.HTTPConnectionPool` and - :class:`~urllib3.poolmanager.PoolManager`. + as :class:`urllib3.HTTPConnectionPool` and + :class:`urllib3.PoolManager`. Provides behavior for making common types of HTTP request methods and decides which type of request field encoding to use. @@ -111,9 +110,9 @@ class RequestMethods(object): the body. This is useful for request methods like POST, PUT, PATCH, etc. When ``encode_multipart=True`` (default), then - :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + :func:`urllib3.encode_multipart_formdata` is used to encode the payload with the appropriate content type. Otherwise - :meth:`urllib.urlencode` is used with the + :func:`urllib.parse.urlencode` is used with the 'application/x-www-form-urlencoded' content type. Multipart encoding must be used when posting files, and it's reasonably diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/response.py b/venv/Lib/site-packages/pip/_vendor/urllib3/response.py index 6090a7350f9c4e4816a85a7ce2c7e5c9b8cce791..38693f4fc6e33766f7a6b4f1227867ae86d2da32 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/response.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/response.py @@ -1,10 +1,11 @@ from __future__ import absolute_import -from contextlib import contextmanager -import zlib + import io import logging -from socket import timeout as SocketTimeout +import zlib +from contextlib import contextmanager from socket import error as SocketError +from socket import timeout as SocketTimeout try: import brotli @@ -12,18 +13,20 @@ except ImportError: brotli = None from ._collections import HTTPHeaderDict +from .connection import BaseSSLError, HTTPException from .exceptions import ( BodyNotHttplibCompatible, - ProtocolError, DecodeError, - ReadTimeoutError, - ResponseNotChunked, + HTTPError, IncompleteRead, + InvalidChunkLength, InvalidHeader, + ProtocolError, + ReadTimeoutError, + ResponseNotChunked, + SSLError, ) -from .packages.six import string_types as basestring, PY3 -from .packages.six.moves import http_client as httplib -from .connection import HTTPException, BaseSSLError +from .packages import six from .util.response import is_fp_closed, is_response_to_head log = logging.getLogger(__name__) @@ -106,11 +109,10 @@ if brotli is not None: # are for 'brotlipy' and bottom branches for 'Brotli' def __init__(self): self._obj = brotli.Decompressor() - - def decompress(self, data): if hasattr(self._obj, "decompress"): - return self._obj.decompress(data) - return self._obj.process(data) + self.decompress = self._obj.decompress + else: + self.decompress = self._obj.process def flush(self): if hasattr(self._obj, "flush"): @@ -156,13 +158,13 @@ class HTTPResponse(io.IOBase): """ HTTP Response container. - Backwards-compatible to httplib's HTTPResponse but the response ``body`` is + Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is loaded and decoded on-demand when the ``data`` property is accessed. This class is also compatible with the Python standard library's :mod:`io` module, and can hence be treated as a readable object in the context of that framework. - Extra parameters for behaviour not present in httplib.HTTPResponse: + Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`: :param preload_content: If True, the response's body will be preloaded during construction. @@ -172,7 +174,7 @@ class HTTPResponse(io.IOBase): 'content-encoding' header. :param original_response: - When this HTTPResponse wrapper is generated from an httplib.HTTPResponse + When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse` object, it's convenient to include the original for debug purposes. It's otherwise unused. @@ -232,7 +234,7 @@ class HTTPResponse(io.IOBase): self.msg = msg self._request_url = request_url - if body and isinstance(body, (basestring, bytes)): + if body and isinstance(body, (six.string_types, bytes)): self._body = body self._pool = pool @@ -277,9 +279,20 @@ class HTTPResponse(io.IOBase): self._pool._put_conn(self._connection) self._connection = None + def drain_conn(self): + """ + Read and discard any remaining HTTP response data in the response connection. + + Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. + """ + try: + self.read() + except (HTTPError, SocketError, BaseSSLError, HTTPException): + pass + @property def data(self): - # For backwords-compat with earlier urllib3 0.4 and earlier. + # For backwards-compat with earlier urllib3 0.4 and earlier. if self._body: return self._body @@ -296,8 +309,8 @@ class HTTPResponse(io.IOBase): def tell(self): """ Obtain the number of bytes pulled over the wire so far. May differ from - the amount of content returned by :meth:``HTTPResponse.read`` if bytes - are encoded on the wire (e.g, compressed). + the amount of content returned by :meth:``urllib3.response.HTTPResponse.read`` + if bytes are encoded on the wire (e.g, compressed). """ return self._fp_bytes_read @@ -431,10 +444,9 @@ class HTTPResponse(io.IOBase): except BaseSSLError as e: # FIXME: Is there a better way to differentiate between SSLErrors? - if "read operation timed out" not in str(e): # Defensive: - # This shouldn't happen but just in case we're missing an edge - # case, let's avoid swallowing SSL errors. - raise + if "read operation timed out" not in str(e): + # SSL errors related to framing/MAC get wrapped and reraised here + raise SSLError(e) raise ReadTimeoutError(self._pool, None, "Read timed out.") @@ -468,7 +480,7 @@ class HTTPResponse(io.IOBase): def read(self, amt=None, decode_content=None, cache_content=False): """ - Similar to :meth:`httplib.HTTPResponse.read`, but with two additional + Similar to :meth:`http.client.HTTPResponse.read`, but with two additional parameters: ``decode_content`` and ``cache_content``. :param amt: @@ -569,7 +581,7 @@ class HTTPResponse(io.IOBase): @classmethod def from_httplib(ResponseCls, r, **response_kw): """ - Given an :class:`httplib.HTTPResponse` instance ``r``, return a + Given an :class:`http.client.HTTPResponse` instance ``r``, return a corresponding :class:`urllib3.response.HTTPResponse` object. Remaining parameters are passed to the HTTPResponse constructor, along @@ -578,11 +590,11 @@ class HTTPResponse(io.IOBase): headers = r.msg if not isinstance(headers, HTTPHeaderDict): - if PY3: - headers = HTTPHeaderDict(headers.items()) - else: + if six.PY2: # Python 2.7 headers = HTTPHeaderDict.from_httplib(headers) + else: + headers = HTTPHeaderDict(headers.items()) # HTTPResponse objects in Python 3 don't have a .strict attribute strict = getattr(r, "strict", 0) @@ -598,7 +610,7 @@ class HTTPResponse(io.IOBase): ) return resp - # Backwards-compatibility methods for httplib.HTTPResponse + # Backwards-compatibility methods for http.client.HTTPResponse def getheaders(self): return self.headers @@ -668,8 +680,8 @@ class HTTPResponse(io.IOBase): def supports_chunked_reads(self): """ Checks if the underlying file-like object looks like a - httplib.HTTPResponse object. We do this by testing for the fp - attribute. If it is present we assume it returns raw chunks as + :class:`http.client.HTTPResponse` object. We do this by testing for + the fp attribute. If it is present we assume it returns raw chunks as processed by read_chunked(). """ return hasattr(self._fp, "fp") @@ -686,7 +698,7 @@ class HTTPResponse(io.IOBase): except ValueError: # Invalid chunked protocol response, abort. self.close() - raise httplib.IncompleteRead(line) + raise InvalidChunkLength(self, line) def _handle_chunk(self, amt): returned_chunk = None @@ -733,7 +745,7 @@ class HTTPResponse(io.IOBase): ) if not self.supports_chunked_reads(): raise BodyNotHttplibCompatible( - "Body should be httplib.HTTPResponse like. " + "Body should be http.client.HTTPResponse like. " "It should have have an fp attribute which returns raw chunks." ) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py index a96c73a9d85187af6923d548a5cc517f1517e205..4547fc522b690ba2697843edd044f2039a4123a9 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py @@ -2,23 +2,23 @@ from __future__ import absolute_import # For backwards compatibility, provide imports that used to be here. from .connection import is_connection_dropped -from .request import make_headers +from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers from .response import is_fp_closed +from .retry import Retry from .ssl_ import ( - SSLContext, + ALPN_PROTOCOLS, HAS_SNI, IS_PYOPENSSL, IS_SECURETRANSPORT, + PROTOCOL_TLS, + SSLContext, assert_fingerprint, resolve_cert_reqs, resolve_ssl_version, ssl_wrap_socket, - PROTOCOL_TLS, ) -from .timeout import current_time, Timeout - -from .retry import Retry -from .url import get_host, parse_url, split_first, Url +from .timeout import Timeout, current_time +from .url import Url, get_host, parse_url, split_first from .wait import wait_for_read, wait_for_write __all__ = ( @@ -27,6 +27,7 @@ __all__ = ( "IS_SECURETRANSPORT", "SSLContext", "PROTOCOL_TLS", + "ALPN_PROTOCOLS", "Retry", "Timeout", "Url", @@ -43,4 +44,6 @@ __all__ = ( "ssl_wrap_socket", "wait_for_read", "wait_for_write", + "SKIP_HEADER", + "SKIPPABLE_HEADERS", ) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py index 86f0a3b00edc11b2a313c9c7b2109d04f02bb5f6..facfa0dd249f67ca6abd39ff2ae04d6912d5fc61 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py @@ -1,7 +1,12 @@ from __future__ import absolute_import + import socket -from .wait import NoWayToWaitForSocketError, wait_for_read + +from pip._vendor.urllib3.exceptions import LocationParseError + from ..contrib import _appengine_environ +from ..packages import six +from .wait import NoWayToWaitForSocketError, wait_for_read def is_connection_dropped(conn): # Platform-specific @@ -9,7 +14,7 @@ def is_connection_dropped(conn): # Platform-specific Returns True if the connection is dropped and should be closed. :param conn: - :class:`httplib.HTTPConnection` object. + :class:`http.client.HTTPConnection` object. Note: For platforms like AppEngine, this will always return ``False`` to let the platform handle connection recycling transparently for us. @@ -42,7 +47,7 @@ def create_connection( port)``) and return the socket object. Passing the optional *timeout* parameter will set the timeout on the socket instance before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` + global default timeout setting returned by :func:`socket.getdefaulttimeout` is used. If *source_address* is set it must be a tuple of (host, port) for the socket to bind as a source address before making the connection. An host of '' or port 0 tells the OS to use the default. @@ -58,6 +63,13 @@ def create_connection( # The original create_connection function always returns all records. family = allowed_gai_family() + try: + host.encode("idna") + except UnicodeError: + return six.raise_from( + LocationParseError(u"'%s', label empty or too long" % host), None + ) + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res sock = None @@ -106,7 +118,7 @@ def allowed_gai_family(): def _has_ipv6(host): - """ Returns True if the system can bind an IPv6 address. """ + """Returns True if the system can bind an IPv6 address.""" sock = None has_ipv6 = False diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py index d3d379a19997b0c76c820a82e10b8fd36db9bea4..41784104ee4bd5796006d1052536325d52db1e8c 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py @@ -1,4 +1,5 @@ import collections + from ..packages import six from ..packages.six.moves import queue diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py index 3b7bb54dafb9a0c0e5f5b694d4b359197ecb39de..25103383ec7abc7b46fb6a6f549efa38e4abe24c 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py @@ -1,8 +1,16 @@ from __future__ import absolute_import + from base64 import b64encode -from ..packages.six import b, integer_types from ..exceptions import UnrewindableBodyError +from ..packages.six import b, integer_types + +# Pass as a value within ``headers`` to skip +# emitting some HTTP headers that are added automatically. +# The only headers that are supported are ``Accept-Encoding``, +# ``Host``, and ``User-Agent``. +SKIP_HEADER = "@@@SKIP_HEADER@@@" +SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) ACCEPT_ENCODING = "gzip,deflate" try: diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py index 715868dd1000e9afd52839d0b5f94e5be42b79f3..5ea609ccedf18eb4ab70f8fc6990448eb6407237 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py @@ -1,7 +1,9 @@ from __future__ import absolute_import -from ..packages.six.moves import http_client as httplib + +from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect from ..exceptions import HeaderParsingError +from ..packages.six.moves import http_client as httplib def is_fp_closed(obj): @@ -42,8 +44,7 @@ def assert_header_parsing(headers): Only works on Python 3. - :param headers: Headers to verify. - :type headers: `httplib.HTTPMessage`. + :param http.client.HTTPMessage headers: Headers to verify. :raises urllib3.exceptions.HeaderParsingError: If parsing errors are found. @@ -66,6 +67,25 @@ def assert_header_parsing(headers): if isinstance(payload, (bytes, str)): unparsed_data = payload + if defects: + # httplib is assuming a response body is available + # when parsing headers even when httplib only sends + # header data to parse_headers() This results in + # defects on multipart responses in particular. + # See: https://github.com/urllib3/urllib3/issues/800 + + # So we ignore the following defects: + # - StartBoundaryNotFoundDefect: + # The claimed start boundary was never found. + # - MultipartInvariantViolationDefect: + # A message claimed to be a multipart but no subparts were found. + defects = [ + defect + for defect in defects + if not isinstance( + defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) + ) + ] if defects or unparsed_data: raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) @@ -76,8 +96,9 @@ def is_response_to_head(response): Checks whether the request of a response has been a HEAD-request. Handles the quirks of AppEngine. - :param conn: - :type conn: :class:`httplib.HTTPResponse` + :param http.client.HTTPResponse response: + Response to check if the originating request + used 'HEAD' as a method. """ # FIXME: Can we do this somehow without accessing private httplib _method? method = response._method diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py index 5a049fe65e0f33d80b81388b467811cbed89c758..c7dc42f1d60220a4fc35aa19c76ef3c558cff9fe 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py @@ -1,22 +1,24 @@ from __future__ import absolute_import -import time + +import email import logging +import re +import time +import warnings from collections import namedtuple from itertools import takewhile -import email -import re from ..exceptions import ( ConnectTimeoutError, + InvalidHeader, MaxRetryError, ProtocolError, + ProxyError, ReadTimeoutError, ResponseError, - InvalidHeader, ) from ..packages import six - log = logging.getLogger(__name__) @@ -26,8 +28,51 @@ RequestHistory = namedtuple( ) +# TODO: In v2 we can remove this sentinel and metaclass with deprecated options. +_Default = object() + + +class _RetryMeta(type): + @property + def DEFAULT_METHOD_WHITELIST(cls): + warnings.warn( + "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead", + DeprecationWarning, + ) + return cls.DEFAULT_ALLOWED_METHODS + + @DEFAULT_METHOD_WHITELIST.setter + def DEFAULT_METHOD_WHITELIST(cls, value): + warnings.warn( + "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead", + DeprecationWarning, + ) + cls.DEFAULT_ALLOWED_METHODS = value + + @property + def DEFAULT_REDIRECT_HEADERS_BLACKLIST(cls): + warnings.warn( + "Using 'Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT' instead", + DeprecationWarning, + ) + return cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT + + @DEFAULT_REDIRECT_HEADERS_BLACKLIST.setter + def DEFAULT_REDIRECT_HEADERS_BLACKLIST(cls, value): + warnings.warn( + "Using 'Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT' instead", + DeprecationWarning, + ) + cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT = value + + +@six.add_metaclass(_RetryMeta) class Retry(object): - """ Retry configuration. + """Retry configuration. Each retry attempt will create a new Retry object with updated values, so they can be safely reused. @@ -53,8 +98,7 @@ class Retry(object): Total number of retries to allow. Takes precedence over other counts. Set to ``None`` to remove this constraint and fall back on other - counts. It's a good idea to set this to some sensibly-high value to - account for unexpected edge cases and avoid infinite retry loops. + counts. Set to ``0`` to fail on the first retry. @@ -95,18 +139,35 @@ class Retry(object): Set to ``0`` to fail on the first retry of this type. - :param iterable method_whitelist: + :param int other: + How many times to retry on other errors. + + Other errors are errors that are not connect, read, redirect or status errors. + These errors might be raised after the request was sent to the server, so the + request might have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + If ``total`` is not set, it's a good idea to set this to 0 to account + for unexpected edge cases and avoid infinite retry loops. + + :param iterable allowed_methods: Set of uppercased HTTP method verbs that we should retry on. By default, we only retry on methods which are considered to be idempotent (multiple requests with the same parameters end with the - same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`. Set to a ``False`` value to retry on any verb. + .. warning:: + + Previously this parameter was named ``method_whitelist``, that + usage is deprecated in v1.26.0 and will be removed in v2.0. + :param iterable status_forcelist: A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``method_whitelist`` + A retry is initiated if the request method is in ``allowed_methods`` and the response status code is in ``status_forcelist``. By default, this is disabled with ``None``. @@ -147,13 +208,16 @@ class Retry(object): request. """ - DEFAULT_METHOD_WHITELIST = frozenset( + #: Default methods to be used for ``allowed_methods`` + DEFAULT_ALLOWED_METHODS = frozenset( ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] ) + #: Default status codes to be used for ``status_forcelist`` RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) - DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(["Authorization"]) + #: Default headers to be used for ``remove_headers_on_redirect`` + DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset(["Authorization"]) #: Maximum backoff time. BACKOFF_MAX = 120 @@ -165,20 +229,43 @@ class Retry(object): read=None, redirect=None, status=None, - method_whitelist=DEFAULT_METHOD_WHITELIST, + other=None, + allowed_methods=_Default, status_forcelist=None, backoff_factor=0, raise_on_redirect=True, raise_on_status=True, history=None, respect_retry_after_header=True, - remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST, + remove_headers_on_redirect=_Default, + # TODO: Deprecated, remove in v2.0 + method_whitelist=_Default, ): + if method_whitelist is not _Default: + if allowed_methods is not _Default: + raise ValueError( + "Using both 'allowed_methods' and " + "'method_whitelist' together is not allowed. " + "Instead only use 'allowed_methods'" + ) + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + stacklevel=2, + ) + allowed_methods = method_whitelist + if allowed_methods is _Default: + allowed_methods = self.DEFAULT_ALLOWED_METHODS + if remove_headers_on_redirect is _Default: + remove_headers_on_redirect = self.DEFAULT_REMOVE_HEADERS_ON_REDIRECT + self.total = total self.connect = connect self.read = read self.status = status + self.other = other if redirect is False or total is False: redirect = 0 @@ -186,7 +273,7 @@ class Retry(object): self.redirect = redirect self.status_forcelist = status_forcelist or set() - self.method_whitelist = method_whitelist + self.allowed_methods = allowed_methods self.backoff_factor = backoff_factor self.raise_on_redirect = raise_on_redirect self.raise_on_status = raise_on_status @@ -203,7 +290,7 @@ class Retry(object): read=self.read, redirect=self.redirect, status=self.status, - method_whitelist=self.method_whitelist, + other=self.other, status_forcelist=self.status_forcelist, backoff_factor=self.backoff_factor, raise_on_redirect=self.raise_on_redirect, @@ -212,12 +299,29 @@ class Retry(object): remove_headers_on_redirect=self.remove_headers_on_redirect, respect_retry_after_header=self.respect_retry_after_header, ) + + # TODO: If already given in **kw we use what's given to us + # If not given we need to figure out what to pass. We decide + # based on whether our class has the 'method_whitelist' property + # and if so we pass the deprecated 'method_whitelist' otherwise + # we use 'allowed_methods'. Remove in v2.0 + if "method_whitelist" not in kw and "allowed_methods" not in kw: + if "method_whitelist" in self.__dict__: + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + ) + params["method_whitelist"] = self.allowed_methods + else: + params["allowed_methods"] = self.allowed_methods + params.update(kw) return type(self)(**params) @classmethod def from_int(cls, retries, redirect=True, default=None): - """ Backwards-compatibility for the old retries format.""" + """Backwards-compatibility for the old retries format.""" if retries is None: retries = default if default is not None else cls.DEFAULT @@ -230,7 +334,7 @@ class Retry(object): return new_retries def get_backoff_time(self): - """ Formula for computing the current backoff + """Formula for computing the current backoff :rtype: float """ @@ -251,10 +355,17 @@ class Retry(object): if re.match(r"^\s*[0-9]+\s*$", retry_after): seconds = int(retry_after) else: - retry_date_tuple = email.utils.parsedate(retry_after) + retry_date_tuple = email.utils.parsedate_tz(retry_after) if retry_date_tuple is None: raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) - retry_date = time.mktime(retry_date_tuple) + if retry_date_tuple[9] is None: # Python 2 + # Assume UTC if no timezone was specified + # On Python2.7, parsedate_tz returns None for a timezone offset + # instead of 0 if no timezone is given, where mktime_tz treats + # a None timezone offset as local time. + retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] + + retry_date = email.utils.mktime_tz(retry_date_tuple) seconds = retry_date - time.time() if seconds < 0: @@ -263,7 +374,7 @@ class Retry(object): return seconds def get_retry_after(self, response): - """ Get the value of Retry-After in seconds. """ + """Get the value of Retry-After in seconds.""" retry_after = response.getheader("Retry-After") @@ -287,7 +398,7 @@ class Retry(object): time.sleep(backoff) def sleep(self, response=None): - """ Sleep between retry attempts. + """Sleep between retry attempts. This method will respect a server's ``Retry-After`` response header and sleep the duration of the time requested. If that is not present, it @@ -303,28 +414,41 @@ class Retry(object): self._sleep_backoff() def _is_connection_error(self, err): - """ Errors when we're fairly sure that the server did not receive the + """Errors when we're fairly sure that the server did not receive the request, so it should be safe to retry. """ + if isinstance(err, ProxyError): + err = err.original_error return isinstance(err, ConnectTimeoutError) def _is_read_error(self, err): - """ Errors that occur after the request has been started, so we should + """Errors that occur after the request has been started, so we should assume that the server began processing it. """ return isinstance(err, (ReadTimeoutError, ProtocolError)) def _is_method_retryable(self, method): - """ Checks if a given HTTP method should be retried upon, depending if - it is included on the method whitelist. + """Checks if a given HTTP method should be retried upon, depending if + it is included in the allowed_methods """ - if self.method_whitelist and method.upper() not in self.method_whitelist: - return False + # TODO: For now favor if the Retry implementation sets its own method_whitelist + # property outside of our constructor to avoid breaking custom implementations. + if "method_whitelist" in self.__dict__: + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + ) + allowed_methods = self.method_whitelist + else: + allowed_methods = self.allowed_methods + if allowed_methods and method.upper() not in allowed_methods: + return False return True def is_retry(self, method, status_code, has_retry_after=False): - """ Is this method/status code retryable? (Based on whitelists and control + """Is this method/status code retryable? (Based on allowlists and control variables such as the number of total retries to allow, whether to respect the Retry-After header, whether this header is present, and whether the returned status code is on the list of status codes to @@ -344,8 +468,15 @@ class Retry(object): ) def is_exhausted(self): - """ Are we out of retries? """ - retry_counts = (self.total, self.connect, self.read, self.redirect, self.status) + """Are we out of retries?""" + retry_counts = ( + self.total, + self.connect, + self.read, + self.redirect, + self.status, + self.other, + ) retry_counts = list(filter(None, retry_counts)) if not retry_counts: return False @@ -361,7 +492,7 @@ class Retry(object): _pool=None, _stacktrace=None, ): - """ Return a new Retry object with incremented retry counters. + """Return a new Retry object with incremented retry counters. :param response: A response object, or None, if the server did not return a response. @@ -383,6 +514,7 @@ class Retry(object): read = self.read redirect = self.redirect status_count = self.status + other = self.other cause = "unknown" status = None redirect_location = None @@ -401,6 +533,11 @@ class Retry(object): elif read is not None: read -= 1 + elif error: + # Other retry? + if other is not None: + other -= 1 + elif response and response.get_redirect_location(): # Redirect retry? if redirect is not None: @@ -411,7 +548,7 @@ class Retry(object): else: # Incrementing because of a server error like a 500 in - # status_forcelist and a the given method is in the whitelist + # status_forcelist and the given method is in the allowed_methods cause = ResponseError.GENERIC_ERROR if response and response.status: if status_count is not None: @@ -429,6 +566,7 @@ class Retry(object): read=read, redirect=redirect, status=status_count, + other=other, history=history, ) @@ -445,6 +583,20 @@ class Retry(object): "read={self.read}, redirect={self.redirect}, status={self.status})" ).format(cls=type(self), self=self) + def __getattr__(self, item): + if item == "method_whitelist": + # TODO: Remove this deprecated alias in v2.0 + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + ) + return self.allowed_methods + try: + return getattr(super(Retry, self), item) + except AttributeError: + return getattr(Retry, item) + # For backwards compatibility (equivalent to pre-v1.9): Retry.DEFAULT = Retry(3) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py index 3f78296f6565a4d23502623fcdded5a16d0fa225..2b45d391d4d7398e4769f45f9dd25eb55daef437 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py @@ -1,21 +1,27 @@ from __future__ import absolute_import -import errno -import warnings + import hmac +import os import sys - +import warnings from binascii import hexlify, unhexlify from hashlib import md5, sha1, sha256 -from .url import IPV4_RE, BRACELESS_IPV6_ADDRZ_RE -from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning +from ..exceptions import ( + InsecurePlatformWarning, + ProxySchemeUnsupported, + SNIMissingWarning, + SSLError, +) from ..packages import six - +from .url import BRACELESS_IPV6_ADDRZ_RE, IPV4_RE SSLContext = None +SSLTransport = None HAS_SNI = False IS_PYOPENSSL = False IS_SECURETRANSPORT = False +ALPN_PROTOCOLS = ["http/1.1"] # Maps the length of a digest to a possible hash function producing this digest HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256} @@ -29,8 +35,8 @@ def _const_compare_digest_backport(a, b): Returns True if the digests match, and False otherwise. """ result = abs(len(a) - len(b)) - for l, r in zip(bytearray(a), bytearray(b)): - result |= l ^ r + for left, right in zip(bytearray(a), bytearray(b)): + result |= left ^ right return result == 0 @@ -38,11 +44,21 @@ _const_compare_digest = getattr(hmac, "compare_digest", _const_compare_digest_ba try: # Test for SSL features import ssl - from ssl import wrap_socket, CERT_REQUIRED + from ssl import CERT_REQUIRED, wrap_socket +except ImportError: + pass + +try: from ssl import HAS_SNI # Has SNI? except ImportError: pass +try: + from .ssltransport import SSLTransport +except ImportError: + pass + + try: # Platform-specific: Python 3.6 from ssl import PROTOCOL_TLS @@ -55,14 +71,25 @@ except ImportError: except ImportError: PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 +try: + from ssl import PROTOCOL_TLS_CLIENT +except ImportError: + PROTOCOL_TLS_CLIENT = PROTOCOL_TLS + try: - from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION + from ssl import OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3 except ImportError: OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 OP_NO_COMPRESSION = 0x20000 +try: # OP_NO_TICKET was added in Python 3.6 + from ssl import OP_NO_TICKET +except ImportError: + OP_NO_TICKET = 0x4000 + + # A secure default. # Sources for more information on TLS ciphers: # @@ -119,12 +146,15 @@ except ImportError: self.certfile = certfile self.keyfile = keyfile - def load_verify_locations(self, cafile=None, capath=None): + def load_verify_locations(self, cafile=None, capath=None, cadata=None): self.ca_certs = cafile if capath is not None: raise SSLError("CA directories not supported in older Pythons") + if cadata is not None: + raise SSLError("CA data not supported in older Pythons") + def set_ciphers(self, cipher_suite): self.ciphers = cipher_suite @@ -134,7 +164,7 @@ except ImportError: "urllib3 from configuring SSL appropriately and may cause " "certain SSL connections to fail. You can upgrade to a newer " "version of Python to solve this. For more information, see " - "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" "#ssl-warnings", InsecurePlatformWarning, ) @@ -246,14 +276,18 @@ def create_urllib3_context( ``ssl.CERT_REQUIRED``. :param options: Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, - ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``, and ``ssl.OP_NO_TICKET``. :param ciphers: Which cipher suites to allow the server to select. :returns: Constructed SSLContext object with specified options :rtype: SSLContext """ - context = SSLContext(ssl_version or PROTOCOL_TLS) + # PROTOCOL_TLS is deprecated in Python 3.10 + if not ssl_version or ssl_version == PROTOCOL_TLS: + ssl_version = PROTOCOL_TLS_CLIENT + + context = SSLContext(ssl_version) context.set_ciphers(ciphers or DEFAULT_CIPHERS) @@ -269,6 +303,11 @@ def create_urllib3_context( # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ # (issue #309) options |= OP_NO_COMPRESSION + # TLSv1.2 only. Unless set explicitly, do not request tickets. + # This may save some bandwidth on wire, and although the ticket is encrypted, + # there is a risk associated with it being on wire, + # if the server is not rotating its ticketing keys properly. + options |= OP_NO_TICKET context.options |= options @@ -283,13 +322,33 @@ def create_urllib3_context( ) is not None: context.post_handshake_auth = True - context.verify_mode = cert_reqs - if ( - getattr(context, "check_hostname", None) is not None - ): # Platform-specific: Python 3.2 - # We do our own verification, including fingerprints and alternative - # hostnames. So disable it here - context.check_hostname = False + def disable_check_hostname(): + if ( + getattr(context, "check_hostname", None) is not None + ): # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + + # The order of the below lines setting verify_mode and check_hostname + # matter due to safe-guards SSLContext has to prevent an SSLContext with + # check_hostname=True, verify_mode=NONE/OPTIONAL. This is made even more + # complex because we don't know whether PROTOCOL_TLS_CLIENT will be used + # or not so we don't know the initial state of the freshly created SSLContext. + if cert_reqs == ssl.CERT_REQUIRED: + context.verify_mode = cert_reqs + disable_check_hostname() + else: + disable_check_hostname() + context.verify_mode = cert_reqs + + # Enable logging of TLS session keys via defacto standard environment variable + # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. + if hasattr(context, "keylog_filename"): + sslkeylogfile = os.environ.get("SSLKEYLOGFILE") + if sslkeylogfile: + context.keylog_filename = sslkeylogfile + return context @@ -305,6 +364,8 @@ def ssl_wrap_socket( ssl_context=None, ca_cert_dir=None, key_password=None, + ca_cert_data=None, + tls_in_tls=False, ): """ All arguments except for server_hostname, ssl_context, and ca_cert_dir have @@ -323,6 +384,11 @@ def ssl_wrap_socket( SSLContext.load_verify_locations(). :param key_password: Optional password if the keyfile is encrypted. + :param ca_cert_data: + Optional string containing CA certificates in PEM format suitable for + passing as the cadata parameter to SSLContext.load_verify_locations() + :param tls_in_tls: + Use SSLTransport to wrap the existing socket. """ context = ssl_context if context is None: @@ -331,17 +397,11 @@ def ssl_wrap_socket( # this code. context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) - if ca_certs or ca_cert_dir: + if ca_certs or ca_cert_dir or ca_cert_data: try: - context.load_verify_locations(ca_certs, ca_cert_dir) - except IOError as e: # Platform-specific: Python 2.7 + context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) + except (IOError, OSError) as e: raise SSLError(e) - # Py33 raises FileNotFoundError which subclasses OSError - # These are not equivalent unless we check the errno attribute - except OSError as e: # Platform-specific: Python 3.3 and beyond - if e.errno == errno.ENOENT: - raise SSLError(e) - raise elif ssl_context is None and hasattr(context, "load_default_certs"): # try to load OS default certs; works well on Windows (require Python3.4+) @@ -359,28 +419,39 @@ def ssl_wrap_socket( else: context.load_cert_chain(certfile, keyfile, key_password) + try: + if hasattr(context, "set_alpn_protocols"): + context.set_alpn_protocols(ALPN_PROTOCOLS) + except NotImplementedError: # Defensive: in CI, we always have set_alpn_protocols + pass + # If we detect server_hostname is an IP address then the SNI # extension should not be used according to RFC3546 Section 3.1 - # We shouldn't warn the user if SNI isn't available but we would - # not be using SNI anyways due to IP address for server_hostname. - if ( - server_hostname is not None and not is_ipaddress(server_hostname) - ) or IS_SECURETRANSPORT: - if HAS_SNI and server_hostname is not None: - return context.wrap_socket(sock, server_hostname=server_hostname) - + use_sni_hostname = server_hostname and not is_ipaddress(server_hostname) + # SecureTransport uses server_hostname in certificate verification. + send_sni = (use_sni_hostname and HAS_SNI) or ( + IS_SECURETRANSPORT and server_hostname + ) + # Do not warn the user if server_hostname is an invalid SNI hostname. + if not HAS_SNI and use_sni_hostname: warnings.warn( "An HTTPS request has been made, but the SNI (Server Name " "Indication) extension to TLS is not available on this platform. " "This may cause the server to present an incorrect TLS " "certificate, which can cause validation failures. You can upgrade to " "a newer version of Python to solve this. For more information, see " - "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" "#ssl-warnings", SNIMissingWarning, ) - return context.wrap_socket(sock) + if send_sni: + ssl_sock = _ssl_wrap_socket_impl( + sock, context, tls_in_tls, server_hostname=server_hostname + ) + else: + ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls) + return ssl_sock def is_ipaddress(hostname): @@ -405,3 +476,20 @@ def _is_key_file_encrypted(key_file): return True return False + + +def _ssl_wrap_socket_impl(sock, ssl_context, tls_in_tls, server_hostname=None): + if tls_in_tls: + if not SSLTransport: + # Import error, ssl is not available. + raise ProxySchemeUnsupported( + "TLS in TLS requires support for the 'ssl' module" + ) + + SSLTransport._validate_ssl_context_for_tls_in_tls(ssl_context) + return SSLTransport(sock, ssl_context, server_hostname) + + if server_hostname: + return ssl_context.wrap_socket(sock, server_hostname=server_hostname) + else: + return ssl_context.wrap_socket(sock) diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py index 9883700556ef70bd153ae48021c3d6fb410d4122..ff69593b05b5eb5fcd336b4bd16193c44dc48ef5 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py @@ -1,9 +1,10 @@ from __future__ import absolute_import +import time + # The default socket timeout, used by httplib to indicate that no timeout was # specified by the user from socket import _GLOBAL_DEFAULT_TIMEOUT -import time from ..exceptions import TimeoutStateError @@ -17,22 +18,28 @@ current_time = getattr(time, "monotonic", time.time) class Timeout(object): - """ Timeout configuration. + """Timeout configuration. + + Timeouts can be defined as a default for a pool: - Timeouts can be defined as a default for a pool:: + .. code-block:: python - timeout = Timeout(connect=2.0, read=7.0) - http = PoolManager(timeout=timeout) - response = http.request('GET', 'http://example.com/') + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') - Or per-request (which overrides the default for the pool):: + Or per-request (which overrides the default for the pool): - response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + .. code-block:: python - Timeouts can be disabled by setting all the parameters to ``None``:: + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) - no_timeout = Timeout(connect=None, read=None) - response = http.request('GET', 'http://example.com/, timeout=no_timeout) + Timeouts can be disabled by setting all the parameters to ``None``: + + .. code-block:: python + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) :param total: @@ -43,7 +50,7 @@ class Timeout(object): Defaults to None. - :type total: integer, float, or None + :type total: int, float, or None :param connect: The maximum amount of time (in seconds) to wait for a connection @@ -53,7 +60,7 @@ class Timeout(object): `_. None will set an infinite timeout for connection attempts. - :type connect: integer, float, or None + :type connect: int, float, or None :param read: The maximum amount of time (in seconds) to wait between consecutive @@ -63,7 +70,7 @@ class Timeout(object): `_. None will set an infinite timeout. - :type read: integer, float, or None + :type read: int, float, or None .. note:: @@ -98,7 +105,7 @@ class Timeout(object): self.total = self._validate_timeout(total, "total") self._start_connect = None - def __str__(self): + def __repr__(self): return "%s(connect=%r, read=%r, total=%r)" % ( type(self).__name__, self._connect, @@ -106,9 +113,12 @@ class Timeout(object): self.total, ) + # __str__ provided for backwards compatibility + __str__ = __repr__ + @classmethod def _validate_timeout(cls, value, name): - """ Check that a timeout attribute is valid. + """Check that a timeout attribute is valid. :param value: The timeout value to validate :param name: The name of the timeout attribute to validate. This is @@ -154,7 +164,7 @@ class Timeout(object): @classmethod def from_float(cls, timeout): - """ Create a new Timeout from a legacy timeout value. + """Create a new Timeout from a legacy timeout value. The timeout value used by httplib.py sets the same timeout on the connect(), and recv() socket requests. This creates a :class:`Timeout` @@ -169,7 +179,7 @@ class Timeout(object): return Timeout(read=timeout, connect=timeout) def clone(self): - """ Create a copy of the timeout object + """Create a copy of the timeout object Timeout properties are stored per-pool but each request needs a fresh Timeout object to ensure each one has its own start/stop configured. @@ -183,7 +193,7 @@ class Timeout(object): return Timeout(connect=self._connect, read=self._read, total=self.total) def start_connect(self): - """ Start the timeout clock, used during a connect() attempt + """Start the timeout clock, used during a connect() attempt :raises urllib3.exceptions.TimeoutStateError: if you attempt to start a timer that has been started already. @@ -194,7 +204,7 @@ class Timeout(object): return self._start_connect def get_connect_duration(self): - """ Gets the time elapsed since the call to :meth:`start_connect`. + """Gets the time elapsed since the call to :meth:`start_connect`. :return: Elapsed time in seconds. :rtype: float @@ -209,7 +219,7 @@ class Timeout(object): @property def connect_timeout(self): - """ Get the value to use when setting a connection timeout. + """Get the value to use when setting a connection timeout. This will be a positive float or integer, the value None (never timeout), or the default system timeout. @@ -227,7 +237,7 @@ class Timeout(object): @property def read_timeout(self): - """ Get the value for the read timeout. + """Get the value for the read timeout. This assumes some time has elapsed in the connection timeout and computes the read timeout appropriately. diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py index 5f8aee629a70b4c5fbeac4a9d22a9d35d91a7730..3651c43182a0b32f676983e781129b7f660d112b 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py @@ -1,11 +1,11 @@ from __future__ import absolute_import + import re from collections import namedtuple from ..exceptions import LocationParseError from ..packages import six - url_attrs = ["scheme", "auth", "host", "port", "path", "query", "fragment"] # We only want to normalize urls with an HTTP(S) scheme. @@ -18,7 +18,7 @@ PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") URI_RE = re.compile( r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" - r"(?://([^/?#]*))?" + r"(?://([^\\/?#]*))?" r"([^?#]*)" r"(?:\?([^#]*))?" r"(?:#(.*))?$", @@ -63,12 +63,12 @@ IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$") BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") -SUBAUTHORITY_PAT = (u"^(?:(.*)@)?(%s|%s|%s)(?::([0-9]{0,5}))?$") % ( +_HOST_PORT_PAT = ("^(%s|%s|%s)(?::([0-9]{0,5}))?$") % ( REG_NAME_PAT, IPV4_PAT, IPV6_ADDRZ_PAT, ) -SUBAUTHORITY_RE = re.compile(SUBAUTHORITY_PAT, re.UNICODE | re.DOTALL) +_HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL) UNRESERVED_CHARS = set( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" @@ -365,7 +365,9 @@ def parse_url(url): scheme = scheme.lower() if authority: - auth, host, port = SUBAUTHORITY_RE.match(authority).groups() + auth, _, host_port = authority.rpartition("@") + auth = auth or None + host, port = _HOST_PORT_RE.match(host_port).groups() if auth and normalize_uri: auth = _encode_invalid_chars(auth, USERINFO_CHARS) if port == "": diff --git a/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py b/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py index d71d2fd722bedb03a45d1f70cdc41d028ffa7d69..c280646c7be0b1887878254408c6f6c5158651ca 100644 --- a/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py +++ b/venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py @@ -1,7 +1,7 @@ import errno -from functools import partial import select import sys +from functools import partial try: from time import monotonic @@ -140,14 +140,14 @@ def wait_for_socket(*args, **kwargs): def wait_for_read(sock, timeout=None): - """ Waits for reading to be available on a given socket. + """Waits for reading to be available on a given socket. Returns True if the socket is readable, or False if the timeout expired. """ return wait_for_socket(sock, read=True, timeout=timeout) def wait_for_write(sock, timeout=None): - """ Waits for writing to be available on a given socket. + """Waits for writing to be available on a given socket. Returns True if the socket is readable, or False if the timeout expired. """ return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/venv/Lib/site-packages/pip/_vendor/vendor.txt b/venv/Lib/site-packages/pip/_vendor/vendor.txt index 74ecca4252e98e13a698432077fa9028308ad4e5..ab2d6152890d02fecbaa0d2cdb55d5a6d91d4f63 100644 --- a/venv/Lib/site-packages/pip/_vendor/vendor.txt +++ b/venv/Lib/site-packages/pip/_vendor/vendor.txt @@ -1,24 +1,22 @@ -appdirs==1.4.3 -CacheControl==0.12.6 -colorama==0.4.3 -contextlib2==0.6.0.post1 -distlib==0.3.0 -distro==1.5.0 -html5lib==1.0.1 -ipaddress==1.0.23 # Only needed on 2.6 and 2.7 -msgpack==1.0.0 -packaging==20.3 -pep517==0.8.2 -progress==1.5 +CacheControl==0.12.6 # Make sure to update the license in pyproject.toml for this. +colorama==0.4.4 +distlib==0.3.3 +distro==1.6.0 +html5lib==1.1 +msgpack==1.0.2 +packaging==21.0 +pep517==0.12.0 +platformdirs==2.4.0 +progress==1.6 pyparsing==2.4.7 -requests==2.23.0 - certifi==2020.04.05.1 - chardet==3.0.4 - idna==2.9 - urllib3==1.25.8 -resolvelib==0.3.0 -retrying==1.3.3 +requests==2.26.0 + certifi==2021.05.30 + chardet==4.0.0 + idna==3.2 + urllib3==1.26.7 +resolvelib==0.8.0 setuptools==44.0.0 -six==1.14.0 -toml==0.10.0 +six==1.16.0 +tenacity==8.0.1 +tomli==1.0.3 webencodings==0.5.1 diff --git a/venv/Lib/site-packages/pkg_resources/__init__.py b/venv/Lib/site-packages/pkg_resources/__init__.py index 5927ef0dfe98f49d29470fb7acf80e3c9fc20de2..955fdc48b60cc6f9bc03916039a379a741a7d93a 100644 --- a/venv/Lib/site-packages/pkg_resources/__init__.py +++ b/venv/Lib/site-packages/pkg_resources/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Package resource API -------------------- @@ -15,8 +14,6 @@ The package resource API is designed to work with normal filesystem packages, method. """ -from __future__ import absolute_import - import sys import os import io @@ -41,6 +38,7 @@ import itertools import inspect import ntpath import posixpath +import importlib from pkgutil import get_importer try: @@ -54,9 +52,6 @@ try: except NameError: FileExistsError = OSError -from pkg_resources.extern import six -from pkg_resources.extern.six.moves import map, filter - # capture these to bypass sandboxing from os import utime try: @@ -83,18 +78,9 @@ __import__('pkg_resources.extern.packaging.specifiers') __import__('pkg_resources.extern.packaging.requirements') __import__('pkg_resources.extern.packaging.markers') - -__metaclass__ = type - - -if (3, 0) < sys.version_info < (3, 5): +if sys.version_info < (3, 5): raise RuntimeError("Python 3.5 or later is required") -if six.PY2: - # Those builtin exceptions are only defined in Python 3 - PermissionError = None - NotADirectoryError = None - # declare some globals that will be defined later to # satisfy the linters. require = None @@ -127,6 +113,11 @@ def parse_version(v): try: return packaging.version.Version(v) except packaging.version.InvalidVersion: + warnings.warn( + f"{v} is an invalid version and will not be supported in " + "a future release", + PkgResourcesDeprecationWarning, + ) return packaging.version.LegacyVersion(v) @@ -474,7 +465,7 @@ run_main = run_script def get_distribution(dist): """Return a current distribution object for a Requirement or string""" - if isinstance(dist, six.string_types): + if isinstance(dist, str): dist = Requirement.parse(dist) if isinstance(dist, Requirement): dist = get_provider(dist) @@ -711,7 +702,8 @@ class WorkingSet: keys2.append(dist.key) self._added_new(dist) - def resolve(self, requirements, env=None, installer=None, + # FIXME: 'WorkingSet.resolve' is too complex (11) + def resolve(self, requirements, env=None, installer=None, # noqa: C901 replace_conflicting=False, extras=None): """List all distributions needed to (recursively) meet `requirements` @@ -1418,8 +1410,6 @@ class NullProvider: return "" path = self._get_metadata_path(name) value = self._get(path) - if six.PY2: - return value try: return value.decode('utf-8') except UnicodeDecodeError as exc: @@ -1494,7 +1484,7 @@ class NullProvider: def _validate_resource_path(path): """ Validate the resource paths according to the docs. - https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access + https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access >>> warned = getfixture('recwarn') >>> warnings.simplefilter('always') @@ -1762,7 +1752,8 @@ class ZipProvider(EggProvider): timestamp = time.mktime(date_time) return timestamp, size - def _extract_resource(self, manager, zip_path): + # FIXME: 'ZipProvider._extract_resource' is too complex (12) + def _extract_resource(self, manager, zip_path): # noqa: C901 if zip_path in self._index(): for name in self._index()[zip_path]: @@ -1910,8 +1901,7 @@ class FileMetadata(EmptyProvider): return metadata def _warn_on_replacement(self, metadata): - # Python 2.7 compat for: replacement_char = '�' - replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + replacement_char = '�' if replacement_char in metadata: tmpl = "{self.path} could not be properly decoded in UTF-8" msg = tmpl.format(**locals()) @@ -2001,7 +1991,7 @@ def find_eggs_in_zip(importer, path_item, only=False): dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) for dist in dists: yield dist - elif subitem.lower().endswith('.dist-info'): + elif subitem.lower().endswith(('.dist-info', '.egg-info')): subpath = os.path.join(path_item, subitem) submeta = EggMetadata(zipimport.zipimporter(subpath)) submeta.egg_info = subpath @@ -2025,7 +2015,7 @@ def _by_version_descending(names): >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' >>> _by_version_descending(names) - ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'bar', 'foo'] >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' >>> _by_version_descending(names) ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] @@ -2033,13 +2023,22 @@ def _by_version_descending(names): >>> _by_version_descending(names) ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] """ + def try_parse(name): + """ + Attempt to parse as a version or return a null version. + """ + try: + return packaging.version.Version(name) + except Exception: + return packaging.version.Version('0') + def _by_version(name): """ Parse each component of the filename """ name, ext = os.path.splitext(name) parts = itertools.chain(name.split('-'), [ext]) - return [packaging.version.parse(part) for part in parts] + return [try_parse(part) for part in parts] return sorted(names, key=_by_version, reverse=True) @@ -2056,7 +2055,10 @@ def find_on_path(importer, path_item, only=False): ) return - entries = safe_listdir(path_item) + entries = ( + os.path.join(path_item, child) + for child in safe_listdir(path_item) + ) # for performance, before sorting by version, # screen entries for only those that will yield @@ -2106,8 +2108,6 @@ class NoDists: """ def __bool__(self): return False - if six.PY2: - __nonzero__ = __bool__ def __call__(self, fullpath): return iter(()) @@ -2124,12 +2124,7 @@ def safe_listdir(path): except OSError as e: # Ignore the directory if does not exist, not a directory or # permission denied - ignorable = ( - e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) - # Python 2 on Windows needs to be handled this way :( - or getattr(e, "winerror", None) == 267 - ) - if not ignorable: + if e.errno not in (errno.ENOTDIR, errno.EACCES, errno.ENOENT): raise return () @@ -2231,7 +2226,7 @@ def _handle_ns(packageName, path_item): if subpath is not None: path = module.__path__ path.append(subpath) - loader.load_module(packageName) + importlib.import_module(packageName) _rebuild_mod_path(path, packageName, module) return subpath @@ -2372,7 +2367,15 @@ def _is_egg_path(path): """ Determine if given path appears to be an egg. """ - return path.lower().endswith('.egg') + return _is_zip_egg(path) or _is_unpacked_egg(path) + + +def _is_zip_egg(path): + return ( + path.lower().endswith('.egg') and + os.path.isfile(path) and + zipfile.is_zipfile(path) + ) def _is_unpacked_egg(path): @@ -2380,7 +2383,7 @@ def _is_unpacked_egg(path): Determine if given path appears to be an unpacked egg. """ return ( - _is_egg_path(path) and + path.lower().endswith('.egg') and os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) ) @@ -2393,18 +2396,19 @@ def _set_parent_ns(packageName): setattr(sys.modules[parent], name, sys.modules[packageName]) -def yield_lines(strs): - """Yield non-empty/non-comment lines of a string or sequence""" - if isinstance(strs, six.string_types): - for s in strs.splitlines(): - s = s.strip() - # skip blank lines/comments - if s and not s.startswith('#'): - yield s - else: - for ss in strs: - for s in yield_lines(ss): - yield s +def _nonblank(str): + return str and not str.startswith('#') + + +@functools.singledispatch +def yield_lines(iterable): + """Yield valid lines of a string or iterable""" + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return filter(_nonblank, map(str.strip, text.splitlines())) MODULE = re.compile(r"\w+(\.\w+)*$").match @@ -2833,10 +2837,6 @@ class Distribution: ) ) - if not hasattr(object, '__dir__'): - # python 2.7 not supported - del __dir__ - @classmethod def from_filename(cls, filename, metadata=None, **kw): return cls.from_location( @@ -2876,7 +2876,8 @@ class Distribution: """Return the EntryPoint object for `group`+`name`, or ``None``""" return self.get_entry_map(group).get(name) - def insert_on(self, path, loc=None, replace=False): + # FIXME: 'Distribution.insert_on' is too complex (13) + def insert_on(self, path, loc=None, replace=False): # noqa: C901 """Ensure self.location is on path If replace=False (default): @@ -3253,6 +3254,15 @@ def _initialize(g=globals()): ) +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ + + @_call_aside def _initialize_master_working_set(): """ @@ -3291,12 +3301,3 @@ def _initialize_master_working_set(): # match order list(map(working_set.add_entry, sys.path)) globals().update(locals()) - - -class PkgResourcesDeprecationWarning(Warning): - """ - Base class for warning about deprecations in ``pkg_resources`` - - This class is not derived from ``DeprecationWarning``, and as such is - visible by default. - """ diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py index dc95138d049ba3194964d528b552a6d1514fa382..c359122f97125ed630760029f7fd0689f1caefd3 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py @@ -1,7 +1,6 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function __all__ = [ "__title__", @@ -18,10 +17,10 @@ __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "19.2" +__version__ = "21.2" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" -__license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py index a0cf67df5245be16a020ca048832e180f7ce8661..3c50c5dcfeeda2efed282200a5c5cc8c5f7542f7 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -1,7 +1,6 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function from .__about__ import ( __author__, diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_compat.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_compat.py deleted file mode 100644 index 25da473c196855ad59a6d2d785ef1ddef49795be..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_compat.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import sys - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = (str,) -else: - string_types = (basestring,) - - -def with_metaclass(meta, *bases): - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py index 68dcca634d8e3f0081bad2f9ae5e653a2942db68..951549753afa255148c7c60d868303963f8c1813 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py @@ -1,68 +1,67 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function -class Infinity(object): - def __repr__(self): +class InfinityType: + def __repr__(self) -> str: return "Infinity" - def __hash__(self): + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): + def __lt__(self, other: object) -> bool: return False - def __le__(self, other): + def __le__(self, other: object) -> bool: return False - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): + def __gt__(self, other: object) -> bool: return True - def __ge__(self, other): + def __ge__(self, other: object) -> bool: return True - def __neg__(self): + def __neg__(self: object) -> "NegativeInfinityType": return NegativeInfinity -Infinity = Infinity() +Infinity = InfinityType() -class NegativeInfinity(object): - def __repr__(self): +class NegativeInfinityType: + def __repr__(self) -> str: return "-Infinity" - def __hash__(self): + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): + def __lt__(self, other: object) -> bool: return True - def __le__(self, other): + def __le__(self, other: object) -> bool: return True - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): + def __gt__(self, other: object) -> bool: return False - def __ge__(self, other): + def __ge__(self, other: object) -> bool: return False - def __neg__(self): + def __neg__(self: object) -> InfinityType: return Infinity -NegativeInfinity = NegativeInfinity() +NegativeInfinity = NegativeInfinityType() diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py index 733123fb3220fc5d732848ffe4336f4b61dc2c70..18769b09a8a34f1e7d63cc61e62cd128ff5f9484 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -1,20 +1,26 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import operator import os import platform import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from pkg_resources.extern.pyparsing import ( # noqa: N817 + Forward, + Group, + Literal as L, + ParseException, + ParseResults, + QuotedString, + ZeroOrMore, + stringEnd, + stringStart, +) -from pkg_resources.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd -from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString -from pkg_resources.extern.pyparsing import Literal as L # noqa - -from ._compat import string_types -from .specifiers import Specifier, InvalidSpecifier - +from .specifiers import InvalidSpecifier, Specifier __all__ = [ "InvalidMarker", @@ -24,6 +30,8 @@ __all__ = [ "default_environment", ] +Operator = Callable[[str, str], bool] + class InvalidMarker(ValueError): """ @@ -44,32 +52,32 @@ class UndefinedEnvironmentName(ValueError): """ -class Node(object): - def __init__(self, value): +class Node: + def __init__(self, value: Any) -> None: self.value = value - def __str__(self): + def __str__(self) -> str: return str(self.value) - def __repr__(self): - return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" - def serialize(self): + def serialize(self) -> str: raise NotImplementedError class Variable(Node): - def serialize(self): + def serialize(self) -> str: return str(self) class Value(Node): - def serialize(self): - return '"{0}"'.format(self) + def serialize(self) -> str: + return f'"{self}"' class Op(Node): - def serialize(self): + def serialize(self) -> str: return str(self) @@ -85,13 +93,13 @@ VARIABLE = ( | L("python_version") | L("sys_platform") | L("os_name") - | L("os.name") + | L("os.name") # PEP-345 | L("sys.platform") # PEP-345 | L("platform.version") # PEP-345 | L("platform.machine") # PEP-345 | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # PEP-345 - | L("extra") # undocumented setuptools legacy + | L("python_implementation") # undocumented setuptools legacy + | L("extra") # PEP-508 ) ALIASES = { "os.name": "os_name", @@ -130,15 +138,18 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) MARKER = stringStart + MARKER_EXPR + stringEnd -def _coerce_parse_result(results): +def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: if isinstance(results, ParseResults): return [_coerce_parse_result(i) for i in results] else: return results -def _format_marker(marker, first=True): - assert isinstance(marker, (list, tuple, string_types)) +def _format_marker( + marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) # Sometimes we have a structure like [[...]] which is a single item list # where the single item is itself it's own list. In that case we want skip @@ -163,7 +174,7 @@ def _format_marker(marker, first=True): return marker -_operators = { +_operators: Dict[str, Operator] = { "in": lambda lhs, rhs: lhs in rhs, "not in": lambda lhs, rhs: lhs not in rhs, "<": operator.lt, @@ -175,7 +186,7 @@ _operators = { } -def _eval_op(lhs, op, rhs): +def _eval_op(lhs: str, op: Op, rhs: str) -> bool: try: spec = Specifier("".join([op.serialize(), rhs])) except InvalidSpecifier: @@ -183,34 +194,36 @@ def _eval_op(lhs, op, rhs): else: return spec.contains(lhs) - oper = _operators.get(op.serialize()) + oper: Optional[Operator] = _operators.get(op.serialize()) if oper is None: - raise UndefinedComparison( - "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) - ) + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") return oper(lhs, rhs) -_undefined = object() +class Undefined: + pass + + +_undefined = Undefined() -def _get_env(environment, name): - value = environment.get(name, _undefined) +def _get_env(environment: Dict[str, str], name: str) -> str: + value: Union[str, Undefined] = environment.get(name, _undefined) - if value is _undefined: + if isinstance(value, Undefined): raise UndefinedEnvironmentName( - "{0!r} does not exist in evaluation environment.".format(name) + f"{name!r} does not exist in evaluation environment." ) return value -def _evaluate_markers(markers, environment): - groups = [[]] +def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] for marker in markers: - assert isinstance(marker, (list, tuple, string_types)) + assert isinstance(marker, (list, tuple, str)) if isinstance(marker, list): groups[-1].append(_evaluate_markers(marker, environment)) @@ -233,7 +246,7 @@ def _evaluate_markers(markers, environment): return any(all(item) for item in groups) -def format_full_version(info): +def format_full_version(info: "sys._version_info") -> str: version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel if kind != "final": @@ -241,14 +254,9 @@ def format_full_version(info): return version -def default_environment(): - if hasattr(sys, "implementation"): - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name - else: - iver = "0" - implementation_name = "" - +def default_environment() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name return { "implementation_name": implementation_name, "implementation_version": iver, @@ -264,23 +272,23 @@ def default_environment(): } -class Marker(object): - def __init__(self, marker): +class Marker: + def __init__(self, marker: str) -> None: try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: - err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc : e.loc + 8] + raise InvalidMarker( + f"Invalid marker: {marker!r}, parse error at " + f"{marker[e.loc : e.loc + 8]!r}" ) - raise InvalidMarker(err_str) - def __str__(self): + def __str__(self) -> str: return _format_marker(self._markers) - def __repr__(self): - return "".format(str(self)) + def __repr__(self) -> str: + return f"" - def evaluate(self, environment=None): + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: """Evaluate a marker. Return the boolean from evaluating the given marker against the diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py index c3424dcb64a2e092bfa8a815c30e1b1579fc568e..6af14ec4ce49e633d030611c26f0bd9beaf13e6a 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py @@ -1,15 +1,24 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function -import string import re - -from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from pkg_resources.extern.pyparsing import Literal as L # noqa -from pkg_resources.extern.six.moves.urllib import parse as urlparse +import string +import urllib.parse +from typing import List, Optional as TOptional, Set + +from pkg_resources.extern.pyparsing import ( # noqa + Combine, + Literal as L, + Optional, + ParseException, + Regex, + Word, + ZeroOrMore, + originalTextFor, + stringEnd, + stringStart, +) from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet @@ -51,7 +60,7 @@ VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY VERSION_MANY = Combine( VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False )("_raw_spec") -_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) _VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") @@ -75,7 +84,7 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd REQUIREMENT.parseString("x[]") -class Requirement(object): +class Requirement: """Parse a requirement. Parse a given requirement string into its parts, such as name, specifier, @@ -88,51 +97,50 @@ class Requirement(object): # the thing as well as the version? What about the markers? # TODO: Can we normalize the name and extra name? - def __init__(self, requirement_string): + def __init__(self, requirement_string: str) -> None: try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( - 'Parse error at "{0!r}": {1}'.format( - requirement_string[e.loc : e.loc + 8], e.msg - ) + f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' ) - self.name = req.name + self.name: str = req.name if req.url: - parsed_url = urlparse.urlparse(req.url) + parsed_url = urllib.parse.urlparse(req.url) if parsed_url.scheme == "file": - if urlparse.urlunparse(parsed_url) != req.url: + if urllib.parse.urlunparse(parsed_url) != req.url: raise InvalidRequirement("Invalid URL given") elif not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): - raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url + raise InvalidRequirement(f"Invalid URL: {req.url}") + self.url: TOptional[str] = req.url else: self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None + self.extras: Set[str] = set(req.extras.asList() if req.extras else []) + self.specifier: SpecifierSet = SpecifierSet(req.specifier) + self.marker: TOptional[Marker] = req.marker if req.marker else None - def __str__(self): - parts = [self.name] + def __str__(self) -> str: + parts: List[str] = [self.name] if self.extras: - parts.append("[{0}]".format(",".join(sorted(self.extras)))) + formatted_extras = ",".join(sorted(self.extras)) + parts.append(f"[{formatted_extras}]") if self.specifier: parts.append(str(self.specifier)) if self.url: - parts.append("@ {0}".format(self.url)) + parts.append(f"@ {self.url}") if self.marker: parts.append(" ") if self.marker: - parts.append("; {0}".format(self.marker)) + parts.append(f"; {self.marker}") return "".join(parts) - def __repr__(self): - return "".format(str(self)) + def __repr__(self) -> str: + return f"" diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py index 743576a080a0af8d0995f307ea6afc645b13ca61..ce66bd4addbde1e332e9a42f6eb62adc471193e5 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py @@ -1,15 +1,33 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import abc import functools import itertools import re - -from ._compat import string_types, with_metaclass -from .version import Version, LegacyVersion, parse +import warnings +from typing import ( + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Pattern, + Set, + Tuple, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import LegacyVersion, Version, parse + +ParsedVersion = Union[Version, LegacyVersion] +UnparsedVersion = Union[Version, LegacyVersion, str] +VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) +CallableOperator = Callable[[ParsedVersion, str], bool] class InvalidSpecifier(ValueError): @@ -18,56 +36,58 @@ class InvalidSpecifier(ValueError): """ -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): +class BaseSpecifier(metaclass=abc.ABCMeta): @abc.abstractmethod - def __str__(self): + def __str__(self) -> str: """ Returns the str representation of this Specifier like object. This should be representative of the Specifier itself. """ @abc.abstractmethod - def __hash__(self): + def __hash__(self) -> int: """ Returns a hash value for this Specifier like object. """ @abc.abstractmethod - def __eq__(self, other): + def __eq__(self, other: object) -> bool: """ Returns a boolean representing whether or not the two Specifier like objects are equal. """ @abc.abstractmethod - def __ne__(self, other): + def __ne__(self, other: object) -> bool: """ Returns a boolean representing whether or not the two Specifier like objects are not equal. """ @abc.abstractproperty - def prereleases(self): + def prereleases(self) -> Optional[bool]: """ Returns whether or not pre-releases as a whole are allowed by this specifier. """ @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: """ Sets whether or not pre-releases as a whole are allowed by this specifier. """ @abc.abstractmethod - def contains(self, item, prereleases=None): + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod - def filter(self, iterable, prereleases=None): + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. @@ -76,48 +96,56 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): class _IndividualSpecifier(BaseSpecifier): - _operators = {} + _operators: Dict[str, str] = {} + _regex: Pattern[str] - def __init__(self, spec="", prereleases=None): + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: match = self._regex.search(spec) if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - self._spec = (match.group("operator").strip(), match.group("version").strip()) + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases - def __repr__(self): + def __repr__(self) -> str: pre = ( - ", prereleases={0!r}".format(self.prereleases) + f", prereleases={self.prereleases!r}" if self._prereleases is not None else "" ) - return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) + return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre) - def __str__(self): - return "{0}{1}".format(*self._spec) + def __str__(self) -> str: + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + return self._spec[0], canonicalize_version(self._spec[1]) - def __hash__(self): - return hash(self._spec) + def __hash__(self) -> int: + return hash(self._canonical_spec) - def __eq__(self, other): - if isinstance(other, string_types): + def __eq__(self, other: object) -> bool: + if isinstance(other, str): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): return NotImplemented - return self._spec == other._spec + return self._canonical_spec == other._canonical_spec - def __ne__(self, other): - if isinstance(other, string_types): + def __ne__(self, other: object) -> bool: + if isinstance(other, str): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): @@ -125,53 +153,63 @@ class _IndividualSpecifier(BaseSpecifier): return self._spec != other._spec - def _get_operator(self, op): - return getattr(self, "_compare_{0}".format(self._operators[op])) + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable - def _coerce_version(self, version): + def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: if not isinstance(version, (LegacyVersion, Version)): version = parse(version) return version @property - def operator(self): + def operator(self) -> str: return self._spec[0] @property - def version(self): + def version(self) -> str: return self._spec[1] @property - def prereleases(self): + def prereleases(self) -> Optional[bool]: return self._prereleases @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: self._prereleases = value - def __contains__(self, item): + def __contains__(self, item: str) -> bool: return self.contains(item) - def contains(self, item, prereleases=None): + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + # Determine if prereleases are to be allowed or not. if prereleases is None: prereleases = self.prereleases # Normalize item to a Version or LegacyVersion, this allows us to have # a shortcut for ``"2.0" in Specifier(">=2") - item = self._coerce_version(item) + normalized_item = self._coerce_version(item) # Determine if we should be supporting prereleases in this specifier # or not, if we do not support prereleases than we can short circuit # logic if this version is a prereleases. - if item.is_prerelease and not prereleases: + if normalized_item.is_prerelease and not prereleases: return False # Actually do the comparison to determine if this item is contained # within this Specifier or not. - return self._get_operator(self.operator)(item, self.version) + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: - def filter(self, iterable, prereleases=None): yielded = False found_prereleases = [] @@ -184,7 +222,7 @@ class _IndividualSpecifier(BaseSpecifier): if self.contains(parsed_version, **kw): # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing + # prereleases, then we'll store it for later in case nothing # else matches this specifier. if parsed_version.is_prerelease and not ( prereleases or self.prereleases @@ -229,33 +267,46 @@ class LegacySpecifier(_IndividualSpecifier): ">": "greater_than", } - def _coerce_version(self, version): + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + super().__init__(spec, prereleases) + + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: if not isinstance(version, LegacyVersion): version = LegacyVersion(str(version)) return version - def _compare_equal(self, prospective, spec): + def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective == self._coerce_version(spec) - def _compare_not_equal(self, prospective, spec): + def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective != self._coerce_version(spec) - def _compare_less_than_equal(self, prospective, spec): + def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective <= self._coerce_version(spec) - def _compare_greater_than_equal(self, prospective, spec): + def _compare_greater_than_equal( + self, prospective: LegacyVersion, spec: str + ) -> bool: return prospective >= self._coerce_version(spec) - def _compare_less_than(self, prospective, spec): + def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: return prospective < self._coerce_version(spec) - def _compare_greater_than(self, prospective, spec): + def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: return prospective > self._coerce_version(spec) -def _require_version_compare(fn): +def _require_version_compare( + fn: Callable[["Specifier", ParsedVersion, str], bool] +) -> Callable[["Specifier", ParsedVersion, str], bool]: @functools.wraps(fn) - def wrapped(self, prospective, spec): + def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: if not isinstance(prospective, Version): return False return fn(self, prospective, spec) @@ -372,7 +423,8 @@ class Specifier(_IndividualSpecifier): } @_require_version_compare - def _compare_compatible(self, prospective, spec): + def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: + # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to # implement this in terms of the other specifiers instead of @@ -380,15 +432,9 @@ class Specifier(_IndividualSpecifier): # the other specifiers. # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. + # ignore suffix segments. prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] ) # Add the prefix notation to the end of our string @@ -399,57 +445,73 @@ class Specifier(_IndividualSpecifier): ) @_require_version_compare - def _compare_equal(self, prospective, spec): + def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: + # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. prospective = Version(prospective.public) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. - spec = _version_split(spec[:-2]) # Remove the trailing .* + split_spec = _version_split(spec[:-2]) # Remove the trailing .* # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. - prospective = _version_split(str(prospective)) + split_prospective = _version_split(str(prospective)) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - prospective = prospective[: len(spec)] + shortened_prospective = split_prospective[: len(split_spec)] # Pad out our two sides with zeros so that they both equal the same # length. - spec, prospective = _pad_version(spec, prospective) + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec else: # Convert our spec string into a Version - spec = Version(spec) + spec_version = Version(spec) # If the specifier does not have a local segment, then we want to # act as if the prospective version also does not have a local # segment. - if not spec.local: + if not spec_version.local: prospective = Version(prospective.public) - return prospective == spec + return prospective == spec_version @_require_version_compare - def _compare_not_equal(self, prospective, spec): + def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: return not self._compare_equal(prospective, spec) @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - return prospective <= Version(spec) + def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - return prospective >= Version(spec) + def _compare_greater_than_equal( + self, prospective: ParsedVersion, spec: str + ) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) @_require_version_compare - def _compare_less_than(self, prospective, spec): + def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is less than the spec # version. If it's not we can short circuit and just return False now @@ -471,10 +533,11 @@ class Specifier(_IndividualSpecifier): return True @_require_version_compare - def _compare_greater_than(self, prospective, spec): + def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is greater than the spec # version. If it's not we can short circuit and just return False now @@ -501,11 +564,12 @@ class Specifier(_IndividualSpecifier): # same version in the spec. return True - def _compare_arbitrary(self, prospective, spec): + def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: return str(prospective).lower() == str(spec).lower() @property - def prereleases(self): + def prereleases(self) -> bool: + # If there is an explicit prereleases set for this, then we'll just # blindly use that. if self._prereleases is not None: @@ -529,15 +593,15 @@ class Specifier(_IndividualSpecifier): return False @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: self._prereleases = value _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") -def _version_split(version): - result = [] +def _version_split(version: str) -> List[str]: + result: List[str] = [] for item in version.split("."): match = _prefix_regex.search(item) if match: @@ -547,7 +611,13 @@ def _version_split(version): return result -def _pad_version(left, right): +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: left_split, right_split = [], [] # Get the release segment of our versions @@ -566,15 +636,18 @@ def _pad_version(left, right): class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): - # Split on , to break each indidivual specifier into it's own item, and + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + + # Split on , to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in specifiers: + parsed: Set[_IndividualSpecifier] = set() + for specifier in split_specifiers: try: parsed.add(Specifier(specifier)) except InvalidSpecifier: @@ -587,23 +660,23 @@ class SpecifierSet(BaseSpecifier): # we accept prereleases or not. self._prereleases = prereleases - def __repr__(self): + def __repr__(self) -> str: pre = ( - ", prereleases={0!r}".format(self.prereleases) + f", prereleases={self.prereleases!r}" if self._prereleases is not None else "" ) - return "".format(str(self), pre) + return "".format(str(self), pre) - def __str__(self): + def __str__(self) -> str: return ",".join(sorted(str(s) for s in self._specs)) - def __hash__(self): + def __hash__(self) -> int: return hash(self._specs) - def __and__(self, other): - if isinstance(other, string_types): + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + if isinstance(other, str): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -625,34 +698,31 @@ class SpecifierSet(BaseSpecifier): return specifier - def __eq__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + def __eq__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs == other._specs - def __ne__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + def __ne__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs != other._specs - def __len__(self): + def __len__(self) -> int: return len(self._specs) - def __iter__(self): + def __iter__(self) -> Iterator[_IndividualSpecifier]: return iter(self._specs) @property - def prereleases(self): + def prereleases(self) -> Optional[bool]: + # If we have been given an explicit prerelease modifier, then we'll # pass that through here. if self._prereleases is not None: @@ -669,13 +739,16 @@ class SpecifierSet(BaseSpecifier): return any(s.prereleases for s in self._specs) @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: self._prereleases = value - def __contains__(self, item): + def __contains__(self, item: UnparsedVersion) -> bool: return self.contains(item) - def contains(self, item, prereleases=None): + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + # Ensure that our item is a Version or LegacyVersion instance. if not isinstance(item, (LegacyVersion, Version)): item = parse(item) @@ -701,7 +774,10 @@ class SpecifierSet(BaseSpecifier): # will always return True, this is an explicit design decision. return all(s.contains(item, prereleases=prereleases) for s in self._specs) - def filter(self, iterable, prereleases=None): + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: + # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. @@ -719,8 +795,11 @@ class SpecifierSet(BaseSpecifier): # which will filter out any pre-releases, unless there are no final # releases, and which will filter out LegacyVersion in general. else: - filtered = [] - found_prereleases = [] + filtered: List[VersionTypeVar] = [] + found_prereleases: List[VersionTypeVar] = [] + + item: UnparsedVersion + parsed_version: Union[Version, LegacyVersion] for item in iterable: # Ensure that we some kind of Version class for this item. diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py index ec9942f0f6627f34554082a8c0909bc70bd2a260..e65890a90cd709489865750e953bf347720c75cd 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py @@ -2,25 +2,32 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import - -import distutils.util - -try: - from importlib.machinery import EXTENSION_SUFFIXES -except ImportError: # pragma: no cover - import imp - - EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] - del imp +import logging import platform -import re import sys import sysconfig -import warnings - - -INTERPRETER_SHORT_NAMES = { +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { "python": "py", # Generic. "cpython": "cp", "pypy": "pp", @@ -32,45 +39,67 @@ INTERPRETER_SHORT_NAMES = { _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 -class Tag(object): +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ - __slots__ = ["_interpreter", "_abi", "_platform"] + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] - def __init__(self, interpreter, abi, platform): + def __init__(self, interpreter: str, abi: str, platform: str) -> None: self._interpreter = interpreter.lower() self._abi = abi.lower() self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) @property - def interpreter(self): + def interpreter(self) -> str: return self._interpreter @property - def abi(self): + def abi(self) -> str: return self._abi @property - def platform(self): + def platform(self) -> str: return self._platform - def __eq__(self, other): + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + return ( - (self.platform == other.platform) - and (self.abi == other.abi) - and (self.interpreter == other.interpreter) + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) ) - def __hash__(self): - return hash((self._interpreter, self._abi, self._platform)) + def __hash__(self) -> int: + return self._hash - def __str__(self): - return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" - def __repr__(self): + def __repr__(self) -> str: return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) -def parse_tag(tag): +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ tags = set() interpreters, abis, platforms = tag.split("-") for interpreter in interpreters.split("."): @@ -80,20 +109,34 @@ def parse_tag(tag): return frozenset(tags) -def _normalize_string(string): +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string: str) -> str: return string.replace(".", "_").replace("-", "_") -def _cpython_interpreter(py_version): - # TODO: Is using py_version_nodot for interpreter version critical? - return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) +def _abi3_applies(python_version: PythonVersion) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version): +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: + py_version = tuple(py_version) # To allow for version comparison. abis = [] - version = "{}{}".format(*py_version[:2]) + version = _version_nodot(py_version[:2]) debug = pymalloc = ucs4 = "" - with_debug = sysconfig.get_config_var("Py_DEBUG") + with_debug = _get_config_var("Py_DEBUG", warn) has_refcount = hasattr(sys, "gettotalrefcount") # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled # extension modules is the best option. @@ -102,11 +145,11 @@ def _cpython_abis(py_version): if with_debug or (with_debug is None and (has_refcount or has_ext)): debug = "d" if py_version < (3, 8): - with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) if with_pymalloc or with_pymalloc is None: pymalloc = "m" if py_version < (3, 3): - unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) if unicode_size == 4 or ( unicode_size is None and sys.maxunicode == 0x10FFFF ): @@ -114,7 +157,7 @@ def _cpython_abis(py_version): elif debug: # Debug builds can also load "normal" extension modules. # We can also assume no UCS-4 or pymalloc requirement. - abis.append("cp{version}".format(version=version)) + abis.append(f"cp{version}") abis.insert( 0, "cp{version}{debug}{pymalloc}{ucs4}".format( @@ -124,86 +167,140 @@ def _cpython_abis(py_version): return abis -def _cpython_tags(py_version, interpreter, abis, platforms): +def cpython_tags( + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = "cp{}".format(_version_nodot(python_version[:2])) + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or platform_tags()) for abi in abis: for platform_ in platforms: yield Tag(interpreter, abi, platform_) - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): - yield tag - # PEP 384 was first implemented in Python 3.2. - for minor_version in range(py_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{major}{minor}".format( - major=py_version[0], minor=minor_version - ) - yield Tag(interpreter, "abi3", platform_) - - -def _pypy_interpreter(): - return "pp{py_major}{pypy_major}{pypy_minor}".format( - py_major=sys.version_info[0], - pypy_major=sys.pypy_version_info.major, - pypy_minor=sys.pypy_version_info.minor, - ) + if _abi3_applies(python_version): + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) -def _generic_abi(): +def _generic_abi() -> Iterator[str]: abi = sysconfig.get_config_var("SOABI") if abi: - return _normalize_string(abi) - else: - return "none" + yield _normalize_string(abi) -def _pypy_tags(py_version, interpreter, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform) for platform in platforms): - yield tag +def generic_tags( + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a generic interpreter. + The tags consist of: + - -- -def _generic_tags(interpreter, py_version, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - if abi != "none": - tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) - for tag in tags: - yield tag + The "none" ABI will be added if it was not explicitly provided. + """ + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) -def _py_interpreter_range(py_version): +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: """ - Yield Python versions in descending order. + Yields Python versions in descending order. After the latest version, the major-only version will be yielded, and then - all following versions up to 'end'. + all previous versions of that major version. """ - yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + if len(py_version) > 1: + yield "py{version}".format(version=_version_nodot(py_version[:2])) yield "py{major}".format(major=py_version[0]) - for minor in range(py_version[1] - 1, -1, -1): - yield "py{major}{minor}".format(major=py_version[0], minor=minor) + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield "py{version}".format(version=_version_nodot((py_version[0], minor))) -def _independent_tags(interpreter, py_version, platforms): +def compatible_tags( + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> Iterator[Tag]: """ - Return the sequence of tags that are consistent across implementations. + Yields the sequence of tags that are compatible with a specific version of Python. The tags consist of: - py*-none- - - -none-any + - -none-any # ... if `interpreter` is provided. - py*-none-any """ - for version in _py_interpreter_range(py_version): + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or platform_tags()) + for version in _py_interpreter_range(python_version): for platform_ in platforms: yield Tag(version, "none", platform_) - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(py_version): + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): yield Tag(version, "none", "any") -def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: if not is_32bit: return arch @@ -213,7 +310,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): return "i386" -def _mac_binary_formats(version, cpu_arch): +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -236,169 +333,152 @@ def _mac_binary_formats(version, cpu_arch): return [] formats.extend(["fat32", "fat"]) - formats.append("universal") + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + return formats -def _mac_platforms(version=None, arch=None): +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> Iterator[str]: + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ version_str, _, cpu_arch = platform.mac_ver() if version is None: - version = tuple(map(int, version_str.split(".")[:2])) + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version if arch is None: arch = _mac_arch(cpu_arch) - platforms = [] - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - platforms.append( - "macosx_{major}_{minor}_{binary_format}".format( + else: + arch = arch + + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( major=compat_version[0], minor=compat_version[1], binary_format=binary_format, ) - ) - return platforms - - -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # Check for presence of _manylinux module. - try: - import _manylinux - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. - pass - - return _have_compatible_glibc(*glibc_version) - - -def _glibc_version_string(): - # Returns glibc version string, or None if not using glibc. - import ctypes - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - process_namespace = ctypes.CDLL(None) - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # Parse string and check against requested version. - # - # We use a regexp instead of str.split because we want to discard any - # random junk that might come after the minor version -- this might happen - # in patched/forked versions of glibc (e.g. Linaro's version of glibc - # uses version strings like "2.20-2014.11"). See gh-3588. - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) -def _have_compatible_glibc(required_major, minimum_minor): - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + _, arch = linux.split("_", 1) + yield from _manylinux.platform_tags(linux, arch) + yield from _musllinux.platform_tags(arch) + yield linux -def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): - linux = _normalize_string(distutils.util.get_platform()) - if linux == "linux_x86_64" and is_32bit: - linux = "linux_i686" - manylinux_support = ( - ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) - ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) - ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) - ) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - platforms = [linux.replace("linux", name)] - break - else: - platforms = [] - # Support for a later manylinux implies support for an earlier version. - platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] - platforms.append(linux) - return platforms +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) -def _generic_platforms(): - platform = _normalize_string(distutils.util.get_platform()) - return [platform] +def platform_tags() -> Iterator[str]: + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() -def _interpreter_name(): - name = platform.python_implementation().lower() +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + """ + name = sys.implementation.name return INTERPRETER_SHORT_NAMES.get(name) or name -def _generic_interpreter(name, py_version): - version = sysconfig.get_config_var("py_version_nodot") - if not version: - version = "".join(map(str, py_version[:2])) - return "{name}{version}".format(name=name, version=version) +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the version of the running interpreter. + """ + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) -def sys_tags(): + +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: """ Returns the sequence of tag triples for the running interpreter. The order of the sequence corresponds to priority order for the interpreter, from most to least important. """ - py_version = sys.version_info[:2] - interpreter_name = _interpreter_name() - if platform.system() == "Darwin": - platforms = _mac_platforms() - elif platform.system() == "Linux": - platforms = _linux_platforms() - else: - platforms = _generic_platforms() - - if interpreter_name == "cp": - interpreter = _cpython_interpreter(py_version) - abis = _cpython_abis(py_version) - for tag in _cpython_tags(py_version, interpreter, abis, platforms): - yield tag - elif interpreter_name == "pp": - interpreter = _pypy_interpreter() - abi = _generic_abi() - for tag in _pypy_tags(py_version, interpreter, abi, platforms): - yield tag + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) else: - interpreter = _generic_interpreter(interpreter_name, py_version) - abi = _generic_abi() - for tag in _generic_tags(interpreter, py_version, abi, platforms): - yield tag - for tag in _independent_tags(interpreter, py_version, platforms): - yield tag + yield from generic_tags() + + yield from compatible_tags() diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py index 88418786933b8bc5f6179b8e191f60f79efd7074..bab11b80c60f10a4f3bccb12eb5b17c48a449767 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -1,57 +1,136 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import re +from typing import FrozenSet, NewType, Tuple, Union, cast +from .tags import Tag, parse_tag from .version import InvalidVersion, Version +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + _canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") -def canonicalize_name(name): +def canonicalize_name(name: str) -> NormalizedName: # This is taken from PEP 503. - return _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) -def canonicalize_version(version): +def canonicalize_version(version: Union[Version, str]) -> str: """ - This is very similar to Version.__str__, but has one subtle differences + This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. """ - - try: - version = Version(version) - except InvalidVersion: - # Legacy versions cannot be normalized - return version + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version parts = [] # Epoch - if version.epoch != 0: - parts.append("{0}!".format(version.epoch)) + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") # Release segment # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) # Pre-release - if version.pre is not None: - parts.append("".join(str(x) for x in version.pre)) + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) # Post-release - if version.post is not None: - parts.append(".post{0}".format(version.post)) + if parsed.post is not None: + parts.append(f".post{parsed.post}") # Development release - if version.dev is not None: - parts.append(".dev{0}".format(version.dev)) + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") # Local version segment - if version.local is not None: - parts.append("+{0}".format(version.local)) + if parsed.local is not None: + parts.append(f"+{parsed.local}") return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py index 95157a1f78c26829ffbe1bd2463f7735b636d16f..de9a09a4ed3b078b37e7490a6686f660ae935aca 100644 --- a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py @@ -1,24 +1,45 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import collections import itertools import re +import warnings +from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union -from ._structures import Infinity - +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType __all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] +InfiniteTypes = Union[InfinityType, NegativeInfinityType] +PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] +SubLocalType = Union[InfiniteTypes, int, str] +LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], +] +CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType +] +LegacyCmpKey = Tuple[int, Tuple[str, ...]] +VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool +] _Version = collections.namedtuple( "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) -def parse(version): +def parse(version: str) -> Union["LegacyVersion", "Version"]: """ Parse the given version string and return either a :class:`Version` object or a :class:`LegacyVersion` object depending on if the given version is @@ -36,88 +57,111 @@ class InvalidVersion(ValueError): """ -class _BaseVersion(object): - def __hash__(self): +class _BaseVersion: + _key: Union[CmpKey, LegacyCmpKey] + + def __hash__(self) -> int: return hash(self._key) - def __lt__(self, other): - return self._compare(other, lambda s, o: s < o) + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key - def __le__(self, other): - return self._compare(other, lambda s, o: s <= o) + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented - def __eq__(self, other): - return self._compare(other, lambda s, o: s == o) + return self._key == other._key - def __ge__(self, other): - return self._compare(other, lambda s, o: s >= o) + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented - def __gt__(self, other): - return self._compare(other, lambda s, o: s > o) + return self._key >= other._key - def __ne__(self, other): - return self._compare(other, lambda s, o: s != o) + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key - def _compare(self, other, method): + def __ne__(self, other: object) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented - return method(self._key, other._key) + return self._key != other._key class LegacyVersion(_BaseVersion): - def __init__(self, version): + def __init__(self, version: str) -> None: self._version = str(version) self._key = _legacy_cmpkey(self._version) - def __str__(self): + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def __str__(self) -> str: return self._version - def __repr__(self): - return "".format(repr(str(self))) + def __repr__(self) -> str: + return f"" @property - def public(self): + def public(self) -> str: return self._version @property - def base_version(self): + def base_version(self) -> str: return self._version @property - def epoch(self): + def epoch(self) -> int: return -1 @property - def release(self): + def release(self) -> None: return None @property - def pre(self): + def pre(self) -> None: return None @property - def post(self): + def post(self) -> None: return None @property - def dev(self): + def dev(self) -> None: return None @property - def local(self): + def local(self) -> None: return None @property - def is_prerelease(self): + def is_prerelease(self) -> bool: return False @property - def is_postrelease(self): + def is_postrelease(self) -> bool: return False @property - def is_devrelease(self): + def is_devrelease(self) -> bool: return False @@ -132,7 +176,7 @@ _legacy_version_replacement_map = { } -def _parse_version_parts(s): +def _parse_version_parts(s: str) -> Iterator[str]: for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -149,7 +193,8 @@ def _parse_version_parts(s): yield "*final" -def _legacy_cmpkey(version): +def _legacy_cmpkey(version: str) -> LegacyCmpKey: + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, @@ -158,7 +203,7 @@ def _legacy_cmpkey(version): # This scheme is taken from pkg_resources.parse_version setuptools prior to # it's adoption of the packaging library. - parts = [] + parts: List[str] = [] for part in _parse_version_parts(version.lower()): if part.startswith("*"): # remove "-" before a prerelease tag @@ -171,9 +216,8 @@ def _legacy_cmpkey(version): parts.pop() parts.append(part) - parts = tuple(parts) - return epoch, parts + return epoch, tuple(parts) # Deliberately not anchored to the start and end of the string, to make it @@ -214,11 +258,12 @@ class Version(_BaseVersion): _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) - def __init__(self, version): + def __init__(self, version: str) -> None: + # Validate the version and parse it into pieces match = self._regex.search(version) if not match: - raise InvalidVersion("Invalid version: '{0}'".format(version)) + raise InvalidVersion(f"Invalid version: '{version}'") # Store the parsed out pieces of the version self._version = _Version( @@ -242,15 +287,15 @@ class Version(_BaseVersion): self._version.local, ) - def __repr__(self): - return "".format(repr(str(self))) + def __repr__(self) -> str: + return f"" - def __str__(self): + def __str__(self) -> str: parts = [] # Epoch if self.epoch != 0: - parts.append("{0}!".format(self.epoch)) + parts.append(f"{self.epoch}!") # Release segment parts.append(".".join(str(x) for x in self.release)) @@ -261,56 +306,59 @@ class Version(_BaseVersion): # Post-release if self.post is not None: - parts.append(".post{0}".format(self.post)) + parts.append(f".post{self.post}") # Development release if self.dev is not None: - parts.append(".dev{0}".format(self.dev)) + parts.append(f".dev{self.dev}") # Local version segment if self.local is not None: - parts.append("+{0}".format(self.local)) + parts.append(f"+{self.local}") return "".join(parts) @property - def epoch(self): - return self._version.epoch + def epoch(self) -> int: + _epoch: int = self._version.epoch + return _epoch @property - def release(self): - return self._version.release + def release(self) -> Tuple[int, ...]: + _release: Tuple[int, ...] = self._version.release + return _release @property - def pre(self): - return self._version.pre + def pre(self) -> Optional[Tuple[str, int]]: + _pre: Optional[Tuple[str, int]] = self._version.pre + return _pre @property - def post(self): + def post(self) -> Optional[int]: return self._version.post[1] if self._version.post else None @property - def dev(self): + def dev(self) -> Optional[int]: return self._version.dev[1] if self._version.dev else None @property - def local(self): + def local(self) -> Optional[str]: if self._version.local: return ".".join(str(x) for x in self._version.local) else: return None @property - def public(self): + def public(self) -> str: return str(self).split("+", 1)[0] @property - def base_version(self): + def base_version(self) -> str: parts = [] # Epoch if self.epoch != 0: - parts.append("{0}!".format(self.epoch)) + parts.append(f"{self.epoch}!") # Release segment parts.append(".".join(str(x) for x in self.release)) @@ -318,19 +366,34 @@ class Version(_BaseVersion): return "".join(parts) @property - def is_prerelease(self): + def is_prerelease(self) -> bool: return self.dev is not None or self.pre is not None @property - def is_postrelease(self): + def is_postrelease(self) -> bool: return self.post is not None @property - def is_devrelease(self): + def is_devrelease(self) -> bool: return self.dev is not None + @property + def major(self) -> int: + return self.release[0] if len(self.release) >= 1 else 0 + + @property + def minor(self) -> int: + return self.release[1] if len(self.release) >= 2 else 0 + + @property + def micro(self) -> int: + return self.release[2] if len(self.release) >= 3 else 0 + + +def _parse_letter_version( + letter: str, number: Union[str, bytes, SupportsInt] +) -> Optional[Tuple[str, int]]: -def _parse_letter_version(letter, number): if letter: # We consider there to be an implicit 0 in a pre-release if there is # not a numeral associated with it. @@ -360,11 +423,13 @@ def _parse_letter_version(letter, number): return letter, int(number) + return None + _local_version_separators = re.compile(r"[\._-]") -def _parse_local_version(local): +def _parse_local_version(local: str) -> Optional[LocalType]: """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -373,15 +438,24 @@ def _parse_local_version(local): part.lower() if not part.isdigit() else int(part) for part in _local_version_separators.split(local) ) + return None + +def _cmpkey( + epoch: int, + release: Tuple[int, ...], + pre: Optional[Tuple[str, int]], + post: Optional[Tuple[str, int]], + dev: Optional[Tuple[str, int]], + local: Optional[Tuple[SubLocalType]], +) -> CmpKey: -def _cmpkey(epoch, release, pre, post, dev, local): # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now # leading zeros until we come to something non zero, then take the rest # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. - release = tuple( + _release = tuple( reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) ) @@ -390,23 +464,31 @@ def _cmpkey(epoch, release, pre, post, dev, local): # if there is not a pre or a post segment. If we have one of those then # the normal sorting rules will handle this case correctly. if pre is None and post is None and dev is not None: - pre = -Infinity + _pre: PrePostDevType = NegativeInfinity # Versions without a pre-release (except as noted above) should sort after # those with one. elif pre is None: - pre = Infinity + _pre = Infinity + else: + _pre = pre # Versions without a post segment should sort before those with one. if post is None: - post = -Infinity + _post: PrePostDevType = NegativeInfinity + + else: + _post = post # Versions without a development segment should sort after those with one. if dev is None: - dev = Infinity + _dev: PrePostDevType = Infinity + + else: + _dev = dev if local is None: # Versions without a local segment should sort before those with one. - local = -Infinity + _local: LocalType = NegativeInfinity else: # Versions with a local segment need that segment parsed to implement # the sorting rules in PEP440. @@ -415,6 +497,8 @@ def _cmpkey(epoch, release, pre, post, dev, local): # - Numeric segments sort numerically # - Shorter versions sort before longer versions when the prefixes # match exactly - local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local) + _local = tuple( + (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local + ) - return epoch, release, pre, post, dev, local + return epoch, _release, _pre, _post, _dev, _local diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/six.py b/venv/Lib/site-packages/pkg_resources/_vendor/six.py deleted file mode 100644 index 190c0239cd7d7af82a6e0cbc8d68053fa2e3dfaf..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pkg_resources/_vendor/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/pkg_resources/extern/__init__.py b/venv/Lib/site-packages/pkg_resources/extern/__init__.py index bf98d8f296030c2af71bb569b8d2d90483b729a7..fed59295403b80b1711d0d189ff97dde22808690 100644 --- a/venv/Lib/site-packages/pkg_resources/extern/__init__.py +++ b/venv/Lib/site-packages/pkg_resources/extern/__init__.py @@ -1,3 +1,4 @@ +import importlib.util import sys @@ -20,17 +21,10 @@ class VendorImporter: yield self.vendor_pkg + '.' yield '' - def find_module(self, fullname, path=None): - """ - Return self when fullname starts with root_name and the - target module is one vendored through this importer. - """ + def _module_matches_namespace(self, fullname): + """Figure out if the target module is vendored.""" root, base, target = fullname.partition(self.root_name + '.') - if root: - return - if not any(map(target.startswith, self.vendored_names)): - return - return self + return not root and any(map(target.startswith, self.vendored_names)) def load_module(self, fullname): """ @@ -54,6 +48,19 @@ class VendorImporter: "distribution.".format(**locals()) ) + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + + def find_spec(self, fullname, path=None, target=None): + """Return a module spec for vendored names.""" + return ( + importlib.util.spec_from_loader(fullname, self) + if self._module_matches_namespace(fullname) else None + ) + def install(self): """ Install this importer into sys.meta_path if not already present. @@ -62,5 +69,5 @@ class VendorImporter: sys.meta_path.append(self) -names = 'packaging', 'pyparsing', 'six', 'appdirs' +names = 'packaging', 'pyparsing', 'appdirs' VendorImporter(__name__, names).install() diff --git a/venv/Lib/site-packages/pygame-1.9.6.dist-info/INSTALLER b/venv/Lib/site-packages/pygame-1.9.6.dist-info/INSTALLER deleted file mode 100644 index a1b589e38a32041e49332e5e81c2d363dc418d68..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame-1.9.6.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/pygame-1.9.6.dist-info/METADATA b/venv/Lib/site-packages/pygame-1.9.6.dist-info/METADATA deleted file mode 100644 index b318aa79fa84192789b23d8ab731eea5f0c8b4ba..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame-1.9.6.dist-info/METADATA +++ /dev/null @@ -1,15 +0,0 @@ -Metadata-Version: 2.1 -Name: pygame -Version: 1.9.6 -Summary: Python Game Development -Home-page: https://www.pygame.org -Author: A community project. -Author-email: pygame@pygame.org -License: LGPL -Platform: UNKNOWN - -Pygame is a Python wrapper module for the -SDL multimedia library. It contains python functions and classes -that will allow you to use SDL's support for playing cdroms, -audio and video output, and keyboard, mouse and joystick input. - diff --git a/venv/Lib/site-packages/pygame-1.9.6.dist-info/RECORD b/venv/Lib/site-packages/pygame-1.9.6.dist-info/RECORD deleted file mode 100644 index c1750b8516d108a9567bc4bb02ca724adc97c020..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame-1.9.6.dist-info/RECORD +++ /dev/null @@ -1,496 +0,0 @@ -../../include/site/python3.7/pygame/_camera.h,sha256=4dX0hz1SNd_5xZn0kuwHw6-3pGxEOuj1XcENVbAeucg,840 -../../include/site/python3.7/pygame/_pygame.h,sha256=OQ9soShwz-VZtechnxP3TfBV1VM58l_u4w3p0D8Vu5c,28409 -../../include/site/python3.7/pygame/_surface.h,sha256=G6ICVNMqd3DqzBtTh1BxFxW1bs2fPrO__vcBUQtbwRM,958 -../../include/site/python3.7/pygame/bitmask.h,sha256=7FJpaFGWXPa581C333b1ObFfQkfc6i-bW1In5qjxwvg,4777 -../../include/site/python3.7/pygame/camera.h,sha256=jwdMpuVS1iAhbNgIjXvCcL-nolqbLdnwNlbrCjKfn24,6957 -../../include/site/python3.7/pygame/fastevents.h,sha256=OKgCTiH8K4ud2orTBJXeF5w3Mo0_ImbgRW5f5UTnCqY,1643 -../../include/site/python3.7/pygame/font.h,sha256=97S2JuIstaUlyTmmuEGGr_Mg33EhvDJZ5nlTTbU_6O0,1836 -../../include/site/python3.7/pygame/freetype.h,sha256=9OyIUWvVYfqIUZRNdTQpvVzdH2W7x21yjItkdc7GQZo,4175 -../../include/site/python3.7/pygame/mask.h,sha256=pKCMpOLRBF4KxnF-c9shrUPFbLbr6-LqDlEXHm_IwvM,620 -../../include/site/python3.7/pygame/mixer.h,sha256=hr5NeLohLT_ljX6FceMNK_10ASrIKhIyASK2YVtvupo,2258 -../../include/site/python3.7/pygame/palette.h,sha256=dzARYIsQdHAaV8ypCrQbYRWFisXYXABD4ToMlzYKojg,7057 -../../include/site/python3.7/pygame/pgarrinter.h,sha256=alsw7p6X7ukOB1o3curyrjWOcGHgVCQgCvS1D9FtiRc,1060 -../../include/site/python3.7/pygame/pgbufferproxy.h,sha256=hcAe-mipMYC4hNL9xIYVBwg4Aot4w1qpit84Zmkmnqw,1961 -../../include/site/python3.7/pygame/pgcompat.h,sha256=HFUiPlkczXkP3Bjpvz60iW9JiD6LKZ8L4qt2DL5KfcM,5927 -../../include/site/python3.7/pygame/pgopengl.h,sha256=hbNYtcJU3jxmvubD890dhSSE2KjzISXT76PJcguKV04,379 -../../include/site/python3.7/pygame/pygame.h,sha256=90tX-gvapUFv077c3Z22x8r17DB0bmMYfQ7aduxOKHc,1246 -../../include/site/python3.7/pygame/scrap.h,sha256=MhdSrMflRdBNnwemNm1Os5kgQ-tU9rGk5yN3qoMJza4,4594 -../../include/site/python3.7/pygame/surface.h,sha256=gbtI4NuqYrVty0iJLXgPwc_7tQjtRy1FJgHQ-bCeFuI,14554 -pygame-1.9.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pygame-1.9.6.dist-info/METADATA,sha256=QkfSJHGdvxbVNWr5ZSCH-DF_HJZhmXey3HDoc2yhXWc,444 -pygame-1.9.6.dist-info/RECORD,, -pygame-1.9.6.dist-info/WHEEL,sha256=QOmb-VuQJwuZ7Av_Q2839PCv6qsn6RGHIrt6Y0esFDg,106 -pygame-1.9.6.dist-info/top_level.txt,sha256=ABXdFGIAE2g9m2VOzQPaLa917r6XEu6d96RqIzvAWCs,7 -pygame/SDL.dll,sha256=jHdZYpfjY48eT_vddZDS9Ckh368TFFSmy17dDanKfao,454656 -pygame/SDL_image.dll,sha256=VTvjGNd8MAysZfLyUOer2x0jG_SFI2j-Ok28vSHZ-ZM,70144 -pygame/SDL_mixer.dll,sha256=FDAXD56_juxdGhBtmQd41KjWresAtWWF8e-XLm4CXF4,674304 -pygame/SDL_ttf.dll,sha256=JyY1W4xpp1nskfMZlcX3emFH1XOlkkEGXFM2tra6_5Q,38912 -pygame/__init__.py,sha256=eowEkDP_hoyHbEdkVkDlV1d1SOsSSYx7rmWYseNWiOQ,11582 -pygame/__pycache__/__init__.cpython-37.pyc,, -pygame/__pycache__/_camera_opencv_highgui.cpython-37.pyc,, -pygame/__pycache__/_camera_vidcapture.cpython-37.pyc,, -pygame/__pycache__/_dummybackend.cpython-37.pyc,, -pygame/__pycache__/_numpysndarray.cpython-37.pyc,, -pygame/__pycache__/_numpysurfarray.cpython-37.pyc,, -pygame/__pycache__/camera.cpython-37.pyc,, -pygame/__pycache__/colordict.cpython-37.pyc,, -pygame/__pycache__/compat.cpython-37.pyc,, -pygame/__pycache__/cursors.cpython-37.pyc,, -pygame/__pycache__/draw_py.cpython-37.pyc,, -pygame/__pycache__/freetype.cpython-37.pyc,, -pygame/__pycache__/ftfont.cpython-37.pyc,, -pygame/__pycache__/locals.cpython-37.pyc,, -pygame/__pycache__/macosx.cpython-37.pyc,, -pygame/__pycache__/midi.cpython-37.pyc,, -pygame/__pycache__/pkgdata.cpython-37.pyc,, -pygame/__pycache__/sndarray.cpython-37.pyc,, -pygame/__pycache__/sprite.cpython-37.pyc,, -pygame/__pycache__/surfarray.cpython-37.pyc,, -pygame/__pycache__/sysfont.cpython-37.pyc,, -pygame/__pycache__/version.cpython-37.pyc,, -pygame/_camera_opencv_highgui.py,sha256=0BxVKRNVVKV6De4Fj7bjHY6xZgFPD7DG43X8sJ7YC9o,2230 -pygame/_camera_vidcapture.py,sha256=3MSVsbGNi7jRLR9o4MwkL7Kqwzfrb4PY4Vobl_IDjIU,3739 -pygame/_dummybackend.py,sha256=cIt88kBhPzgset-VkJ1D3bG10pvlXLevpqRgrHhbeuo,770 -pygame/_freetype.cp37-win_amd64.pyd,sha256=V9Ste0NnBqi_0ITQIL8rH3lsMCNXM4jMDN8RMJZry04,82432 -pygame/_numpysndarray.py,sha256=58IIFmOiKGbYSt8yiKSGGGztcmg3aJKcPlqhCMvTYwY,2616 -pygame/_numpysurfarray.py,sha256=OYIpTY3mCz87c1IF-IXiLrTmeUmDKnrpj2q84ssW5d0,12999 -pygame/base.cp37-win_amd64.pyd,sha256=8-F6sFglkF6Eg2CCFk-UjHZ5AUrSzatGqRznw_wRY4Q,31744 -pygame/bufferproxy.cp37-win_amd64.pyd,sha256=gmEGVa1PtY1hKwmra7j-TMhQ9j1HpU1-QzcTU0Pzb2U,18944 -pygame/camera.py,sha256=Yop3_-IWJzFaJgAv-NiAlLKpLkK8guAEbpGVhdQ4IdI,2871 -pygame/cdrom.cp37-win_amd64.pyd,sha256=DSQCZUhrBNBndUTcBSk0w0PaY5E3MTRzTNENkJfT9u8,22016 -pygame/color.cp37-win_amd64.pyd,sha256=q54YHtEOaoeeFLV9297qorN4EXpxe9Z7vby1oa89hdY,32768 -pygame/colordict.py,sha256=3Xu50TCCxRdi-ue8Vp9YBGQpmKOsv1rU9bOxf3kpkR4,24175 -pygame/compat.py,sha256=ybSN_4HbNWvDJ6quCTklD1hATz8LyN0bIeRzN2yTqD8,3113 -pygame/constants.cp37-win_amd64.pyd,sha256=CUthN-OXp8anfq53drUTXSrvi9k3hdHyGhN3RW-mAI4,22528 -pygame/cursors.py,sha256=haMvZ4N2kCrHrmFXY161Q_tDt8zVaKFW_JNjji13n-0,10043 -pygame/display.cp37-win_amd64.pyd,sha256=rEBw9zeDvPXGFYgvS0r9tnnXiz4bcdrPG2OepUSXBEw,31744 -pygame/docs/__init__.py,sha256=29vwukb_6yJPWEe66QZQynt10HvgrYBmGP2vWm7Nt_w,287 -pygame/docs/__main__.py,sha256=5r8cR_y261Kf_QDTxmw9hCdFaVBGexXV5-OrQLbSNac,750 -pygame/docs/__pycache__/__init__.cpython-37.pyc,, -pygame/docs/__pycache__/__main__.cpython-37.pyc,, -pygame/docs/logos.html,sha256=Av4WDVwgX0xfhNJ-GJ2hFARHmZiDi_yv0WGk8FKzvJI,1604 -pygame/docs/pygame_logo.gif,sha256=XTZuyss-niaQhtGw78vP9MQmqIH_yLgq6cJqtb8hRtI,25116 -pygame/docs/pygame_powered.gif,sha256=JOcurZ9ApwPx420aiIOPFgoKFHkhpQKbll0w9-qhvnY,10171 -pygame/docs/pygame_small.gif,sha256=OT5k5n6OnoatNH9HfWETFx88Cr3PrUzkZyZritBviOM,10286 -pygame/docs/pygame_tiny.gif,sha256=vx7ERhvSpj51wp_qu-jISAgQr8E6vxK4_3I58-oSRm0,5485 -pygame/docs/ref/docscomments.json,sha256=MN3tMIlMeCG4G3qA3J52ZuR_vkf535KlPuRot9pJu8s,445603 -pygame/draw.cp37-win_amd64.pyd,sha256=nhuCWsJPimLR--MTwya4RJS6AK5Ir0ZYmTnplgFHLII,39424 -pygame/draw_py.py,sha256=fQooMsO259L-Z56ZTJXliaxyVBPKLEUsrEsIrrmFBAM,17943 -pygame/event.cp37-win_amd64.pyd,sha256=dnjx55y6fw5CU98yt530R4hjFltu3RpBnCD3dp9sNzs,28672 -pygame/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pygame/examples/__pycache__/__init__.cpython-37.pyc,, -pygame/examples/__pycache__/aacircle.cpython-37.pyc,, -pygame/examples/__pycache__/aliens.cpython-37.pyc,, -pygame/examples/__pycache__/arraydemo.cpython-37.pyc,, -pygame/examples/__pycache__/audiocapture.cpython-37.pyc,, -pygame/examples/__pycache__/blend_fill.cpython-37.pyc,, -pygame/examples/__pycache__/blit_blends.cpython-37.pyc,, -pygame/examples/__pycache__/camera.cpython-37.pyc,, -pygame/examples/__pycache__/chimp.cpython-37.pyc,, -pygame/examples/__pycache__/cursors.cpython-37.pyc,, -pygame/examples/__pycache__/dropevent.cpython-37.pyc,, -pygame/examples/__pycache__/eventlist.cpython-37.pyc,, -pygame/examples/__pycache__/fastevents.cpython-37.pyc,, -pygame/examples/__pycache__/fonty.cpython-37.pyc,, -pygame/examples/__pycache__/freetype_misc.cpython-37.pyc,, -pygame/examples/__pycache__/glcube.cpython-37.pyc,, -pygame/examples/__pycache__/headless_no_windows_needed.cpython-37.pyc,, -pygame/examples/__pycache__/liquid.cpython-37.pyc,, -pygame/examples/__pycache__/mask.cpython-37.pyc,, -pygame/examples/__pycache__/midi.cpython-37.pyc,, -pygame/examples/__pycache__/moveit.cpython-37.pyc,, -pygame/examples/__pycache__/oldalien.cpython-37.pyc,, -pygame/examples/__pycache__/overlay.cpython-37.pyc,, -pygame/examples/__pycache__/pixelarray.cpython-37.pyc,, -pygame/examples/__pycache__/playmus.cpython-37.pyc,, -pygame/examples/__pycache__/prevent_display_stretching.cpython-37.pyc,, -pygame/examples/__pycache__/scaletest.cpython-37.pyc,, -pygame/examples/__pycache__/scrap_clipboard.cpython-37.pyc,, -pygame/examples/__pycache__/scroll.cpython-37.pyc,, -pygame/examples/__pycache__/sound.cpython-37.pyc,, -pygame/examples/__pycache__/sound_array_demos.cpython-37.pyc,, -pygame/examples/__pycache__/stars.cpython-37.pyc,, -pygame/examples/__pycache__/testsprite.cpython-37.pyc,, -pygame/examples/__pycache__/textinput.cpython-37.pyc,, -pygame/examples/__pycache__/vgrade.cpython-37.pyc,, -pygame/examples/__pycache__/video.cpython-37.pyc,, -pygame/examples/aacircle.py,sha256=mVTq3082ajYV7-uiYGCdjKldNPSZIh1y98_sqFDJ3g8,909 -pygame/examples/aliens.py,sha256=df5swn9ApFlOoq3Glbo9V7mnUmLCixwc9bgmnl8txHo,10720 -pygame/examples/arraydemo.py,sha256=UaIWsusqmEhKg9l520pupgwhGlXsN_Vz2Hk8y_Mdif8,3721 -pygame/examples/audiocapture.py,sha256=N5lSw4TV0GNVPW4rYs3Zz-W_o-zS_l4VJDXP4IvYhRQ,1275 -pygame/examples/blend_fill.py,sha256=DQ6UsV6qwK2PwuFuDhdTnlUBLddOwsD_loGG-xGi1iY,3105 -pygame/examples/blit_blends.py,sha256=roYwEUM6atE8mxF0Z_FHJlvVZ61LZVgg2rMjifmUvf0,5908 -pygame/examples/camera.py,sha256=UyKAwSHwV9_GxMLym4FO6O8WfyNuiy5xb2u-ewtZwl4,2756 -pygame/examples/chimp.py,sha256=1KllbxPCeLMP3GhqUxPcpXBTQVmSwxmbVTqyJWxVKwE,6016 -pygame/examples/cursors.py,sha256=kDG9Fv-J-5z7jFmrpCNVojkaihgTQZSyPO5GOhD8qiA,3062 -pygame/examples/data/alien1.gif,sha256=8Wveo1zpLVaFCtYITm_SoYqjy8L-TDuaZOcNa8Osqsw,3826 -pygame/examples/data/alien1.jpg,sha256=HOjXjmW4Ofsu_en9WNrkuIp_DCwupXcFB0Yt_cqV9rA,3103 -pygame/examples/data/alien1.png,sha256=femzLssV7oGvT3S2tyviyq7qO32QfhBDtMOR3ENBCLs,3522 -pygame/examples/data/alien2.gif,sha256=0MPpVYzvjAECy0pd7YRFKCEzzIYDKEJt70rbjlLbTZM,3834 -pygame/examples/data/alien2.png,sha256=FKGYDI2FBBR1Z56BLn357PNfh3-M38gAJpSQL8BpKYY,3526 -pygame/examples/data/alien3.gif,sha256=bFCRGZOQPaadCKIc-tlqoUjHdsi5IzR0E-2SjpPEvmA,3829 -pygame/examples/data/alien3.png,sha256=a51Tb9E4IvoICGzQChHq51RKVQJLf1GOCEeqA5yYfnk,3518 -pygame/examples/data/arraydemo.bmp,sha256=xM4-n_hRCQFZlfwwdTK6eaBweycUc863TgSFbWp3dbA,76854 -pygame/examples/data/asprite.bmp,sha256=97XMpKq9lLpMuv8UveCf8UJEAxheBhPUjHfMRQBkUx4,578 -pygame/examples/data/background.gif,sha256=-3kZwt99MFUBbBo-kHvPZXVlFrSB34XVNQWWxfHb970,9133 -pygame/examples/data/blue.mpg,sha256=XDj1CRPt1MWxspCfA3oqb822nlZgQ7CyyEuVJwlgmpg,6144 -pygame/examples/data/bomb.gif,sha256=T4VCSOht8tpisgV5rIQnBCPs7vtSzAZBJF7SZ_L6JQM,1162 -pygame/examples/data/boom.wav,sha256=kfoWs0VVDGHv0JSa46nXZBGyw70-jpfPq_B31qNA_F8,12562 -pygame/examples/data/brick.png,sha256=K_mshK0aL81nzOjAorTXyPps6n9mvofLeOWFXFpVjYA,170 -pygame/examples/data/car_door.wav,sha256=TwYWVqme5NqVVID1N4es92RSKEdTYkxbNx6dNamK-_4,3910 -pygame/examples/data/chimp.bmp,sha256=PS9dLh1kfgnmba5lQiKyEQIBi8k-R1kvJ832SNudj1A,5498 -pygame/examples/data/city.png,sha256=c0Nu2o7x7QmvGMDmDCaPnhvJ8tPNuguKKpI_Z-NfQ40,143 -pygame/examples/data/danger.gif,sha256=m0CBKalFbkqlohgOmrwkwVOfqBhRWonb7xm1pzbDy2Q,2761 -pygame/examples/data/explosion1.gif,sha256=WYcdwbZqmYdaaaPYFiR5vka0Anp4F4nnNlpSSx_1xug,6513 -pygame/examples/data/fist.bmp,sha256=Nze8jhiCNl9wLgNYtVtRBqUGYqbn4-frAZaSVYXm_TE,4378 -pygame/examples/data/house_lo.mp3,sha256=R0nZUXymMp_XLPU8S1yvsiVeWT6MKLt5Rjp-WSnVrLQ,116320 -pygame/examples/data/house_lo.ogg,sha256=64FiQ1Zjq-cOj6Bmya_v3ZjEWmBaGZlTl19udKaz6sU,31334 -pygame/examples/data/house_lo.wav,sha256=B1BwfFaPIsSxaash-igVI_YE9SQd1BCXRTnSAKsNunY,78464 -pygame/examples/data/liquid.bmp,sha256=qtzPXhq0dr2ORNCCZ6gY2loT2Tsu0Dx5YvXB548I1Xg,11734 -pygame/examples/data/midikeys.png,sha256=9HCCmMHvlubR6G9a0jMv1C-AKeBzYfb5jjNhol2Mdqw,19666 -pygame/examples/data/oldplayer.gif,sha256=NWEhmaE5FUe0J-uCF8fr-XUAnoaqWa0SicoMQUBFYUg,1075 -pygame/examples/data/player1.gif,sha256=3ZTVWGxnedKqtf3R-X1omPC0Y8jUSPGgHBAzeGhnV4c,3470 -pygame/examples/data/punch.wav,sha256=A0F1xT8aIZ6aNI_5McMqLygb1EfmdIzPi4kWkU4EwQc,4176 -pygame/examples/data/sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088 -pygame/examples/data/secosmic_lo.wav,sha256=-EIFkzj7k5qEqG04n7mnUGUp1SsyCJ4n08TzPT600DY,18700 -pygame/examples/data/shot.gif,sha256=bF2eY629zQzvDu83AKpveSFhJq5G4QpOE98A0tvbPFI,129 -pygame/examples/data/static.png,sha256=Xe4wN80awt7nTNiLemoSNTEKlAbGFW7djNETP8IleNs,1202 -pygame/examples/data/whiff.wav,sha256=FMWM3XnYtce6mHFXQCYPgzT-xu-Q4DJybZfpPjG8cpE,5850 -pygame/examples/data/yuv_1.pgm,sha256=WGXoVZ0O-c6DTX9ALLoy-y4LFeOEul-W1PqFjBXGL20,649743 -pygame/examples/dropevent.py,sha256=EnvfZWDNuFYF5lHihTD5Pe3BlwnvcAmJFFuVVj4qSDE,2112 -pygame/examples/eventlist.py,sha256=6Xjeb9KTdhfeGbxy3slam4iCbwHGlmLCKZeiw2gJmW4,3677 -pygame/examples/fastevents.py,sha256=5AI_PBXwXnZlqG-shmkzEdWBBtnh30CWcyS7SDkVKkk,2890 -pygame/examples/fonty.py,sha256=YMFsysZUCdoUVhr0WYi1jm_WTF3QzzbNB2vvuq3Re5k,2539 -pygame/examples/freetype_misc.py,sha256=LtIEvxRJFwwldAYy-otKQkwa-mm4yAMwhXhewUEN2QE,3543 -pygame/examples/glcube.py,sha256=Q7XdacBhYgOTVdRy5LVKqNqAB1vm5LVYjPp5aZ9FoA8,3404 -pygame/examples/headless_no_windows_needed.py,sha256=iT4Ifv-CRP71ebyd7oYIGjXyDniFCu3W49Df8SlJMLY,1333 -pygame/examples/liquid.py,sha256=pi2Wc_fwtb4StxiAuGqCV99a-Kp39mwPU6GCptaPSvA,2519 -pygame/examples/macosx/__pycache__/macfont.cpython-37.pyc,, -pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/JavaCompiling.plist,sha256=xhIgbq92qrjlpg57wb29TGJP0m0shOPN46mZMaumQOM,278 -pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/_MainMenu_EOArchive_English.java,sha256=u16hPcsJQdm1ylRbrg2zn4DBwEEIALLAj53CkzcykHk,5793 -pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/classes.nib,sha256=5bgACAlsCG2ezXYqAOAKtoBXAj-0yU7JrIby-Fuj4Dc,306 -pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/info.nib,sha256=QZyXxX5PBZnb_A-_-Xd2MFX1YBHdJJecqKF4T0N1BJg,566 -pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/keyedobjects.nib,sha256=8IiTBRH77Z1V-WRSL1nYA_dQ5eAGbTRqZ6H6JUYCFBg,9442 -pygame/examples/macosx/aliens_app_example/English.lproj/aliens.icns,sha256=DuKpW8ACs9WR98NVge-Rda6bHyYhqGFN2EXlH-WmgBY,7236 -pygame/examples/macosx/aliens_app_example/README.txt,sha256=i6ixQdcSofTjmtIY5OKVdkHQ8TvyHjdcMroWzFpIffE,432 -pygame/examples/macosx/aliens_app_example/__pycache__/aliens.cpython-37.pyc,, -pygame/examples/macosx/aliens_app_example/__pycache__/setup.cpython-37.pyc,, -pygame/examples/macosx/aliens_app_example/aliens.py,sha256=e6wjvgHqDicdXHUCXp3zhaR1tjNdLyyzd-6vfJRv8Pc,9634 -pygame/examples/macosx/aliens_app_example/setup.py,sha256=dvNmUM1MRKe6LZvBnoM4Zhe8vLRdce1QcSJ2Zv-cE5w,578 -pygame/examples/macosx/macfont.py,sha256=El5xehQNCjZub57OJmRUmfJQrcaEzUND12lvjMor3Bk,4486 -pygame/examples/mask.py,sha256=ERdhkGFu6iPk5L-coC42vzeaK32VlCUcY0R1uVTa6Zw,5555 -pygame/examples/midi.py,sha256=djuCoTzwhcxWmtQ0PYcq7nfKCnHES8wKviIL4hq52iw,29990 -pygame/examples/moveit.py,sha256=xc2Sug_XjmAEsdyrBGu5sr3DBM0tO8_Z_i3MYNUo_PQ,1837 -pygame/examples/oldalien.py,sha256=mGla5IFZqIQmL3YQ7kNw4f7O9b83_XmcWeoSkh8vToM,6714 -pygame/examples/overlay.py,sha256=CLqqCwwOxIFKKZDX__7iZ7hH8rQ-tPr5BF0UERcdwkI,1397 -pygame/examples/pixelarray.py,sha256=WFoqKpM73S1CylX_2Tr15zvfnUoqnruQzwxV8UCEJ38,3318 -pygame/examples/playmus.py,sha256=lsOaUiCFjn_H0OOUhFK3gqhOBM_Kfyd22V6J6Vj56Ic,4564 -pygame/examples/prevent_display_stretching.py,sha256=0SrxOL-zECoN2ZPhYqH85d78u_-V_M8FG_FlWCDajtY,2741 -pygame/examples/scaletest.py,sha256=dRx3I3cO3uBrdrxrUEqKCjYLPyWlms70ZSUeU3bH3Lk,4689 -pygame/examples/scrap_clipboard.py,sha256=t1Ofdo-FaSsNt5TmrnDD6nZpgB6PN6mDNThDYrvEVY4,2926 -pygame/examples/scroll.py,sha256=yG4OCUDI4eZHXHR5viINt5d7qXRpcCVLSTVnG_SLYX0,6792 -pygame/examples/sound.py,sha256=ncjK2E85bLTk6IhIlBDKKa1hw_NCi2zHMk3nKPwqrrw,1359 -pygame/examples/sound_array_demos.py,sha256=5HsxrBo0uWXUHGOsfUXOxsCo3vQVjmx3c6vCrTxq1bA,6598 -pygame/examples/stars.py,sha256=M6_Q3pD9DgPVNfF0EtTsVet3m09ny-HlQvw1o5F5wf0,2488 -pygame/examples/testsprite.py,sha256=ObeWnWbPnM8D3W3tnhJFfGn1nEcYYfXzgSkHgjGLMuM,6910 -pygame/examples/textinput.py,sha256=JVRlppW4pCGQ_vYNb3Y3VAeusB0fLcOPko4F-OibuBs,5346 -pygame/examples/vgrade.py,sha256=ImMjT5H7_3JsziaxPFRLoLQRIk5H_0LzW0spxLTBEeI,3320 -pygame/examples/video.py,sha256=QP1Z2h1u99YFJsYOxw_5WugiBSuw23Z8qXk7huNjBDw,2814 -pygame/fastevent.cp37-win_amd64.pyd,sha256=OX4gNaRlEiL7rLQ0nits_vx4txBP7qKfH-puQ8DuBZk,18944 -pygame/font.cp37-win_amd64.pyd,sha256=xS0fYj9O7jy2GwHmxsrWfoXtZ9ZV51hCTvhOr8ldGP0,24064 -pygame/freesansbold.ttf,sha256=v5JRJp8R5LNVgqmTdglt7uPQxJc6RZy9l7C-vAH0QK0,98600 -pygame/freetype.py,sha256=jIO8euGMhCUIzpTsRSZQ0BSEyLGiA24IYkuicmOAWTM,1814 -pygame/ftfont.py,sha256=vHzoGq-lEswO_AU9G23bzsKg6m6g-dJzXokhszW4z-I,6239 -pygame/gfxdraw.cp37-win_amd64.pyd,sha256=NFo0sg9GQ1FR_wC8zH0Zm8ge0Gh83xCS4WZcQcgCiuU,64512 -pygame/image.cp37-win_amd64.pyd,sha256=Tmi05zovBEB84Moyx0DI5C7G4aICPZFbb2g0Lc0ptSw,29696 -pygame/imageext.cp37-win_amd64.pyd,sha256=lK1pidxIQmhR4L2IA5F7L5HuoxXXE9EE7ISvL4pPwEM,20480 -pygame/joystick.cp37-win_amd64.pyd,sha256=uJZM2G5Zu_aQ8S8-WtwmpPGqFTdSImS0jcNDayRJL0M,19456 -pygame/key.cp37-win_amd64.pyd,sha256=eRaBOGJAoT0QXelWub4DrrDYPUWaxnrlkT47EyWHZMU,15360 -pygame/libfreetype-6.dll,sha256=uNLns13cdxpb79_5Th4xazUaz0iQ6tk3Qe-IEWlHaCw,720384 -pygame/libgcc_s_sjlj-1.dll,sha256=k6xzHwz3CIO7QneuQnIi2XS0Wl7wJEHeYkIYwrbAIZk,91136 -pygame/libjpeg-8.dll,sha256=7NmabPJatUw0DRCxboTDSgp2OCwXVkadg0uNAuMRxlw,318464 -pygame/libmpg123-0.dll,sha256=sFcWLNAsfHOz-_S-nLrtjqYtZler9Cow2TMH9c3zes4,317952 -pygame/libogg-0.dll,sha256=6w4cSCO_SSYGOnha9vPtRzO8xE8Gd_3W2BUH8q46a5Y,44544 -pygame/libpng16-16.dll,sha256=zsUpkj8GbcetRoL8BylRJ_tSmShj-7I8t4McWdu52H8,278528 -pygame/libstdc++-6.dll,sha256=bpXrIud3i2K3KwDB5FOdZ9H8spFHTQjC7CosyIBTC9I,1209344 -pygame/libtiff-5.dll,sha256=qLxpKYWR1qm1yzJaZ1i09zYU-QTAJDm51_-ELg8m-dc,547328 -pygame/libvorbis-0.dll,sha256=NRkC5Bn2L5TTksJWaJy3rpXfGtwIWYNYSJRZRJC0aEE,230912 -pygame/libvorbisfile-3.dll,sha256=G2CgFEljQ4F9XHQPgqzBSWrc3JXbLe32nEIow-s9LP0,47616 -pygame/libwebp-5.dll,sha256=5sx9Ux6dZT070P2Ym1I3p09eyaNZ1d9aR74NEmiSQok,520192 -pygame/locals.py,sha256=8ZvWyqOqFiaPT5y7ru0_PHeCp15u_ecMqlRqNaNJ_As,1102 -pygame/macosx.py,sha256=H9QEz8HaEG__660Nz2mndCPpQHLEjf6jNulFy_3xQXk,769 -pygame/mask.cp37-win_amd64.pyd,sha256=PKJUP5TL2a97D27pD_XfDi21XmPlOKnNLNbVUwuGKkE,48128 -pygame/math.cp37-win_amd64.pyd,sha256=dRZeswmcdJcapqIIx_L7gKN_qOdbMjkTTc_QMSNIM-s,62976 -pygame/midi.py,sha256=ezAnHYQ2szG9o3C_1N6kyzax9fnhz6CKiqlk27dormM,23836 -pygame/mixer.cp37-win_amd64.pyd,sha256=wFJJF1Jt8yRsHGpE8FzUqDWCZIZ5R60tDyEHa_AyH3Q,35840 -pygame/mixer_music.cp37-win_amd64.pyd,sha256=BYB3oD1U2WJg02bziBp3pcw5-_y4dWaUt1LlUG--tpQ,18432 -pygame/mouse.cp37-win_amd64.pyd,sha256=gq1T0Vh8aXUgL2j7454Bs8NUJIegRdkPNQAmZ8swyYw,16384 -pygame/newbuffer.cp37-win_amd64.pyd,sha256=AioFYdAJ_GIWkvWtCJIHQTiFyPb7tROd4XkeQAkYjIk,23552 -pygame/overlay.cp37-win_amd64.pyd,sha256=QBTyMcx7zrLIJyZhzVSZOWVucsABNkMoBH0idswKjEk,14848 -pygame/pixelarray.cp37-win_amd64.pyd,sha256=XypkjyAXPF26q8jej7BXK5CLx5331IFLhdzc26piTsI,53760 -pygame/pixelcopy.cp37-win_amd64.pyd,sha256=S4Z7B_cqxxJvT_RXUROKe6uSk_SYZSgrne0tAx8Ru-E,26112 -pygame/pkgdata.py,sha256=sN89ew2QZXjAyc1wVlqNSOcPcK4z0cAy9Zmw29qkFoA,2264 -pygame/portmidi.dll,sha256=yfjZBDrBVwsQ8QTy0ArseR9WJhyE7kB3O-c9Cjgi4BM,41984 -pygame/pygame.ico,sha256=PBF9cw0Ca9Rw2pNmDD3iXvcYYQeI9ZzZ9vxtRLQRoJc,145516 -pygame/pygame_icon.bmp,sha256=Twnby8nv4HMhGka49n-47CPseDvwrSLZ0l1o9U2Bb5s,630 -pygame/pygame_icon.icns,sha256=B3Q59PaET66Br-x3wUFtoymOjJBB9cxEUEA74YE1TL4,53627 -pygame/pygame_icon.svg,sha256=oxge7RESGgP2--7fUmM7HnmD3vadIT5hjDykhVLcIj0,15363 -pygame/pygame_icon.tiff,sha256=cvDqNeR5SckSMyD6a7vNUsVgV7QYXNVWjsmC0BAUX98,61604 -pygame/pypm.cp37-win_amd64.pyd,sha256=HE_HoEzlW3v5fVkgPq79R9YHP2_1M9ZZuw-XlX9yM98,80384 -pygame/rect.cp37-win_amd64.pyd,sha256=tnACq0dDiM9P5aW5_LxzUel860_0r_GeIuUFGZdd8bs,34816 -pygame/rwobject.cp37-win_amd64.pyd,sha256=IHjKXDYsSb73osG9RIIgH-UHucCWOjhfS_0kW5YDxDg,18432 -pygame/scrap.cp37-win_amd64.pyd,sha256=eUOa3dqQJFjEXLcc2IHSuXlJwFJxtMYuqvYcUY8xDVE,19968 -pygame/sndarray.py,sha256=0zkFW14vjzBkA_FprdfwoWxt4UEeu4gOLkWXtLjk7sQ,3413 -pygame/sprite.py,sha256=t8_kbgO15FPjrBBiAxlKM1Y-mlSSv81h_0O2asJQYDM,56082 -pygame/surface.cp37-win_amd64.pyd,sha256=P2p9JBGUNLu4Ec5-j2YIzXYxShOsD6Svek2KgTEO1-U,249344 -pygame/surfarray.py,sha256=8FC0ltTJxGrxaZ1RLZ3HGy8ap72ePe99-uSb-zVg50E,10257 -pygame/surflock.cp37-win_amd64.pyd,sha256=lQZo5iFrqAMHPnx9qF0IGu5WYFT3RGfXIfXQEUjENvs,13824 -pygame/sysfont.py,sha256=kfgHO6VDvDUVzj8tsL4reURbkKK2gs4R4HiIDpkF9qc,13581 -pygame/tests/__init__.py,sha256=dE6IFYHjVPdSDk9jtWne-2yjHXt1ukGOxMP37sBRfgA,1273 -pygame/tests/__main__.py,sha256=HJMNTIIl6i0nAbrghd3sQsVSqcyJleYCmfqt3Rv8ekA,3834 -pygame/tests/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/__pycache__/__main__.cpython-37.pyc,, -pygame/tests/__pycache__/base_test.cpython-37.pyc,, -pygame/tests/__pycache__/blit_test.cpython-37.pyc,, -pygame/tests/__pycache__/bufferproxy_test.cpython-37.pyc,, -pygame/tests/__pycache__/camera_test.cpython-37.pyc,, -pygame/tests/__pycache__/cdrom_tags.cpython-37.pyc,, -pygame/tests/__pycache__/cdrom_test.cpython-37.pyc,, -pygame/tests/__pycache__/color_test.cpython-37.pyc,, -pygame/tests/__pycache__/compat_test.cpython-37.pyc,, -pygame/tests/__pycache__/constants_test.cpython-37.pyc,, -pygame/tests/__pycache__/cursors_test.cpython-37.pyc,, -pygame/tests/__pycache__/display_test.cpython-37.pyc,, -pygame/tests/__pycache__/draw_test.cpython-37.pyc,, -pygame/tests/__pycache__/event_test.cpython-37.pyc,, -pygame/tests/__pycache__/fastevent_tags.cpython-37.pyc,, -pygame/tests/__pycache__/fastevent_test.cpython-37.pyc,, -pygame/tests/__pycache__/font_tags.cpython-37.pyc,, -pygame/tests/__pycache__/font_test.cpython-37.pyc,, -pygame/tests/__pycache__/freetype_tags.cpython-37.pyc,, -pygame/tests/__pycache__/freetype_test.cpython-37.pyc,, -pygame/tests/__pycache__/ftfont_tags.cpython-37.pyc,, -pygame/tests/__pycache__/ftfont_test.cpython-37.pyc,, -pygame/tests/__pycache__/gfxdraw_test.cpython-37.pyc,, -pygame/tests/__pycache__/image__save_gl_surface_test.cpython-37.pyc,, -pygame/tests/__pycache__/image_tags.cpython-37.pyc,, -pygame/tests/__pycache__/image_test.cpython-37.pyc,, -pygame/tests/__pycache__/imageext_tags.cpython-37.pyc,, -pygame/tests/__pycache__/imageext_test.cpython-37.pyc,, -pygame/tests/__pycache__/joystick_test.cpython-37.pyc,, -pygame/tests/__pycache__/key_test.cpython-37.pyc,, -pygame/tests/__pycache__/mask_test.cpython-37.pyc,, -pygame/tests/__pycache__/math_test.cpython-37.pyc,, -pygame/tests/__pycache__/midi_tags.cpython-37.pyc,, -pygame/tests/__pycache__/midi_test.cpython-37.pyc,, -pygame/tests/__pycache__/mixer_music_tags.cpython-37.pyc,, -pygame/tests/__pycache__/mixer_music_test.cpython-37.pyc,, -pygame/tests/__pycache__/mixer_tags.cpython-37.pyc,, -pygame/tests/__pycache__/mixer_test.cpython-37.pyc,, -pygame/tests/__pycache__/mouse_test.cpython-37.pyc,, -pygame/tests/__pycache__/overlay_tags.cpython-37.pyc,, -pygame/tests/__pycache__/overlay_test.cpython-37.pyc,, -pygame/tests/__pycache__/pixelarray_test.cpython-37.pyc,, -pygame/tests/__pycache__/pixelcopy_test.cpython-37.pyc,, -pygame/tests/__pycache__/rect_test.cpython-37.pyc,, -pygame/tests/__pycache__/rwobject_test.cpython-37.pyc,, -pygame/tests/__pycache__/scrap_tags.cpython-37.pyc,, -pygame/tests/__pycache__/scrap_test.cpython-37.pyc,, -pygame/tests/__pycache__/sndarray_tags.cpython-37.pyc,, -pygame/tests/__pycache__/sndarray_test.cpython-37.pyc,, -pygame/tests/__pycache__/sprite_test.cpython-37.pyc,, -pygame/tests/__pycache__/surface_test.cpython-37.pyc,, -pygame/tests/__pycache__/surfarray_tags.cpython-37.pyc,, -pygame/tests/__pycache__/surfarray_test.cpython-37.pyc,, -pygame/tests/__pycache__/surflock_test.cpython-37.pyc,, -pygame/tests/__pycache__/sysfont_test.cpython-37.pyc,, -pygame/tests/__pycache__/test_test_.cpython-37.pyc,, -pygame/tests/__pycache__/threads_test.cpython-37.pyc,, -pygame/tests/__pycache__/time_test.cpython-37.pyc,, -pygame/tests/__pycache__/touch_tags.cpython-37.pyc,, -pygame/tests/__pycache__/touch_test.cpython-37.pyc,, -pygame/tests/__pycache__/transform_test.cpython-37.pyc,, -pygame/tests/__pycache__/version_test.cpython-37.pyc,, -pygame/tests/base_test.py,sha256=VxGkbo89qJfmc53FdXjouXqAoWBhhCC4owFnYebN3LA,24055 -pygame/tests/blit_test.py,sha256=cl0dU599wvF2wwMRoM81rITzp11BpxQhxiP0xVUbG_k,4683 -pygame/tests/bufferproxy_test.py,sha256=VZukxi7C90IQq8mfY32uXPJdLZFENABl7UsiSWZrxlU,16808 -pygame/tests/camera_test.py,sha256=rcfoTOQKD57NaYS7uQ8_IUM45tjH0v-AS9VNrgJ-h6I,129 -pygame/tests/cdrom_tags.py,sha256=Ka5HOPip7wostxjh5nPQCPMhQWZzQtqCiOssl01fXEg,42 -pygame/tests/cdrom_test.py,sha256=aL8xrIPUj-FJzIBVk_VT05A1aaFCoWiJxMLtqn8PSsQ,9976 -pygame/tests/color_test.py,sha256=RMoVPGY4ctfObWfJiMF6IYa4dSFQuJRPSid1hSqVKuE,36611 -pygame/tests/compat_test.py,sha256=cuEgdQEKUgUbRJwn5TWqPfygOC_bbyO6Pv18o8XwDHI,2773 -pygame/tests/constants_test.py,sha256=8aeCXB2yg8QitImPVLjOAm8sPRHF5FaRlW74QVdAis4,1445 -pygame/tests/cursors_test.py,sha256=naD_joc1jPy1ATkDyZgsajb_Ez5oiLbV3C4OjgmGgrg,2491 -pygame/tests/display_test.py,sha256=AzRyb33RuWkpZPjsVHBf9f2tCdS2yo9vh0VE5HKeyDI,16299 -pygame/tests/draw_test.py,sha256=pIYeUxGY5NDLS8mrDX6AIoDjS-jub6JWdxR9SKs4EWA,51526 -pygame/tests/event_test.py,sha256=WJfV5VA-ULlnIKblNVQEYQYjaBNplVA9T4V6bSaoLLw,10698 -pygame/tests/fastevent_tags.py,sha256=m9CAhjZFciPRvWFz1fJu6kGZMNt2FrDloEkA_Wz1POg,14 -pygame/tests/fastevent_test.py,sha256=DjPLD3ygDT_bQBsIdPfz5s0xCa1snsPoiTu1_dLuhkY,5410 -pygame/tests/fixtures/fonts/A_PyGameMono-8.png,sha256=QmhReADwKrzW5RWnG1KHEtZIqpVtwWzhXmydX1su10c,92 -pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf,sha256=nm3okxnfAFtADlp7s2AY43zS49NYg9jq7GVzG2lPhOQ,1947 -pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf,sha256=4kB0uYeEpa3W-ZAomFMpc0hD-h6FnOh2m5IPi6xzfds,1648 -pygame/tests/fixtures/fonts/PyGameMono-8.bdf,sha256=aK0KV-_osDPTPiA1BUCgZHOmufy6J9Vh5pf1IAi0_yg,1365 -pygame/tests/fixtures/fonts/PyGameMono.otf,sha256=_Af4LyMEgKKGa8jDlfik89axhLc3HoS8aG5JHWN5sZw,3128 -pygame/tests/fixtures/fonts/test_fixed.otf,sha256=FWHmFsQUobgtbm370Y5XJv1lAokTreGR5fo4tuw3Who,58464 -pygame/tests/fixtures/fonts/test_sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088 -pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png,sha256=x_D28PW8aKed8ZHBK6AISEZ9vlEV76Whi770ItTuFVU,89 -pygame/tests/fixtures/xbm_cursors/white_sizing.xbm,sha256=VLAS1A417T-Vg6GMsmicUCYpOhvGsrgJJYUvdFYYteY,366 -pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm,sha256=CKQeiOtlFoJdAts83UmTEeVk-3pxgJ9Wu2QJaCjzAQM,391 -pygame/tests/font_tags.py,sha256=m9CAhjZFciPRvWFz1fJu6kGZMNt2FrDloEkA_Wz1POg,14 -pygame/tests/font_test.py,sha256=-57BrYOw5XrqvkbnPZpEhUK0mCnZWsAqg3gEYXX24e4,19716 -pygame/tests/freetype_tags.py,sha256=k8pF6wXOxo6geb3zWRLafKemsFBJsaTbgLbGBdZHl5w,183 -pygame/tests/freetype_test.py,sha256=_2SFZTVVZ0LFtRLVPd-Q9BzjDa0D9RtpCxfNM3mMrrs,60454 -pygame/tests/ftfont_tags.py,sha256=QoCzxaa4QXJEtHTVWXauabL2GhNx0ZDi_cbkN2XeUHY,181 -pygame/tests/ftfont_test.py,sha256=c2adgJJmf3ppWLH6ASRBHymm7RMamtQifSeQyfZfTyU,534 -pygame/tests/gfxdraw_test.py,sha256=3CePp-GpTqkfiALwSiiCeI3LN_INJcDYSYf4-999N9Y,34365 -pygame/tests/image__save_gl_surface_test.py,sha256=Kcn8RGVkMSu7WUqMdpIgEQkZIBv2QLm0Gmx069AT6ZQ,1201 -pygame/tests/image_tags.py,sha256=Neq0in62AlLpXmGuEgu3X8-DLQYMaZfoHFyIcyxHe6I,132 -pygame/tests/image_test.py,sha256=wdZZ4i3tFMgq0d-blos7OSz0Ydh6EmX3HYGGzRhZD3Y,20747 -pygame/tests/imageext_tags.py,sha256=CR9cI4WIVu0b_GuPqczNqFk-t-hcWATTaOWSnAOIAEs,140 -pygame/tests/imageext_test.py,sha256=h8GgWEdcLuxAXPbRXgqY0AGTVS5aJfcBud_JBc_JcTs,2956 -pygame/tests/joystick_test.py,sha256=fvRuB3uaiewWcmbrg4W2U3tlKlxArWmMjTtCo0OoNKk,3254 -pygame/tests/key_test.py,sha256=uv8nnTloAdKfJ5uPeaL0oVUHbG2H48Pi9QN0zqMm8GY,2099 -pygame/tests/mask_test.py,sha256=3YW-64LEmbPYLt1j8sbXHJzcUnwdAVW0wLRPBajRT2A,75723 -pygame/tests/math_test.py,sha256=-re2ESSFIrR4yC0TX5BejVE6D5sJ24D3nnjC03fsxag,75473 -pygame/tests/midi_tags.py,sha256=kh-RgQubZpoDpC0fLslJZOOgKCuyANiJKWFPcuUXakA,27 -pygame/tests/midi_test.py,sha256=MC178DaLkqxHdtMSj_2hYN-JiYGX0nrw727gWalDFdI,13053 -pygame/tests/mixer_music_tags.py,sha256=tv3QqQFSCb4m5Ej67KVNbM0CneqEwp1cqdinoPD-wC0,138 -pygame/tests/mixer_music_test.py,sha256=B8F7V3Bf4FLQJOru1CrKd-eXTEq2JQXTcbZxsTO3OOQ,8371 -pygame/tests/mixer_tags.py,sha256=OxorCxCHpu5TIVo70M_VfQXlPU9ksfbpFE5giS-zb5I,132 -pygame/tests/mixer_test.py,sha256=pHAI2Msqv_I_nWbxgIe5A0Qy23CpJ-XpCXWCrUSA-U4,38752 -pygame/tests/mouse_test.py,sha256=ZhHxTcAyIuzDTk71EAo2g5F-u_Og9WV8dH3fsPltMtc,5607 -pygame/tests/overlay_tags.py,sha256=o4vxnSAG_uhNaj7lKUmbzpdwqldhCX9mbjOjtdsJLbc,66 -pygame/tests/overlay_test.py,sha256=jVkFbxEl94DfrMr0CX5eqLL6-cpJR0FLt4RWI1ItJ0A,943 -pygame/tests/pixelarray_test.py,sha256=HP1jLfIhnACqILGny28c5PtNW9Non4_qjf_L2IYa3bQ,56189 -pygame/tests/pixelcopy_test.py,sha256=y7GdW9bM06T22-sM4C87jA86pUNs15L2xrZ3HKpfIME,26072 -pygame/tests/rect_test.py,sha256=4EgyD0WEl6FgdFN9hcFhN55hvrRUl5pC4LKdcbebzAc,27447 -pygame/tests/run_tests__tests/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/all_ok/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/all_ok/fake_3_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/all_ok/fake_4_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/all_ok/fake_5_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/all_ok/fake_6_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py,sha256=yMOBw45z13s73pTx82OzMmYG7ZiSiQ_IEgMEH21_4io,877 -pygame/tests/run_tests__tests/all_ok/zero_tests_test.py,sha256=s30QH2Ae0zlo-IouC0UuFNYa5rrTwPI6UZ5PVW8hxUc,625 -pygame/tests/run_tests__tests/everything/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/everything/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/everything/incomplete_todo_test.py,sha256=zZeKBjmqEtpFcHcuX0jVZudWaTxvM2oX0JToAbKLleY,990 -pygame/tests/run_tests__tests/everything/magic_tag_test.py,sha256=7fQgh8UMxCx51jLGN-5zhxHcl6ETegpYd4y4or5gqZk,940 -pygame/tests/run_tests__tests/everything/sleep_test.py,sha256=4t-3qQW7G4Wn0T-e2yMqCjBdTg_I7-zWFKXUkW6-9Gc,796 -pygame/tests/run_tests__tests/exclude/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/exclude/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/exclude/invisible_tag_test.py,sha256=wYEC59vGVFIsidO0f3mgHir_nUQZRKbehaHq0A-aTy8,1006 -pygame/tests/run_tests__tests/exclude/magic_tag_test.py,sha256=7fQgh8UMxCx51jLGN-5zhxHcl6ETegpYd4y4or5gqZk,940 -pygame/tests/run_tests__tests/failures1/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/failures1/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/failures1/fake_3_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/failures1/fake_4_test.py,sha256=-fMHT7BiAm2z_b38xpCZw2y-jjNNVqxMOwEmOjeSXEw,1030 -pygame/tests/run_tests__tests/incomplete/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/incomplete/fake_2_test.py,sha256=0N2viyjn6Mq10bgcHP_x9rkUfyobvYjGRbT_FfJSUyc,970 -pygame/tests/run_tests__tests/incomplete/fake_3_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/incomplete_todo/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py,sha256=zZeKBjmqEtpFcHcuX0jVZudWaTxvM2oX0JToAbKLleY,990 -pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/infinite_loop/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py,sha256=A6bbE5P9J3jdoLCqGiGKZrI5cfjZy7nikBR_7baHqNw,987 -pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/print_stderr/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stderr/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/print_stderr/fake_3_test.py,sha256=kmHeArRCqQ6CsZjSNxb26ftElmGNSZ6_xWQz2qjmZjo,1034 -pygame/tests/run_tests__tests/print_stderr/fake_4_test.py,sha256=-fMHT7BiAm2z_b38xpCZw2y-jjNNVqxMOwEmOjeSXEw,1030 -pygame/tests/run_tests__tests/print_stdout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/print_stdout/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/print_stdout/fake_3_test.py,sha256=S4aPSIEYpWuOiSKB75WceZgLJEs3LqmnWqMfbuh8pbA,1092 -pygame/tests/run_tests__tests/print_stdout/fake_4_test.py,sha256=-fMHT7BiAm2z_b38xpCZw2y-jjNNVqxMOwEmOjeSXEw,1030 -pygame/tests/run_tests__tests/run_tests__test.py,sha256=-8bey9yXLmafecehooi3xCPScI6DtVPa5p13EkRkGoY,4285 -pygame/tests/run_tests__tests/timeout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8 -pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-37.pyc,, -pygame/tests/run_tests__tests/timeout/fake_2_test.py,sha256=WQbZ12A0FMgoi0Jv-MGSf7K93GKCEA_NtY-s32fFPCA,980 -pygame/tests/run_tests__tests/timeout/sleep_test.py,sha256=f8oaqC2-JwlZADxCqScqv3PA2vfe2CDOTkv4efcdo-0,797 -pygame/tests/rwobject_test.py,sha256=Tg4T0vJDntlCrSmlv2d0yCj09QmbxZtJzZD5SzVkXpg,3865 -pygame/tests/scrap_tags.py,sha256=Z59xMvR6VZh_jxMGXJAQhLcOCrKc8wqLzFYIuFCC1iQ,417 -pygame/tests/scrap_test.py,sha256=RMQ-uB5kxEUMr4twvbP0r7ptJsH0jYFbd1SFqd8OVyw,9036 -pygame/tests/sndarray_tags.py,sha256=OKf8uXDz43bSC5YmOXme0CJ2R3mF33CWfVKKwV5v624,190 -pygame/tests/sndarray_test.py,sha256=VmNjrFkhAfVe0w3VxjLwLu7nrt9JeeIyUWA6rXZh0AM,6841 -pygame/tests/sprite_test.py,sha256=BEhFJlUSOEQqNg-7N7ffoPiebZ53YHo1fJiyXJF1ujg,43313 -pygame/tests/surface_test.py,sha256=oBUzoml2k7LJdITM4FNGH1rWBZTyut4w-NUOE54FUnc,100140 -pygame/tests/surfarray_tags.py,sha256=qBxxuyRGVht11RU3jpN78PPwxBkFmoVyinj0UTIX5FQ,260 -pygame/tests/surfarray_test.py,sha256=wwdi6lC5NQFPuZqnQXOdBM_xWLN-Gs4RsfdIkmr3hHY,25452 -pygame/tests/surflock_test.py,sha256=czBVDuVXbinJ7X055cgX6Q3uSUCtnZTd6vj6OBkQ9PY,4725 -pygame/tests/sysfont_test.py,sha256=cL7hDRuJtXfMKMvQHq8OyADrjI5Do3c5aCaOjCChg1w,798 -pygame/tests/test_test_.py,sha256=YTMzScCIzPnxzIIxhLvMuxKG2EfL9klWbC87pO4Eri8,25 -pygame/tests/test_utils/__init__.py,sha256=IGEBsBPznul3Ki-b6-3JLTm9zMos4r459fduBzoKSS4,5836 -pygame/tests/test_utils/__pycache__/__init__.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/arrinter.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/async_sub.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/buftools.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/endian.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/png.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/run_tests.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/test_machinery.cpython-37.pyc,, -pygame/tests/test_utils/__pycache__/test_runner.cpython-37.pyc,, -pygame/tests/test_utils/arrinter.py,sha256=XWN_5AHyOFWHrTeGfCvQn56jUgN-m13hjAOvjQ0kXLA,15234 -pygame/tests/test_utils/async_sub.py,sha256=5P6H3egMmICa5u83ek4iFMK84wDFBc4pkgGQQbx-2UY,9109 -pygame/tests/test_utils/buftools.py,sha256=nrygK9ouEu70oCi0d0bSBWHlvPH8ERnIyj5r0NeILbQ,23886 -pygame/tests/test_utils/endian.py,sha256=hxXVLc_t5P8_Q5ReHX6wuV-YPD1-jdsVszLDLNaMf7I,493 -pygame/tests/test_utils/png.py,sha256=3Q6zk1_oCs4cSJymYlDaUh3xE-dzoD6tguHOMNSkbYc,151218 -pygame/tests/test_utils/run_tests.py,sha256=tIM5nM5m3EFGPLAm9tLzDC5YMR86qbn3-Zj4KvPCxLk,12292 -pygame/tests/test_utils/test_machinery.py,sha256=sQA7HSiLYnmFfWC6a8OKkYw4xe2f9zYLHYHvEvJwJ6w,2404 -pygame/tests/test_utils/test_runner.py,sha256=nFwRt5Mv5KZcEfWhE2uFFoZYksMoop5g7VI9Un9hkxw,7729 -pygame/tests/threads_test.py,sha256=zov2oHktLZIrlAsvE_fP18TePWN00jXIYVLC5ltfq_s,5055 -pygame/tests/time_test.py,sha256=Zf-AcvJVPNMj4KNU1w1o8u21bJouuAPBZxP2cvkMkGo,6908 -pygame/tests/touch_tags.py,sha256=LsHl4hEKvNKjvGCZWC2lcQ9VZsvMmFbiynd9prQKM54,28 -pygame/tests/touch_test.py,sha256=b7TvlNGOGhODFk72A5IJf2v118XsJ051JH7hTchcFn0,1198 -pygame/tests/transform_test.py,sha256=PyYFWxUwZ1Hqpdr7dIqDq6uyCMOwHk4yOlvWAbTUTuc,39625 -pygame/tests/version_test.py,sha256=LlO7iW9dAK0dBdu3_7X5b-WzkSFArF-1wyti4OX0gZI,1339 -pygame/threads/Py25Queue.py,sha256=qdHdnydu26pfhdq7VRGD8j8cFN2DdYuxImUGdzLWhG0,7759 -pygame/threads/__init__.py,sha256=n1eKpbYy0KyDmVxUXKLgmPQL-UIJl5IN8f8VHP56CFc,8709 -pygame/threads/__pycache__/Py25Queue.cpython-37.pyc,, -pygame/threads/__pycache__/__init__.cpython-37.pyc,, -pygame/time.cp37-win_amd64.pyd,sha256=p0JJclaoDBlhZwJoWBb0vRZfZb2dXKCtqBLYQVKBWJ8,17408 -pygame/transform.cp37-win_amd64.pyd,sha256=8GL5rgB1T3KZyWNYLU6d1ACC2O53h6rPgMyITS4hni0,52224 -pygame/version.py,sha256=tqiSc2GHKlALMPmW9w1EhflZzosvMYzrsgRvGR5OGmc,1965 -pygame/zlib1.dll,sha256=GA8HVwEYYxggbi7JYEioRlNbAlDQhhqO0VF8HQb1m60,117248 diff --git a/venv/Lib/site-packages/pygame-1.9.6.dist-info/WHEEL b/venv/Lib/site-packages/pygame-1.9.6.dist-info/WHEEL deleted file mode 100644 index 2f3c8613ef8f07ab836ac1663903109c7465c87d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame-1.9.6.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.33.1) -Root-Is-Purelib: false -Tag: cp37-cp37m-win_amd64 - diff --git a/venv/Lib/site-packages/pygame-1.9.6.dist-info/top_level.txt b/venv/Lib/site-packages/pygame-1.9.6.dist-info/top_level.txt deleted file mode 100644 index 0cb7ff1d4ce22c0a55bb5cc0b0984298666c73e3..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame-1.9.6.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pygame diff --git a/venv/Lib/site-packages/pygame/SDL.dll b/venv/Lib/site-packages/pygame/SDL.dll deleted file mode 100644 index c82f905729d469784aafbad668448fc0869fa91b..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/SDL.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/SDL_image.dll b/venv/Lib/site-packages/pygame/SDL_image.dll deleted file mode 100644 index 0027cc714d28552c0578fd36728393d1dd8cdc42..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/SDL_image.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/SDL_mixer.dll b/venv/Lib/site-packages/pygame/SDL_mixer.dll deleted file mode 100644 index 67979bca66a52a0c2af24aa1764a4e0d18cf0e72..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/SDL_mixer.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/SDL_ttf.dll b/venv/Lib/site-packages/pygame/SDL_ttf.dll deleted file mode 100644 index a5bdb4ac8fadc69c4a15286c3680ac29e30ec667..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/SDL_ttf.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/__init__.py b/venv/Lib/site-packages/pygame/__init__.py index 92375614e2156e15c50894e615b8e3dd3d25b457..da8bff7c61350af68c14e3397a015812df229430 100644 --- a/venv/Lib/site-packages/pygame/__init__.py +++ b/venv/Lib/site-packages/pygame/__init__.py @@ -1,4 +1,3 @@ -# coding: ascii # pygame - Python Game Library # Copyright (C) 2000-2001 Pete Shinners # @@ -28,56 +27,27 @@ import sys import os # Choose Windows display driver -if os.name == 'nt': - - #pypy does not find the dlls, so we add package folder to PATH. +if os.name == "nt": pygame_dir = os.path.split(__file__)[0] - os.environ['PATH'] = os.environ['PATH'] + ';' + pygame_dir - # Respect existing SDL_VIDEODRIVER setting if it has been set - if 'SDL_VIDEODRIVER' not in os.environ: - - # If the Windows version is 95/98/ME and DirectX 5 or greater is - # installed, then use the directx driver rather than the default - # windib driver. - - # http://docs.python.org/lib/module-sys.html - # 0 (VER_PLATFORM_WIN32s) Win32s on Windows 3.1 - # 1 (VER_PLATFORM_WIN32_WINDOWS) Windows 95/98/ME - # 2 (VER_PLATFORM_WIN32_NT) Windows NT/2000/XP - # 3 (VER_PLATFORM_WIN32_CE) Windows CE - if sys.getwindowsversion()[0] == 1: - - import _winreg - try: + # pypy does not find the dlls, so we add package folder to PATH. + os.environ["PATH"] = os.environ["PATH"] + ";" + pygame_dir - # Get DirectX version from registry - key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, - 'SOFTWARE\\Microsoft\\DirectX') - dx_version_string = _winreg.QueryValueEx(key, 'Version') - key.Close() + # windows store python does not find the dlls, so we run this + if sys.version_info > (3, 8): + os.add_dll_directory(pygame_dir) # only available in 3.8+ - # Set video driver to directx if DirectX 5 or better is - # installed. - # To interpret DirectX version numbers, see this page: - # http://en.wikipedia.org/wiki/DirectX#Releases - minor_dx_version = int(dx_version_string.split('.')[1]) - if minor_dx_version >= 5: - os.environ['SDL_VIDEODRIVER'] = 'directx' - - # Clean up namespace - del key, dx_version_string, minor_dx_version - - except: - pass - - # Clean up namespace - del _winreg + # cleanup namespace + del pygame_dir # when running under X11, always set the SDL window WM_CLASS to make the # window managers correctly match the pygame window. -elif 'DISPLAY' in os.environ and 'SDL_VIDEO_X11_WMCLASS' not in os.environ: - os.environ['SDL_VIDEO_X11_WMCLASS'] = os.path.basename(sys.argv[0]) +elif "DISPLAY" in os.environ and "SDL_VIDEO_X11_WMCLASS" not in os.environ: + os.environ["SDL_VIDEO_X11_WMCLASS"] = os.path.basename(sys.argv[0]) + + +def _attribute_undefined(name): + raise RuntimeError(f"{name} is not available") class MissingModule: @@ -87,7 +57,7 @@ class MissingModule: self.name = name exc_type, exc_msg = sys.exc_info()[:2] self.info = str(exc_msg) - self.reason = "%s: %s" % (exc_type.__name__, self.info) + self.reason = f"{exc_type.__name__}: {self.info}" self.urgent = urgent if urgent: self.warn() @@ -96,39 +66,43 @@ class MissingModule: if not self.urgent: self.warn() self.urgent = 1 - missing_msg = "%s module not available (%s)" % (self.name, self.reason) + missing_msg = f"{self.name} module not available ({self.reason})" raise NotImplementedError(missing_msg) - def __nonzero__(self): - return 0 + def __bool__(self): + return False def warn(self): - msg_type = 'import' if self.urgent else 'use' - message = '%s %s: %s\n(%s)' % (msg_type, self.name, self.info, self.reason) + msg_type = "import" if self.urgent else "use" + message = f"{msg_type} {self.name}: {self.info}\n({self.reason})" try: import warnings + level = 4 if self.urgent else 3 warnings.warn(message, RuntimeWarning, level) except ImportError: - print (message) + print(message) # we need to import like this, each at a time. the cleanest way to import # our modules is with the import command (not the __import__ function) +# isort: skip_file # first, the "required" modules -from pygame.base import * -from pygame.constants import * -from pygame.version import * +from pygame.base import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.constants import * # now has __all__ pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.version import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] from pygame.rect import Rect -from pygame.compat import PY_MAJOR_VERSION from pygame.rwobject import encode_string, encode_file_path import pygame.surflock import pygame.color -Color = color.Color + +Color = pygame.color.Color import pygame.bufferproxy -BufferProxy = bufferproxy.BufferProxy + +BufferProxy = pygame.bufferproxy.BufferProxy import pygame.math + Vector2 = pygame.math.Vector2 Vector3 = pygame.math.Vector3 @@ -140,208 +114,165 @@ if get_sdl_version() < (2, 0, 0): # cdrom only available for SDL 1.2.X try: import pygame.cdrom - except (ImportError, IOError): + except (ImportError, OSError): cdrom = MissingModule("cdrom", urgent=1) -try: - import pygame.cursors -except (ImportError, IOError): - cursors = MissingModule("cursors", urgent=1) - try: import pygame.display -except (ImportError, IOError): +except (ImportError, OSError): display = MissingModule("display", urgent=1) try: import pygame.draw -except (ImportError, IOError): +except (ImportError, OSError): draw = MissingModule("draw", urgent=1) try: import pygame.event -except (ImportError, IOError): +except (ImportError, OSError): event = MissingModule("event", urgent=1) try: import pygame.image -except (ImportError, IOError): +except (ImportError, OSError): image = MissingModule("image", urgent=1) try: import pygame.joystick -except (ImportError, IOError): +except (ImportError, OSError): joystick = MissingModule("joystick", urgent=1) try: import pygame.key -except (ImportError, IOError): +except (ImportError, OSError): key = MissingModule("key", urgent=1) try: import pygame.mouse -except (ImportError, IOError): +except (ImportError, OSError): mouse = MissingModule("mouse", urgent=1) +try: + import pygame.cursors + from pygame.cursors import Cursor +except (ImportError, OSError): + cursors = MissingModule("cursors", urgent=1) + + def Cursor(*args): # pylint: disable=unused-argument + _attribute_undefined("pygame.Cursor") + + try: import pygame.sprite -except (ImportError, IOError): +except (ImportError, OSError): sprite = MissingModule("sprite", urgent=1) try: import pygame.threads -except (ImportError, IOError): +except (ImportError, OSError): threads = MissingModule("threads", urgent=1) try: import pygame.pixelcopy -except (ImportError, IOError): +except (ImportError, OSError): pixelcopy = MissingModule("pixelcopy", urgent=1) -def warn_unwanted_files(): - """warn about unneeded old files""" - - # a temporary hack to warn about camera.so and camera.pyd. - install_path = os.path.split(pygame.base.__file__)[0] - extension_ext = os.path.splitext(pygame.base.__file__)[1] - - # here are the .so/.pyd files we need to ask to remove. - ext_to_remove = ["camera"] - - # here are the .py/.pyo/.pyc files we need to ask to remove. - py_to_remove = ["color"] - - # Don't warn on Symbian. The color.py is used as a wrapper. - if os.name == "e32": - py_to_remove = [] - - # See if any of the files are there. - extension_files = ["%s%s" % (x, extension_ext) for x in ext_to_remove] - - py_files = ["%s%s" % (x, py_ext) - for py_ext in [".py", ".pyc", ".pyo"] - for x in py_to_remove] - - files = py_files + extension_files - - unwanted_files = [] - for f in files: - unwanted_files.append(os.path.join(install_path, f)) - - ask_remove = [] - for f in unwanted_files: - if os.path.exists(f): - ask_remove.append(f) - - if ask_remove: - message = "Detected old file(s). Please remove the old files:\n" +try: + from pygame.surface import Surface, SurfaceType +except (ImportError, OSError): - for f in ask_remove: - message += "%s " % f - message += "\nLeaving them there might break pygame. Cheers!\n\n" + def Surface(size, flags, depth, masks): # pylint: disable=unused-argument + _attribute_undefined("pygame.Surface") - try: - import warnings - level = 4 - warnings.warn(message, RuntimeWarning, level) - except ImportError: - print (message) + SurfaceType = Surface +try: + import pygame.mask + from pygame.mask import Mask +except (ImportError, OSError): + mask = MissingModule("mask", urgent=0) -# disable, because we hopefully don't need it. -# warn_unwanted_files() + def Mask(size, fill): # pylint: disable=unused-argument + _attribute_undefined("pygame.Mask") try: - from pygame.surface import * -except (ImportError, IOError): - Surface = lambda: Missing_Function + from pygame.pixelarray import PixelArray +except (ImportError, OSError): + def PixelArray(surface): # pylint: disable=unused-argument + _attribute_undefined("pygame.PixelArray") -try: - import pygame.mask - from pygame.mask import Mask -except (ImportError, IOError): - Mask = lambda: Missing_Function try: - from pygame.pixelarray import * -except (ImportError, IOError): - PixelArray = lambda: Missing_Function + from pygame.overlay import Overlay +except (ImportError, OSError): + + def Overlay(format, size): # pylint: disable=unused-argument + _attribute_undefined("pygame.Overlay") -try: - from pygame.overlay import * -except (ImportError, IOError): - Overlay = lambda: Missing_Function try: import pygame.time -except (ImportError, IOError): +except (ImportError, OSError): time = MissingModule("time", urgent=1) try: import pygame.transform -except (ImportError, IOError): +except (ImportError, OSError): transform = MissingModule("transform", urgent=1) # lastly, the "optional" pygame modules -if 'PYGAME_FREETYPE' in os.environ: +if "PYGAME_FREETYPE" in os.environ: try: import pygame.ftfont as font - sys.modules['pygame.font'] = font - except (ImportError, IOError): + + sys.modules["pygame.font"] = font + except (ImportError, OSError): pass try: import pygame.font import pygame.sysfont + pygame.font.SysFont = pygame.sysfont.SysFont pygame.font.get_fonts = pygame.sysfont.get_fonts pygame.font.match_font = pygame.sysfont.match_font -except (ImportError, IOError): +except (ImportError, OSError): font = MissingModule("font", urgent=0) # try and load pygame.mixer_music before mixer, for py2app... try: import pygame.mixer_music - #del pygame.mixer_music - #print ("NOTE2: failed importing pygame.mixer_music in lib/__init__.py") -except (ImportError, IOError): + + # del pygame.mixer_music + # print("NOTE2: failed importing pygame.mixer_music in lib/__init__.py") +except (ImportError, OSError): pass try: import pygame.mixer -except (ImportError, IOError): +except (ImportError, OSError): mixer = MissingModule("mixer", urgent=0) -try: - import pygame.movie -except (ImportError, IOError): - movie = MissingModule("movie", urgent=0) - -# try: -# import pygame.movieext -# except (ImportError,IOError): -# movieext=MissingModule("movieext", urgent=0) - try: import pygame.scrap -except (ImportError, IOError): +except (ImportError, OSError): scrap = MissingModule("scrap", urgent=0) try: import pygame.surfarray -except (ImportError, IOError): +except (ImportError, OSError): surfarray = MissingModule("surfarray", urgent=0) try: import pygame.sndarray -except (ImportError, IOError): +except (ImportError, OSError): sndarray = MissingModule("sndarray", urgent=0) try: import pygame.fastevent -except (ImportError, IOError): +except (ImportError, OSError): fastevent = MissingModule("fastevent", urgent=0) # there's also a couple "internal" modules not needed @@ -349,8 +280,17 @@ except (ImportError, IOError): # programs get everything they need (like py2exe) try: import pygame.imageext + del pygame.imageext -except (ImportError, IOError): +except (ImportError, OSError): + pass + +# this internal module needs to be included for dependency +# finders, but can't be deleted, as some tests need it +try: + import pygame.pkgdata + +except (ImportError, OSError): pass @@ -360,15 +300,12 @@ def packager_imports(): import numpy import OpenGL.GL import pygame.macosx - import pygame.bufferproxy import pygame.colordict - import pygame._view + # make Rects pickleable -if PY_MAJOR_VERSION >= 3: - import copyreg as copy_reg -else: - import copy_reg + +import copyreg def __rect_constructor(x, y, w, h): @@ -376,9 +313,11 @@ def __rect_constructor(x, y, w, h): def __rect_reduce(r): - assert type(r) == Rect + assert isinstance(r, Rect) return __rect_constructor, (r.x, r.y, r.w, r.h) -copy_reg.pickle(Rect, __rect_reduce, __rect_constructor) + + +copyreg.pickle(Rect, __rect_reduce, __rect_constructor) # make Colors pickleable @@ -387,16 +326,20 @@ def __color_constructor(r, g, b, a): def __color_reduce(c): - assert type(c) == Color + assert isinstance(c, Color) return __color_constructor, (c.r, c.g, c.b, c.a) -copy_reg.pickle(Color, __color_reduce, __color_constructor) -# Thanks for supporting pygame. Without support now, there won't be pygame later. -if 'PYGAME_HIDE_SUPPORT_PROMPT' not in os.environ: - print('pygame %s' % ver) - print('Hello from the pygame community. https://www.pygame.org/contribute.html') +copyreg.pickle(Color, __color_reduce, __color_constructor) +# Thanks for supporting pygame. Without support now, there won't be pygame later. +if "PYGAME_HIDE_SUPPORT_PROMPT" not in os.environ: + print( + "pygame {} (SDL {}.{}.{}, Python {}.{}.{})".format( # pylint: disable=consider-using-f-string + ver, *get_sdl_version() + sys.version_info[0:3] + ) + ) + print("Hello from the pygame community. https://www.pygame.org/contribute.html") # cleanup namespace -del pygame, os, sys, surflock, MissingModule, copy_reg, PY_MAJOR_VERSION +del pygame, os, sys, MissingModule, copyreg, packager_imports diff --git a/venv/Lib/site-packages/pygame/_camera_opencv_highgui.py b/venv/Lib/site-packages/pygame/_camera_opencv_highgui.py deleted file mode 100644 index 6acf190cb7111d28e3d05f53f9218e47c1c9ab08..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/_camera_opencv_highgui.py +++ /dev/null @@ -1,98 +0,0 @@ - -import pygame -import numpy - -import opencv -#this is important for capturing/displaying images -from opencv import highgui - - - -def list_cameras(): - """ - """ - # -1 for opencv means get any of them. - return [-1] - -def init(): - pass - -def quit(): - pass - - -class Camera: - - def __init__(self, device = 0, size = (640,480), mode = "RGB"): - """ - """ - self.camera = highgui.cvCreateCameraCapture(device) - if not self.camera: - raise ValueError ("Could not open camera. Sorry.") - - - def set_controls(self, **kwargs): - """ - """ - - - def set_resolution(self, width, height): - """Sets the capture resolution. (without dialog) - """ - # nothing to do here. - pass - def query_image(self): - return True - - def stop(self): - pass - - def start(self): - # do nothing here... since the camera is already open. - pass - - def get_buffer(self): - """Returns a string containing the raw pixel data. - """ - return self.get_surface().get_buffer() - - def get_image(self, dest_surf = None): - return self.get_surface(dest_surf) - - def get_surface(self, dest_surf = None): - camera = self.camera - - im = highgui.cvQueryFrame(camera) - #convert Ipl image to PIL image - #print type(im) - if im: - xx = opencv.adaptors.Ipl2NumPy(im) - #print type(xx) - #print xx.iscontiguous() - #print dir(xx) - #print xx.shape - xxx = numpy.reshape(xx, (numpy.product(xx.shape),)) - - if xx.shape[2] != 3: - raise ValueError("not sure what to do about this size") - - pg_img = pygame.image.frombuffer(xxx, (xx.shape[1],xx.shape[0]), "RGB") - - # if there is a destination surface given, we blit onto that. - if dest_surf: - dest_surf.blit(pg_img, (0,0)) - return dest_surf - #return pg_img - - - -if __name__ == "__main__": - - # try and use this camera stuff with the pygame camera example. - import pygame.examples.camera - - pygame.camera.Camera = Camera - pygame.camera.list_cameras = list_cameras - pygame.examples.camera.main() - - diff --git a/venv/Lib/site-packages/pygame/_camera_vidcapture.py b/venv/Lib/site-packages/pygame/_camera_vidcapture.py index 7ee77b6e40fc611638bb58499207e9cdc2b42bdb..56df43d5442e8cbbc3e0f291451d22f7d417315a 100644 --- a/venv/Lib/site-packages/pygame/_camera_vidcapture.py +++ b/venv/Lib/site-packages/pygame/_camera_vidcapture.py @@ -7,6 +7,7 @@ Binary windows wheels: """ import pygame + def list_cameras(): """Always only lists one camera. @@ -15,15 +16,15 @@ def list_cameras(): return [0] # this just cycles through all the cameras trying to open them - cameras = [] - for x in range(256): - try: - c = Camera(x) - except: - break - cameras.append(x) + # cameras = [] + # for x in range(256): + # try: + # c = Camera(x) + # except: + # break + # cameras.append(x) + # return cameras - return cameras def init(): global vidcap @@ -33,27 +34,24 @@ def init(): from VideoCapture import vidcap as vc vidcap = vc + def quit(): global vidcap - pass - del vidcap + vidcap = None class Camera: - - def __init__(self, device =0, - size = (640,480), - mode = "RGB", - show_video_window=0): + # pylint: disable=unused-argument + def __init__(self, device=0, size=(640, 480), mode="RGB", show_video_window=0): """device: VideoCapture enumerates the available video capture devices - on your system. If you have more than one device, specify - the desired one here. The device number starts from 0. + on your system. If you have more than one device, specify + the desired one here. The device number starts from 0. - show_video_window: 0 ... do not display a video window (the default) - 1 ... display a video window + show_video_window: 0 ... do not display a video window (the default) + 1 ... display a video window - Mainly used for debugging, since the video window - can not be closed or moved around. + Mainly used for debugging, since the video window + can not be closed or moved around. """ self.dev = vidcap.new_Dev(device, show_video_window) width, height = size @@ -76,54 +74,40 @@ class Camera: self.dev.displaycapturepinproperties() def set_resolution(self, width, height): - """Sets the capture resolution. (without dialog) - """ + """Sets the capture resolution. (without dialog)""" self.dev.setresolution(width, height) def get_buffer(self): - """Returns a string containing the raw pixel data. - """ + """Returns a string containing the raw pixel data.""" return self.dev.getbuffer() def start(self): - """ Not implemented. - """ + """Not implemented.""" def set_controls(self, **kwargs): - """ Not implemented. - """ + """Not implemented.""" def stop(self): - """ Not implemented. - """ + """Not implemented.""" - def get_image(self, dest_surf = None): - """ - """ + def get_image(self, dest_surf=None): + """ """ return self.get_surface(dest_surf) - def get_surface(self, dest_surf = None): - """Returns a pygame Surface. - """ + def get_surface(self, dest_surf=None): + """Returns a pygame Surface.""" abuffer, width, height = self.get_buffer() - if abuffer: - surf = pygame.image.frombuffer(abuffer, (width, height), "RGB") - - # swap it from a BGR surface to an RGB surface. - r,g,b,a = surf.get_masks() - surf.set_masks((b,g,r,a)) - - r,g,b,a = surf.get_shifts() - surf.set_shifts((b,g,r,a)) - - surf = pygame.transform.flip(surf, 0,1) + if not abuffer: + return None + surf = pygame.image.frombuffer(abuffer, (width, height), "BGR") + surf = pygame.transform.flip(surf, 0, 1) + # if there is a destination surface given, we blit onto that. + if dest_surf: + dest_surf.blit(surf, (0, 0)) + else: + dest_surf = surf + return dest_surf - # if there is a destination surface given, we blit onto that. - if dest_surf: - dest_surf.blit(surf, (0,0)) - else: - dest_surf = surf - return dest_surf if __name__ == "__main__": import pygame.examples.camera diff --git a/venv/Lib/site-packages/pygame/_dummybackend.py b/venv/Lib/site-packages/pygame/_dummybackend.py deleted file mode 100644 index 49b3e30acfad852309e2810c4c6f8a2de66ab7a5..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/_dummybackend.py +++ /dev/null @@ -1,30 +0,0 @@ -"""dummy Movie class if all else fails """ -class Movie: - def __init__(self, filename, surface=None): - self.filename=filename - self.surface = surface - self.process = None - self.loops=0 - self.playing = False - self.paused = False - self._backend = "DUMMY" - self.width=0 - self.height=0 - self.finished = 1 - def play(self, loops=0): - self.playing= not self.playing - - def stop(self): - self.playing=not self.playing - self.paused =not self.paused - - def pause(self): - self.paused=not self.paused - - def resize(self, w, h): - self.width=w - self.height=h - - def __repr__(self): - return "(%s 0.0s)"%self.filename - \ No newline at end of file diff --git a/venv/Lib/site-packages/pygame/_freetype.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/_freetype.cp37-win_amd64.pyd deleted file mode 100644 index a62e7d4a4932884945ff6eb8578b84664dade7c4..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/_freetype.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/_numpysndarray.py b/venv/Lib/site-packages/pygame/_numpysndarray.py deleted file mode 100644 index f531a9a4d7d276d69bd157d4e89770792b5f508a..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/_numpysndarray.py +++ /dev/null @@ -1,76 +0,0 @@ -## pygame - Python Game Library -## Copyright (C) 2008 Marcus von Appen -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Library General Public -## License as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Library General Public License for more details. -## -## You should have received a copy of the GNU Library General Public -## License along with this library; if not, write to the Free -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -## -## Marcus von Appen -## mva@sysfault.org - -"""pygame module for accessing sound sample data using numpy - -Functions to convert between numpy arrays and Sound objects. This module -will only be available when pygame can use the external numpy package. - -Sound data is made of thousands of samples per second, and each sample -is the amplitude of the wave at a particular moment in time. For -example, in 22-kHz format, element number 5 of the array is the -amplitude of the wave after 5/22000 seconds. - -Each sample is an 8-bit or 16-bit integer, depending on the data format. -A stereo sound file has two values per sample, while a mono sound file -only has one. -""" - -import pygame -import pygame.mixer as mixer -import numpy - - -def array (sound): - """pygame._numpysndarray.array(Sound): return array - - Copy Sound samples into an array. - - Creates a new array for the sound data and copies the samples. The - array will always be in the format returned from - pygame.mixer.get_init(). - """ - - return numpy.array (sound, copy=True) - -def samples (sound): - """pygame._numpysndarray.samples(Sound): return array - - Reference Sound samples into an array. - - Creates a new array that directly references the samples in a Sound - object. Modifying the array will change the Sound. The array will - always be in the format returned from pygame.mixer.get_init(). - """ - - return numpy.array (sound, copy=False) - -def make_sound (array): - """pygame._numpysndarray.make_sound(array): return Sound - - Convert an array into a Sound object. - - Create a new playable Sound object from an array. The mixer module - must be initialized and the array format must be similar to the mixer - audio format. - """ - - return mixer.Sound (array=array) - diff --git a/venv/Lib/site-packages/pygame/_numpysurfarray.py b/venv/Lib/site-packages/pygame/_numpysurfarray.py deleted file mode 100644 index e6cf3d8b7784eb4e24b1fefdfc11c15ee2a38e28..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/_numpysurfarray.py +++ /dev/null @@ -1,356 +0,0 @@ -## pygame - Python Game Library -## Copyright (C) 2007 Marcus von Appen -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Library General Public -## License as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Library General Public License for more details. -## -## You should have received a copy of the GNU Library General Public -## License along with this library; if not, write to the Free -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -## -## Marcus von Appen -## mva@sysfault.org - -"""pygame module for accessing surface pixel data using numpy - -Functions to convert pixel data between pygame Surfaces and Numpy -arrays. This module will only be available when pygame can use the -external Numpy package. - -Note, that numpyarray is an optional module. It requires that Numpy is -installed to be used. If not installed, an exception will be raised when -it is used. eg. ImportError: no module named numpy - -Every pixel is stored as a single integer value to represent the red, -green, and blue colors. The 8bit images use a value that looks into a -colormap. Pixels with higher depth use a bit packing process to place -three or four values into a single number. - -The Numpy arrays are indexed by the X axis first, followed by the Y -axis. Arrays that treat the pixels as a single integer are referred to -as 2D arrays. This module can also separate the red, green, and blue -color values into separate indices. These types of arrays are referred -to as 3D arrays, and the last index is 0 for red, 1 for green, and 2 for -blue. - -In contrast to Numeric Numpy does use unsigned 16bit integers, images -with 16bit data will be treated as unsigned integers. -""" - -import pygame -from pygame.compat import bytes_ -from pygame.pixelcopy import array_to_surface, surface_to_array, \ - map_array as pix_map_array, make_surface as pix_make_surface -import numpy -from numpy import array as numpy_array, empty as numpy_empty, \ - around as numpy_around, uint32 as numpy_uint32, \ - ndarray as numpy_ndarray - -#float96 not available on all numpy versions. -numpy_floats = [] -for type_name in "float float32 float64 float96".split(): - if hasattr(numpy, type_name): - numpy_floats.append(getattr(numpy, type_name)) - -# Pixel sizes corresponding to NumPy supported integer sizes, and therefore -# permissible for 2D reference arrays. -_pixel2d_bitdepths = set([8, 16, 32]) - - -def blit_array (surface, array): - """pygame.surfarray.blit_array(Surface, array): return None - - Blit directly from a array values. - - Directly copy values from an array into a Surface. This is faster than - converting the array into a Surface and blitting. The array must be the - same dimensions as the Surface and will completely replace all pixel - values. Only integer, ascii character and record arrays are accepted. - - This function will temporarily lock the Surface as the new values are - copied. - """ - if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: - array = array.round(0).astype(numpy_uint32) - return array_to_surface(surface, array) - -def make_surface(array): - """pygame.surfarray.make_surface (array): return Surface - - Copy an array to a new surface. - - Create a new Surface that best resembles the data and format on the - array. The array can be 2D or 3D with any sized integer values. - """ - if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: - array = array.round(0).astype(numpy_uint32) - return pix_make_surface (array) - -def array2d(surface): - """pygame.numpyarray.array2d(Surface): return array - - copy pixels into a 2d array - - Copy the pixels from a Surface into a 2D array. The bit depth of the - surface will control the size of the integer values, and will work - for any type of pixel format. - - This function will temporarily lock the Surface as pixels are copied - (see the Surface.lock - lock the Surface memory for pixel access - method). - """ - bpp = surface.get_bytesize() - try: - dtype = (numpy.uint8, numpy.uint16, numpy.int32, numpy.int32)[bpp - 1] - except IndexError: - raise ValueError("unsupported bit depth %i for 2D array" % (bpp * 8,)) - size = surface.get_size() - array = numpy.empty(size, dtype) - surface_to_array(array, surface) - return array - -def pixels2d(surface): - """pygame.numpyarray.pixels2d(Surface): return array - - reference pixels into a 2d array - - Create a new 2D array that directly references the pixel values in a - Surface. Any changes to the array will affect the pixels in the - Surface. This is a fast operation since no data is copied. - - Pixels from a 24-bit Surface cannot be referenced, but all other - Surface bit depths can. - - The Surface this references will remain locked for the lifetime of - the array (see the Surface.lock - lock the Surface memory for pixel - access method). - """ - if (surface.get_bitsize() not in _pixel2d_bitdepths): - raise ValueError("unsupport bit depth for 2D reference array") - try: - return numpy_array(surface.get_view('2'), copy=False) - except (ValueError, TypeError): - raise ValueError("bit depth %i unsupported for 2D reference array" % - (surface.get_bitsize(),)) - -def array3d(surface): - """pygame.numpyarray.array3d(Surface): return array - - copy pixels into a 3d array - - Copy the pixels from a Surface into a 3D array. The bit depth of the - surface will control the size of the integer values, and will work - for any type of pixel format. - - This function will temporarily lock the Surface as pixels are copied - (see the Surface.lock - lock the Surface memory for pixel access - method). - """ - w, h = surface.get_size() - array = numpy.empty((w, h, 3), numpy.uint8) - surface_to_array(array, surface) - return array - -def pixels3d (surface): - """pygame.numpyarray.pixels3d(Surface): return array - - reference pixels into a 3d array - - Create a new 3D array that directly references the pixel values in a - Surface. Any changes to the array will affect the pixels in the - Surface. This is a fast operation since no data is copied. - - This will only work on Surfaces that have 24-bit or 32-bit - formats. Lower pixel formats cannot be referenced. - - The Surface this references will remain locked for the lifetime of - the array (see the Surface.lock - lock the Surface memory for pixel - access method). - """ - return numpy_array(surface.get_view('3'), copy=False) - -def array_alpha(surface): - """pygame.numpyarray.array_alpha(Surface): return array - - copy pixel alphas into a 2d array - - Copy the pixel alpha values (degree of transparency) from a Surface - into a 2D array. This will work for any type of Surface - format. Surfaces without a pixel alpha will return an array with all - opaque values. - - This function will temporarily lock the Surface as pixels are copied - (see the Surface.lock - lock the Surface memory for pixel access - method). - """ - size = surface.get_size() - array = numpy.empty(size, numpy.uint8) - surface_to_array(array, surface, 'A') - return array - -def pixels_alpha(surface): - """pygame.numpyarray.pixels_alpha(Surface): return array - - reference pixel alphas into a 2d array - - Create a new 2D array that directly references the alpha values - (degree of transparency) in a Surface. Any changes to the array will - affect the pixels in the Surface. This is a fast operation since no - data is copied. - - This can only work on 32-bit Surfaces with a per-pixel alpha value. - - The Surface this array references will remain locked for the - lifetime of the array. - """ - return numpy.array(surface.get_view('A'), copy=False) - -def pixels_red(surface): - """pygame.surfarray.pixels_red(Surface): return array - - Reference pixel red into a 2d array. - - Create a new 2D array that directly references the red values - in a Surface. Any changes to the array will affect the pixels - in the Surface. This is a fast operation since no data is copied. - - This can only work on 24-bit or 32-bit Surfaces. - - The Surface this array references will remain locked for the - lifetime of the array. - """ - return numpy.array(surface.get_view('R'), copy=False) - -def array_red(surface): - """pygame.numpyarray.array_red(Surface): return array - - copy pixel red into a 2d array - - Copy the pixel red values from a Surface into a 2D array. This will work - for any type of Surface format. - - This function will temporarily lock the Surface as pixels are copied - (see the Surface.lock - lock the Surface memory for pixel access - method). - """ - size = surface.get_size() - array = numpy.empty(size, numpy.uint8) - surface_to_array(array, surface, 'R') - return array - -def pixels_green(surface): - """pygame.surfarray.pixels_green(Surface): return array - - Reference pixel green into a 2d array. - - Create a new 2D array that directly references the green values - in a Surface. Any changes to the array will affect the pixels - in the Surface. This is a fast operation since no data is copied. - - This can only work on 24-bit or 32-bit Surfaces. - - The Surface this array references will remain locked for the - lifetime of the array. - """ - return numpy.array(surface.get_view('G'), copy=False) - -def array_green(surface): - """pygame.numpyarray.array_green(Surface): return array - - copy pixel green into a 2d array - - Copy the pixel green values from a Surface into a 2D array. This will work - for any type of Surface format. - - This function will temporarily lock the Surface as pixels are copied - (see the Surface.lock - lock the Surface memory for pixel access - method). - """ - size = surface.get_size() - array = numpy.empty(size, numpy.uint8) - surface_to_array(array, surface, 'G') - return array - -def pixels_blue (surface): - """pygame.surfarray.pixels_blue(Surface): return array - - Reference pixel blue into a 2d array. - - Create a new 2D array that directly references the blue values - in a Surface. Any changes to the array will affect the pixels - in the Surface. This is a fast operation since no data is copied. - - This can only work on 24-bit or 32-bit Surfaces. - - The Surface this array references will remain locked for the - lifetime of the array. - """ - return numpy.array(surface.get_view('B'), copy=False) - -def array_blue(surface): - """pygame.numpyarray.array_blue(Surface): return array - - copy pixel blue into a 2d array - - Copy the pixel blue values from a Surface into a 2D array. This will work - for any type of Surface format. - - This function will temporarily lock the Surface as pixels are copied - (see the Surface.lock - lock the Surface memory for pixel access - method). - """ - size = surface.get_size() - array = numpy.empty(size, numpy.uint8) - surface_to_array(array, surface, 'B') - return array - -def array_colorkey(surface): - """pygame.numpyarray.array_colorkey(Surface): return array - - copy the colorkey values into a 2d array - - Create a new array with the colorkey transparency value from each - pixel. If the pixel matches the colorkey it will be fully - tranparent; otherwise it will be fully opaque. - - This will work on any type of Surface format. If the image has no - colorkey a solid opaque array will be returned. - - This function will temporarily lock the Surface as pixels are - copied. - """ - size = surface.get_size() - array = numpy.empty(size, numpy.uint8) - surface_to_array(array, surface, 'C') - return array - -def map_array(surface, array): - """pygame.numpyarray.map_array(Surface, array3d): return array2d - - map a 3d array into a 2d array - - Convert a 3D array into a 2D array. This will use the given Surface - format to control the conversion. - - Note: arrays do not need to be 3D, as long as the minor axis has - three elements giving the component colours, any array shape can be - used (for example, a single colour can be mapped, or an array of - colours). The array shape is limited to eleven dimensions maximum, - including the three element minor axis. - """ - if array.ndim == 0: - raise ValueError("array must have at least 1 dimension") - shape = array.shape - if shape[-1] != 3: - raise ValueError("array must be a 3d array of 3-value color data") - target = numpy_empty(shape[:-1], numpy.int32) - pix_map_array(target, array, surface) - return target - diff --git a/venv/Lib/site-packages/pygame/base.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/base.cp37-win_amd64.pyd deleted file mode 100644 index 7137d14a1b193e8c457e11492c3bfa87de6a702e..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/base.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/bufferproxy.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/bufferproxy.cp37-win_amd64.pyd deleted file mode 100644 index b2e09e742d0d421f0b5fc196f60e3b382f2a8dd3..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/bufferproxy.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/camera.py b/venv/Lib/site-packages/pygame/camera.py index 3f183505b6d3171d40d76901a9ca2e5fc6a3ae44..9fc09ef70494686f63a36ac8c0fe6bf717f86a31 100644 --- a/venv/Lib/site-packages/pygame/camera.py +++ b/venv/Lib/site-packages/pygame/camera.py @@ -1,146 +1,211 @@ +import os +import platform +import sys +import warnings +from abc import ABC, abstractmethod -_is_init = 0 +from pygame import error +_is_init = False -def init(): - global list_cameras, Camera, colorspace, _is_init +class AbstractCamera(ABC): + # set_controls and get_controls are not a part of the AbstractCamera ABC, + # because implementations of the same can vary across different Camera + # types + @abstractmethod + def __init__(self, *args, **kwargs): + """ """ + @abstractmethod + def start(self): + """ """ - import os,sys + @abstractmethod + def stop(self): + """ """ - use_opencv = False - use_vidcapture = False - use__camera = True + @abstractmethod + def get_size(self): + """ """ + @abstractmethod + def query_image(self): + """ """ - if sys.platform == 'win32': - use_vidcapture = True - use__camera = False + @abstractmethod + def get_image(self, dest_surf=None): + """ """ - elif "linux" in sys.platform: - use__camera = True - elif "darwin" in sys.platform: - use__camera = True - else: - use_opencv = True + @abstractmethod + def get_raw(self): + """ """ +def _pre_init_placeholder(): + if not _is_init: + raise error("pygame.camera is not initialized") - # see if we have any user specified defaults in environments. - camera_env = os.environ.get("PYGAME_CAMERA", "") - if camera_env == "opencv": - use_opencv = True - if camera_env == "vidcapture": - use_vidcapture = True + # camera was init, and yet functions are not monkey patched. This should + # not happen + raise NotImplementedError() +def _pre_init_placeholder_varargs(*_, **__): + _pre_init_placeholder() - # select the camera module to import here. - # the _camera module has some code which can be reused by other modules. - # it will also be the default one. - if use__camera: +class _PreInitPlaceholderCamera(AbstractCamera): + __init__ = _pre_init_placeholder_varargs + start = _pre_init_placeholder_varargs + stop = _pre_init_placeholder_varargs + get_controls = _pre_init_placeholder_varargs + set_controls = _pre_init_placeholder_varargs + get_size = _pre_init_placeholder_varargs + query_image = _pre_init_placeholder_varargs + get_image = _pre_init_placeholder_varargs + get_raw = _pre_init_placeholder_varargs + + +list_cameras = _pre_init_placeholder +Camera = _PreInitPlaceholderCamera + + +def _colorspace_not_available(*args): + raise RuntimeError("pygame is not built with colorspace support") + + +try: + from pygame import _camera + + colorspace = _camera.colorspace +except ImportError: + # Should not happen in most cases + colorspace = _colorspace_not_available + + +def _setup_backend(backend): + global list_cameras, Camera + if backend == "opencv-mac": + from pygame import _camera_opencv + + list_cameras = _camera_opencv.list_cameras_darwin + Camera = _camera_opencv.CameraMac + + elif backend == "opencv": + from pygame import _camera_opencv + + list_cameras = _camera_opencv.list_cameras + Camera = _camera_opencv.Camera + + elif backend in ("_camera (msmf)", "_camera (v4l2)"): from pygame import _camera - colorspace = _camera.colorspace list_cameras = _camera.list_cameras Camera = _camera.Camera - if use_opencv: - try: - from pygame import _camera_opencv_highgui - except: - _camera_opencv_highgui = None + elif backend == "videocapture": + from pygame import _camera_vidcapture - if _camera_opencv_highgui: - _camera_opencv_highgui.init() + warnings.warn( + "The VideoCapture backend is not recommended and may be removed." + "For Python3 and Windows 8+, there is now a native Windows " + "backend built into pygame.", + DeprecationWarning, + stacklevel=2, + ) - list_cameras = _camera_opencv_highgui.list_cameras - Camera = _camera_opencv_highgui.Camera + _camera_vidcapture.init() + list_cameras = _camera_vidcapture.list_cameras + Camera = _camera_vidcapture.Camera + else: + raise ValueError("unrecognized backend name") - if use_vidcapture: - try: - from pygame import _camera_vidcapture - except: - _camera_vidcapture = None - if _camera_vidcapture: - _camera_vidcapture.init() - list_cameras = _camera_vidcapture.list_cameras - Camera = _camera_vidcapture.Camera +def get_backends(): + possible_backends = [] + if sys.platform == "win32" and int(platform.win32_ver()[0].split(".")[0]) >= 8: + try: + # If cv2 is installed, prefer that on windows. + import cv2 + possible_backends.append("OpenCV") + except ImportError: + possible_backends.append("_camera (MSMF)") - _is_init = 1 - pass + if "linux" in sys.platform: + possible_backends.append("_camera (V4L2)") + if "darwin" in sys.platform: + possible_backends.append("OpenCV-Mac") -def quit(): - global _is_init - _is_init = 0 - pass - + if "OpenCV" not in possible_backends: + possible_backends.append("OpenCV") -def _check_init(): - global _is_init - if not _is_init: - raise ValueError("Need to call camera.init() before using.") + if sys.platform == "win32": + possible_backends.append("VideoCapture") -def list_cameras(): - """ - """ - _check_init() - raise NotImplementedError() + # see if we have any user specified defaults in environments. + camera_env = os.environ.get("PYGAME_CAMERA", "").lower() + if camera_env == "opencv": # prioritize opencv + if "OpenCV" in possible_backends: + possible_backends.remove("OpenCV") + possible_backends = ["OpenCV"] + possible_backends + if camera_env in ("vidcapture", "videocapture"): # prioritize vidcapture + if "VideoCapture" in possible_backends: + possible_backends.remove("VideoCapture") + possible_backends = ["VideoCapture"] + possible_backends -class Camera: + return possible_backends - def __init__(self, device =0, size = (320, 200), mode = "RGB"): - """ - """ - _check_init() - raise NotImplementedError() - def set_resolution(self, width, height): - """Sets the capture resolution. (without dialog) - """ - pass +def init(backend=None): + global _is_init + # select the camera module to import here. - def start(self): - """ - """ + backends = [b.lower() for b in get_backends()] + if not backends: + raise error("No camera backends are supported on your platform!") - def stop(self): - """ - """ + backend = backends[0] if backend is None else backend.lower() + if backend not in backends: + warnings.warn( + "We don't think this is a supported backend on this system, " + "but we'll try it...", + Warning, + stacklevel=2, + ) - def get_buffer(self): - """ - """ + try: + _setup_backend(backend) + except ImportError: + emsg = f"Backend '{backend}' is not supported on your platform!" + if backend in ("opencv", "opencv-mac", "videocapture"): + dep = "vidcap" if backend == "videocapture" else "OpenCV" + emsg += ( + f" Make sure you have '{dep}' installed to be able to use this backend" + ) - def set_controls(self, **kwargs): - """ - """ + raise error(emsg) - def get_image(self, dest_surf = None): - """ - """ + _is_init = True - def get_surface(self, dest_surf = None): - """ - """ +def quit(): + global _is_init, Camera, list_cameras + # reset to their respective pre-init placeholders + list_cameras = _pre_init_placeholder + Camera = _PreInitPlaceholderCamera + _is_init = False -if __name__ == "__main__": +if __name__ == "__main__": # try and use this camera stuff with the pygame camera example. import pygame.examples.camera - #pygame.camera.Camera = Camera - #pygame.camera.list_cameras = list_cameras + # pygame.camera.Camera = Camera + # pygame.camera.list_cameras = list_cameras pygame.examples.camera.main() - - - diff --git a/venv/Lib/site-packages/pygame/cdrom.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/cdrom.cp37-win_amd64.pyd deleted file mode 100644 index cf32ce41a276625286fa57761766447531e40d84..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/cdrom.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/color.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/color.cp37-win_amd64.pyd deleted file mode 100644 index 50fc7c082c20f9b4cc2b9b0763d9d74726f9e584..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/color.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/colordict.py b/venv/Lib/site-packages/pygame/colordict.py index 52b1166ab0bf4016e92f9d659c90fe9ac86ceebc..0bd72564d7a3653339b422a1e358a4522b8d7a17 100644 --- a/venv/Lib/site-packages/pygame/colordict.py +++ b/venv/Lib/site-packages/pygame/colordict.py @@ -1,684 +1,692 @@ -## pygame - Python Game Library -## Copyright (C) 2000-2003 Pete Shinners -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Library General Public -## License as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Library General Public License for more details. -## -## You should have received a copy of the GNU Library General Public -## License along with this library; if not, write to the Free -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -## -## Pete Shinners -## pete@shinners.org +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org -from pygame.compat import unicode_ +""" A dictionary of RGBA tuples indexed by color names. + +See https://www.pygame.org/docs/ref/color_list.html for sample swatches. +""" THECOLORS = { -'gray17' : (43, 43, 43, 255) , -'gold' : (255, 215, 0, 255) , -'gray10' : (26, 26, 26, 255) , -'yellow' : (255, 255, 0, 255) , -'gray11' : (28, 28, 28, 255) , -'grey61' : (156, 156, 156, 255) , -'grey60' : (153, 153, 153, 255) , -'darkseagreen' : (143, 188, 143, 255) , -'grey62' : (158, 158, 158, 255) , -'grey65' : (166, 166, 166, 255) , -'gray12' : (31, 31, 31, 255) , -'grey67' : (171, 171, 171, 255) , -'grey66' : (168, 168, 168, 255) , -'grey69' : (176, 176, 176, 255) , -'gray21' : (54, 54, 54, 255) , -'lightsalmon4' : (139, 87, 66, 255) , -'lightsalmon2' : (238, 149, 114, 255) , -'lightsalmon3' : (205, 129, 98, 255) , -'lightsalmon1' : (255, 160, 122, 255) , -'gray32' : (82, 82, 82, 255) , -'green4' : (0, 139, 0, 255) , -'gray30' : (77, 77, 77, 255) , -'gray31' : (79, 79, 79, 255) , -'green1' : (0, 255, 0, 255) , -'gray37' : (94, 94, 94, 255) , -'green3' : (0, 205, 0, 255) , -'green2' : (0, 238, 0, 255) , -'darkslategray1' : (151, 255, 255, 255) , -'darkslategray2' : (141, 238, 238, 255) , -'darkslategray3' : (121, 205, 205, 255) , -'aquamarine1' : (127, 255, 212, 255) , -'aquamarine3' : (102, 205, 170, 255) , -'aquamarine2' : (118, 238, 198, 255) , -'papayawhip' : (255, 239, 213, 255) , -'black' : (0, 0, 0, 255) , -'darkorange3' : (205, 102, 0, 255) , -'oldlace' : (253, 245, 230, 255) , -'lightgoldenrod4' : (139, 129, 76, 255) , -'gray90' : (229, 229, 229, 255) , -'orchid1' : (255, 131, 250, 255) , -'orchid2' : (238, 122, 233, 255) , -'orchid3' : (205, 105, 201, 255) , -'grey68' : (173, 173, 173, 255) , -'brown' : (165, 42, 42, 255) , -'purple2' : (145, 44, 238, 255) , -'gray80' : (204, 204, 204, 255) , -'antiquewhite3' : (205, 192, 176, 255) , -'antiquewhite2' : (238, 223, 204, 255) , -'antiquewhite1' : (255, 239, 219, 255) , -'palevioletred3' : (205, 104, 137, 255) , -'hotpink' : (255, 105, 180, 255) , -'lightcyan' : (224, 255, 255, 255) , -'coral3' : (205, 91, 69, 255) , -'gray8' : (20, 20, 20, 255) , -'gray9' : (23, 23, 23, 255) , -'grey32' : (82, 82, 82, 255) , -'bisque4' : (139, 125, 107, 255) , -'cyan' : (0, 255, 255, 255) , -'gray0' : (0, 0, 0, 255) , -'gray1' : (3, 3, 3, 255) , -'gray6' : (15, 15, 15, 255) , -'bisque1' : (255, 228, 196, 255) , -'bisque2' : (238, 213, 183, 255) , -'bisque3' : (205, 183, 158, 255) , -'skyblue' : (135, 206, 235, 255) , -'gray' : (190, 190, 190, 255) , -'darkturquoise' : (0, 206, 209, 255) , -'rosybrown4' : (139, 105, 105, 255) , -'deepskyblue3' : (0, 154, 205, 255) , -'grey63' : (161, 161, 161, 255) , -'indianred1' : (255, 106, 106, 255) , -'grey78' : (199, 199, 199, 255) , -'lightpink' : (255, 182, 193, 255) , -'gray88' : (224, 224, 224, 255) , -'gray22' : (56, 56, 56, 255) , -'red' : (255, 0, 0, 255) , -'grey11' : (28, 28, 28, 255) , -'lemonchiffon3' : (205, 201, 165, 255) , -'lemonchiffon2' : (238, 233, 191, 255) , -'lemonchiffon1' : (255, 250, 205, 255) , -'indianred3' : (205, 85, 85, 255) , -'violetred1' : (255, 62, 150, 255) , -'plum2' : (238, 174, 238, 255) , -'plum1' : (255, 187, 255, 255) , -'lemonchiffon4' : (139, 137, 112, 255) , -'gray99' : (252, 252, 252, 255) , -'grey13' : (33, 33, 33, 255) , -'grey55' : (140, 140, 140, 255) , -'darkcyan' : (0, 139, 139, 255) , -'chocolate4' : (139, 69, 19, 255) , -'lightgoldenrodyellow' : (250, 250, 210, 255) , -'gray54' : (138, 138, 138, 255) , -'lavender' : (230, 230, 250, 255) , -'chartreuse3' : (102, 205, 0, 255) , -'chartreuse2' : (118, 238, 0, 255) , -'chartreuse1' : (127, 255, 0, 255) , -'grey48' : (122, 122, 122, 255) , -'grey16' : (41, 41, 41, 255) , -'thistle' : (216, 191, 216, 255) , -'chartreuse4' : (69, 139, 0, 255) , -'darkorchid4' : (104, 34, 139, 255) , -'grey42' : (107, 107, 107, 255) , -'grey41' : (105, 105, 105, 255) , -'grey17' : (43, 43, 43, 255) , -'dimgrey' : (105, 105, 105, 255) , -'dodgerblue4' : (16, 78, 139, 255) , -'darkorchid2' : (178, 58, 238, 255) , -'darkorchid3' : (154, 50, 205, 255) , -'blue' : (0, 0, 255, 255) , -'rosybrown2' : (238, 180, 180, 255) , -'honeydew' : (240, 255, 240, 255) , -'gray18' : (46, 46, 46, 255) , -'cornflowerblue' : (100, 149, 237, 255) , -'grey91' : (232, 232, 232, 255) , -'gray14' : (36, 36, 36, 255) , -'gray15' : (38, 38, 38, 255) , -'gray16' : (41, 41, 41, 255) , -'maroon4' : (139, 28, 98, 255) , -'maroon3' : (205, 41, 144, 255) , -'maroon2' : (238, 48, 167, 255) , -'maroon1' : (255, 52, 179, 255) , -'gray13' : (33, 33, 33, 255) , -'gold3' : (205, 173, 0, 255) , -'gold2' : (238, 201, 0, 255) , -'gold1' : (255, 215, 0, 255) , -'grey79' : (201, 201, 201, 255) , -'palevioletred1' : (255, 130, 171, 255) , -'palevioletred2' : (238, 121, 159, 255) , -'gold4' : (139, 117, 0, 255) , -'gray41' : (105, 105, 105, 255) , -'gray84' : (214, 214, 214, 255) , -'mediumpurple' : (147, 112, 219, 255) , -'rosybrown1' : (255, 193, 193, 255) , -'lightblue2' : (178, 223, 238, 255) , -'lightblue3' : (154, 192, 205, 255) , -'grey57' : (145, 145, 145, 255) , -'lightblue1' : (191, 239, 255, 255) , -'lightblue4' : (104, 131, 139, 255) , -'gray33' : (84, 84, 84, 255) , -'skyblue4' : (74, 112, 139, 255) , -'grey97' : (247, 247, 247, 255) , -'skyblue1' : (135, 206, 255, 255) , -'gray27' : (69, 69, 69, 255) , -'skyblue3' : (108, 166, 205, 255) , -'skyblue2' : (126, 192, 238, 255) , -'lavenderblush1' : (255, 240, 245, 255) , -'darkgrey' : (169, 169, 169, 255) , -'lavenderblush3' : (205, 193, 197, 255) , -'darkslategrey' : (47, 79, 79, 255) , -'lavenderblush4' : (139, 131, 134, 255) , -'deeppink4' : (139, 10, 80, 255) , -'grey99' : (252, 252, 252, 255) , -'gray36' : (92, 92, 92, 255) , -'coral4' : (139, 62, 47, 255) , -'magenta3' : (205, 0, 205, 255) , -'lightskyblue4' : (96, 123, 139, 255) , -'mediumturquoise' : (72, 209, 204, 255) , -'gray34' : (87, 87, 87, 255) , -'floralwhite' : (255, 250, 240, 255) , -'grey39' : (99, 99, 99, 255) , -'grey36' : (92, 92, 92, 255) , -'grey37' : (94, 94, 94, 255) , -'grey34' : (87, 87, 87, 255) , -'gray26' : (66, 66, 66, 255) , -'royalblue2' : (67, 110, 238, 255) , -'grey33' : (84, 84, 84, 255) , -'turquoise1' : (0, 245, 255, 255) , -'grey31' : (79, 79, 79, 255) , -'steelblue1' : (99, 184, 255, 255) , -'sienna4' : (139, 71, 38, 255) , -'steelblue3' : (79, 148, 205, 255) , -'lavenderblush2' : (238, 224, 229, 255) , -'sienna1' : (255, 130, 71, 255) , -'steelblue4' : (54, 100, 139, 255) , -'sienna3' : (205, 104, 57, 255) , -'aquamarine4' : (69, 139, 116, 255) , -'lightyellow1' : (255, 255, 224, 255) , -'lightyellow2' : (238, 238, 209, 255) , -'lightsteelblue' : (176, 196, 222, 255) , -'lightyellow4' : (139, 139, 122, 255) , -'magenta2' : (238, 0, 238, 255) , -'lightskyblue1' : (176, 226, 255, 255) , -'lightgoldenrod' : (238, 221, 130, 255) , -'magenta4' : (139, 0, 139, 255) , -'gray87' : (222, 222, 222, 255) , -'greenyellow' : (173, 255, 47, 255) , -'navajowhite4' : (139, 121, 94, 255) , -'darkslategray4' : (82, 139, 139, 255) , -'olivedrab' : (107, 142, 35, 255) , -'navajowhite1' : (255, 222, 173, 255) , -'navajowhite2' : (238, 207, 161, 255) , -'darkgoldenrod1' : (255, 185, 15, 255) , -'sienna' : (160, 82, 45, 255) , -'blue1' : (0, 0, 255, 255) , -'yellow1' : (255, 255, 0, 255) , -'gray61' : (156, 156, 156, 255) , -'magenta1' : (255, 0, 255, 255) , -'grey52' : (133, 133, 133, 255) , -'orangered4' : (139, 37, 0, 255) , -'palegreen' : (152, 251, 152, 255) , -'gray86' : (219, 219, 219, 255) , -'grey80' : (204, 204, 204, 255) , -'seashell' : (255, 245, 238, 255) , -'royalblue' : (65, 105, 225, 255) , -'firebrick3' : (205, 38, 38, 255) , -'blue4' : (0, 0, 139, 255) , -'peru' : (205, 133, 63, 255) , -'gray60' : (153, 153, 153, 255) , -'aquamarine' : (127, 255, 212, 255) , -'grey53' : (135, 135, 135, 255) , -'tan4' : (139, 90, 43, 255) , -'darkgoldenrod' : (184, 134, 11, 255) , -'tan2' : (238, 154, 73, 255) , -'tan1' : (255, 165, 79, 255) , -'darkslategray' : (47, 79, 79, 255) , -'royalblue3' : (58, 95, 205, 255) , -'red2' : (238, 0, 0, 255) , -'red1' : (255, 0, 0, 255) , -'dodgerblue' : (30, 144, 255, 255) , -'violetred4' : (139, 34, 82, 255) , -'lightyellow' : (255, 255, 224, 255) , -'paleturquoise1' : (187, 255, 255, 255) , -'firebrick2' : (238, 44, 44, 255) , -'mediumaquamarine' : (102, 205, 170, 255) , -'lemonchiffon' : (255, 250, 205, 255) , -'chocolate' : (210, 105, 30, 255) , -'orchid4' : (139, 71, 137, 255) , -'maroon' : (176, 48, 96, 255) , -'gray38' : (97, 97, 97, 255) , -'darkorange4' : (139, 69, 0, 255) , -'mintcream' : (245, 255, 250, 255) , -'darkorange1' : (255, 127, 0, 255) , -'antiquewhite' : (250, 235, 215, 255) , -'darkorange2' : (238, 118, 0, 255) , -'grey18' : (46, 46, 46, 255) , -'grey19' : (48, 48, 48, 255) , -'grey38' : (97, 97, 97, 255) , -'moccasin' : (255, 228, 181, 255) , -'grey10' : (26, 26, 26, 255) , -'chocolate1' : (255, 127, 36, 255) , -'chocolate2' : (238, 118, 33, 255) , -'chocolate3' : (205, 102, 29, 255) , -'saddlebrown' : (139, 69, 19, 255) , -'grey15' : (38, 38, 38, 255) , -'darkslateblue' : (72, 61, 139, 255) , -'lightskyblue' : (135, 206, 250, 255) , -'gray69' : (176, 176, 176, 255) , -'gray68' : (173, 173, 173, 255) , -'deeppink' : (255, 20, 147, 255) , -'gray65' : (166, 166, 166, 255) , -'gray64' : (163, 163, 163, 255) , -'gray67' : (171, 171, 171, 255) , -'gray66' : (168, 168, 168, 255) , -'gray25' : (64, 64, 64, 255) , -'coral' : (255, 127, 80, 255) , -'gray63' : (161, 161, 161, 255) , -'gray62' : (158, 158, 158, 255) , -'goldenrod4' : (139, 105, 20, 255) , -'grey35' : (89, 89, 89, 255) , -'gray89' : (227, 227, 227, 255) , -'goldenrod1' : (255, 193, 37, 255) , -'goldenrod2' : (238, 180, 34, 255) , -'goldenrod3' : (205, 155, 29, 255) , -'springgreen1' : (0, 255, 127, 255) , -'springgreen2' : (0, 238, 118, 255) , -'springgreen3' : (0, 205, 102, 255) , -'springgreen4' : (0, 139, 69, 255) , -'mistyrose1' : (255, 228, 225, 255) , -'sandybrown' : (244, 164, 96, 255) , -'grey30' : (77, 77, 77, 255) , -'seashell2' : (238, 229, 222, 255) , -'seashell3' : (205, 197, 191, 255) , -'tan' : (210, 180, 140, 255) , -'seashell1' : (255, 245, 238, 255) , -'mistyrose3' : (205, 183, 181, 255) , -'magenta' : (255, 0, 255, 255) , -'pink' : (255, 192, 203, 255) , -'ivory2' : (238, 238, 224, 255) , -'ivory1' : (255, 255, 240, 255) , -'lightcyan2' : (209, 238, 238, 255) , -'mediumseagreen' : (60, 179, 113, 255) , -'ivory4' : (139, 139, 131, 255) , -'darkorange' : (255, 140, 0, 255) , -'powderblue' : (176, 224, 230, 255) , -'dodgerblue1' : (30, 144, 255, 255) , -'gray95' : (242, 242, 242, 255) , -'firebrick1' : (255, 48, 48, 255) , -'gray7' : (18, 18, 18, 255) , -'mistyrose4' : (139, 125, 123, 255) , -'tomato' : (255, 99, 71, 255) , -'indianred2' : (238, 99, 99, 255) , -'steelblue2' : (92, 172, 238, 255) , -'gray100' : (255, 255, 255, 255) , -'seashell4' : (139, 134, 130, 255) , -'grey89' : (227, 227, 227, 255) , -'grey88' : (224, 224, 224, 255) , -'grey87' : (222, 222, 222, 255) , -'grey86' : (219, 219, 219, 255) , -'grey85' : (217, 217, 217, 255) , -'grey84' : (214, 214, 214, 255) , -'midnightblue' : (25, 25, 112, 255) , -'grey82' : (209, 209, 209, 255) , -'grey81' : (207, 207, 207, 255) , -'yellow3' : (205, 205, 0, 255) , -'ivory3' : (205, 205, 193, 255) , -'grey22' : (56, 56, 56, 255) , -'gray85' : (217, 217, 217, 255) , -'violetred3' : (205, 50, 120, 255) , -'dodgerblue2' : (28, 134, 238, 255) , -'gray42' : (107, 107, 107, 255) , -'sienna2' : (238, 121, 66, 255) , -'grey72' : (184, 184, 184, 255) , -'grey73' : (186, 186, 186, 255) , -'grey70' : (179, 179, 179, 255) , -'palevioletred' : (219, 112, 147, 255) , -'lightslategray' : (119, 136, 153, 255) , -'grey77' : (196, 196, 196, 255) , -'grey74' : (189, 189, 189, 255) , -'slategray1' : (198, 226, 255, 255) , -'pink1' : (255, 181, 197, 255) , -'mediumpurple1' : (171, 130, 255, 255) , -'pink3' : (205, 145, 158, 255) , -'antiquewhite4' : (139, 131, 120, 255) , -'lightpink1' : (255, 174, 185, 255) , -'honeydew2' : (224, 238, 224, 255) , -'khaki4' : (139, 134, 78, 255) , -'darkolivegreen4' : (110, 139, 61, 255) , -'gray45' : (115, 115, 115, 255) , -'slategray3' : (159, 182, 205, 255) , -'darkolivegreen1' : (202, 255, 112, 255) , -'khaki1' : (255, 246, 143, 255) , -'khaki2' : (238, 230, 133, 255) , -'khaki3' : (205, 198, 115, 255) , -'lavenderblush' : (255, 240, 245, 255) , -'honeydew4' : (131, 139, 131, 255) , -'salmon3' : (205, 112, 84, 255) , -'salmon2' : (238, 130, 98, 255) , -'gray92' : (235, 235, 235, 255) , -'salmon4' : (139, 76, 57, 255) , -'gray49' : (125, 125, 125, 255) , -'gray48' : (122, 122, 122, 255) , -'linen' : (250, 240, 230, 255) , -'burlywood1' : (255, 211, 155, 255) , -'green' : (0, 255, 0, 255) , -'gray47' : (120, 120, 120, 255) , -'blueviolet' : (138, 43, 226, 255) , -'brown2' : (238, 59, 59, 255) , -'brown3' : (205, 51, 51, 255) , -'peachpuff' : (255, 218, 185, 255) , -'brown4' : (139, 35, 35, 255) , -'firebrick4' : (139, 26, 26, 255) , -'azure1' : (240, 255, 255, 255) , -'azure3' : (193, 205, 205, 255) , -'azure2' : (224, 238, 238, 255) , -'azure4' : (131, 139, 139, 255) , -'tomato4' : (139, 54, 38, 255) , -'orange4' : (139, 90, 0, 255) , -'firebrick' : (178, 34, 34, 255) , -'indianred' : (205, 92, 92, 255) , -'orange1' : (255, 165, 0, 255) , -'orange3' : (205, 133, 0, 255) , -'orange2' : (238, 154, 0, 255) , -'darkolivegreen' : (85, 107, 47, 255) , -'gray2' : (5, 5, 5, 255) , -'slategrey' : (112, 128, 144, 255) , -'gray81' : (207, 207, 207, 255) , -'darkred' : (139, 0, 0, 255) , -'gray3' : (8, 8, 8, 255) , -'lightsteelblue1' : (202, 225, 255, 255) , -'lightsteelblue2' : (188, 210, 238, 255) , -'lightsteelblue3' : (162, 181, 205, 255) , -'lightsteelblue4' : (110, 123, 139, 255) , -'tomato3' : (205, 79, 57, 255) , -'gray43' : (110, 110, 110, 255) , -'darkgoldenrod4' : (139, 101, 8, 255) , -'grey50' : (127, 127, 127, 255) , -'yellow4' : (139, 139, 0, 255) , -'mediumorchid' : (186, 85, 211, 255) , -'yellow2' : (238, 238, 0, 255) , -'darkgoldenrod2' : (238, 173, 14, 255) , -'darkgoldenrod3' : (205, 149, 12, 255) , -'chartreuse' : (127, 255, 0, 255) , -'mediumblue' : (0, 0, 205, 255) , -'gray4' : (10, 10, 10, 255) , -'springgreen' : (0, 255, 127, 255) , -'orange' : (255, 165, 0, 255) , -'gray5' : (13, 13, 13, 255) , -'lightsalmon' : (255, 160, 122, 255) , -'gray19' : (48, 48, 48, 255) , -'turquoise' : (64, 224, 208, 255) , -'lightseagreen' : (32, 178, 170, 255) , -'grey8' : (20, 20, 20, 255) , -'grey9' : (23, 23, 23, 255) , -'grey6' : (15, 15, 15, 255) , -'grey7' : (18, 18, 18, 255) , -'grey4' : (10, 10, 10, 255) , -'grey5' : (13, 13, 13, 255) , -'grey2' : (5, 5, 5, 255) , -'grey3' : (8, 8, 8, 255) , -'grey0' : (0, 0, 0, 255) , -'grey1' : (3, 3, 3, 255) , -'gray50' : (127, 127, 127, 255) , -'goldenrod' : (218, 165, 32, 255) , -'grey58' : (148, 148, 148, 255) , -'grey59' : (150, 150, 150, 255) , -'gray51' : (130, 130, 130, 255) , -'grey54' : (138, 138, 138, 255) , -'mediumorchid4' : (122, 55, 139, 255) , -'grey56' : (143, 143, 143, 255) , -'navajowhite3' : (205, 179, 139, 255) , -'mediumorchid1' : (224, 102, 255, 255) , -'grey51' : (130, 130, 130, 255) , -'mediumorchid3' : (180, 82, 205, 255) , -'mediumorchid2' : (209, 95, 238, 255) , -'cyan2' : (0, 238, 238, 255) , -'cyan3' : (0, 205, 205, 255) , -'gray23' : (59, 59, 59, 255) , -'cyan1' : (0, 255, 255, 255) , -'darkgreen' : (0, 100, 0, 255) , -'gray24' : (61, 61, 61, 255) , -'cyan4' : (0, 139, 139, 255) , -'darkviolet' : (148, 0, 211, 255) , -'peachpuff4' : (139, 119, 101, 255) , -'gray28' : (71, 71, 71, 255) , -'slateblue4' : (71, 60, 139, 255) , -'slateblue3' : (105, 89, 205, 255) , -'peachpuff1' : (255, 218, 185, 255) , -'peachpuff2' : (238, 203, 173, 255) , -'peachpuff3' : (205, 175, 149, 255) , -'gray29' : (74, 74, 74, 255) , -'paleturquoise' : (175, 238, 238, 255) , -'darkgray' : (169, 169, 169, 255) , -'grey25' : (64, 64, 64, 255) , -'darkmagenta' : (139, 0, 139, 255) , -'palegoldenrod' : (238, 232, 170, 255) , -'grey64' : (163, 163, 163, 255) , -'grey12' : (31, 31, 31, 255) , -'deeppink3' : (205, 16, 118, 255) , -'gray79' : (201, 201, 201, 255) , -'gray83' : (212, 212, 212, 255) , -'deeppink2' : (238, 18, 137, 255) , -'burlywood4' : (139, 115, 85, 255) , -'palevioletred4' : (139, 71, 93, 255) , -'deeppink1' : (255, 20, 147, 255) , -'slateblue2' : (122, 103, 238, 255) , -'grey46' : (117, 117, 117, 255) , -'royalblue4' : (39, 64, 139, 255) , -'yellowgreen' : (154, 205, 50, 255) , -'royalblue1' : (72, 118, 255, 255) , -'slateblue1' : (131, 111, 255, 255) , -'lightgoldenrod3' : (205, 190, 112, 255) , -'lightgoldenrod2' : (238, 220, 130, 255) , -'navy' : (0, 0, 128, 255) , -'orchid' : (218, 112, 214, 255) , -'ghostwhite' : (248, 248, 255, 255) , -'purple' : (160, 32, 240, 255) , -'darkkhaki' : (189, 183, 107, 255) , -'grey45' : (115, 115, 115, 255) , -'gray94' : (240, 240, 240, 255) , -'wheat4' : (139, 126, 102, 255) , -'gray96' : (245, 245, 245, 255) , -'gray97' : (247, 247, 247, 255) , -'wheat1' : (255, 231, 186, 255) , -'gray91' : (232, 232, 232, 255) , -'wheat3' : (205, 186, 150, 255) , -'wheat2' : (238, 216, 174, 255) , -'indianred4' : (139, 58, 58, 255) , -'coral2' : (238, 106, 80, 255) , -'coral1' : (255, 114, 86, 255) , -'violetred' : (208, 32, 144, 255) , -'rosybrown3' : (205, 155, 155, 255) , -'deepskyblue2' : (0, 178, 238, 255) , -'deepskyblue1' : (0, 191, 255, 255) , -'bisque' : (255, 228, 196, 255) , -'grey49' : (125, 125, 125, 255) , -'khaki' : (240, 230, 140, 255) , -'wheat' : (245, 222, 179, 255) , -'lightslateblue' : (132, 112, 255, 255) , -'mediumpurple3' : (137, 104, 205, 255) , -'gray55' : (140, 140, 140, 255) , -'deepskyblue' : (0, 191, 255, 255) , -'gray98' : (250, 250, 250, 255) , -'steelblue' : (70, 130, 180, 255) , -'aliceblue' : (240, 248, 255, 255) , -'lightskyblue2' : (164, 211, 238, 255) , -'lightskyblue3' : (141, 182, 205, 255) , -'lightslategrey' : (119, 136, 153, 255) , -'blue3' : (0, 0, 205, 255) , -'blue2' : (0, 0, 238, 255) , -'gainsboro' : (220, 220, 220, 255) , -'grey76' : (194, 194, 194, 255) , -'purple3' : (125, 38, 205, 255) , -'plum4' : (139, 102, 139, 255) , -'gray56' : (143, 143, 143, 255) , -'plum3' : (205, 150, 205, 255) , -'plum' : (221, 160, 221, 255) , -'lightgrey' : (211, 211, 211, 255) , -'mediumslateblue' : (123, 104, 238, 255) , -'mistyrose' : (255, 228, 225, 255) , -'lightcyan1' : (224, 255, 255, 255) , -'grey71' : (181, 181, 181, 255) , -'darksalmon' : (233, 150, 122, 255) , -'beige' : (245, 245, 220, 255) , -'grey24' : (61, 61, 61, 255) , -'azure' : (240, 255, 255, 255) , -'honeydew1' : (240, 255, 240, 255) , -'slategray2' : (185, 211, 238, 255) , -'dodgerblue3' : (24, 116, 205, 255) , -'slategray4' : (108, 123, 139, 255) , -'grey27' : (69, 69, 69, 255) , -'lightcyan3' : (180, 205, 205, 255) , -'cornsilk' : (255, 248, 220, 255) , -'tomato1' : (255, 99, 71, 255) , -'gray57' : (145, 145, 145, 255) , -'mediumvioletred' : (199, 21, 133, 255) , -'tomato2' : (238, 92, 66, 255) , -'snow4' : (139, 137, 137, 255) , -'grey75' : (191, 191, 191, 255) , -'snow2' : (238, 233, 233, 255) , -'snow3' : (205, 201, 201, 255) , -'snow1' : (255, 250, 250, 255) , -'grey23' : (59, 59, 59, 255) , -'cornsilk3' : (205, 200, 177, 255) , -'lightcoral' : (240, 128, 128, 255) , -'orangered' : (255, 69, 0, 255) , -'navajowhite' : (255, 222, 173, 255) , -'mediumpurple2' : (159, 121, 238, 255) , -'slategray' : (112, 128, 144, 255) , -'pink2' : (238, 169, 184, 255) , -'grey29' : (74, 74, 74, 255) , -'grey28' : (71, 71, 71, 255) , -'gray82' : (209, 209, 209, 255) , -'burlywood' : (222, 184, 135, 255) , -'mediumpurple4' : (93, 71, 139, 255) , -'mediumspringgreen' : (0, 250, 154, 255) , -'grey26' : (66, 66, 66, 255) , -'grey21' : (54, 54, 54, 255) , -'grey20' : (51, 51, 51, 255) , -'blanchedalmond' : (255, 235, 205, 255) , -'pink4' : (139, 99, 108, 255) , -'gray78' : (199, 199, 199, 255) , -'tan3' : (205, 133, 63, 255) , -'gray76' : (194, 194, 194, 255) , -'gray77' : (196, 196, 196, 255) , -'white' : (255, 255, 255, 255) , -'gray75' : (191, 191, 191, 255) , -'gray72' : (184, 184, 184, 255) , -'gray73' : (186, 186, 186, 255) , -'gray70' : (179, 179, 179, 255) , -'gray71' : (181, 181, 181, 255) , -'lightgray' : (211, 211, 211, 255) , -'ivory' : (255, 255, 240, 255) , -'gray46' : (117, 117, 117, 255) , -'gray74' : (189, 189, 189, 255) , -'lightyellow3' : (205, 205, 180, 255) , -'lightpink2' : (238, 162, 173, 255) , -'lightpink3' : (205, 140, 149, 255) , -'paleturquoise4' : (102, 139, 139, 255) , -'lightpink4' : (139, 95, 101, 255) , -'paleturquoise3' : (150, 205, 205, 255) , -'seagreen4' : (46, 139, 87, 255) , -'seagreen3' : (67, 205, 128, 255) , -'seagreen2' : (78, 238, 148, 255) , -'seagreen1' : (84, 255, 159, 255) , -'paleturquoise2' : (174, 238, 238, 255) , -'gray52' : (133, 133, 133, 255) , -'cornsilk4' : (139, 136, 120, 255) , -'cornsilk2' : (238, 232, 205, 255) , -'darkolivegreen3' : (162, 205, 90, 255) , -'cornsilk1' : (255, 248, 220, 255) , -'limegreen' : (50, 205, 50, 255) , -'darkolivegreen2' : (188, 238, 104, 255) , -'grey' : (190, 190, 190, 255) , -'violetred2' : (238, 58, 140, 255) , -'salmon1' : (255, 140, 105, 255) , -'grey92' : (235, 235, 235, 255) , -'grey93' : (237, 237, 237, 255) , -'grey94' : (240, 240, 240, 255) , -'grey95' : (242, 242, 242, 255) , -'grey96' : (245, 245, 245, 255) , -'grey83' : (212, 212, 212, 255) , -'grey98' : (250, 250, 250, 255) , -'lightgoldenrod1' : (255, 236, 139, 255) , -'palegreen1' : (154, 255, 154, 255) , -'red3' : (205, 0, 0, 255) , -'palegreen3' : (124, 205, 124, 255) , -'palegreen2' : (144, 238, 144, 255) , -'palegreen4' : (84, 139, 84, 255) , -'cadetblue' : (95, 158, 160, 255) , -'violet' : (238, 130, 238, 255) , -'mistyrose2' : (238, 213, 210, 255) , -'slateblue' : (106, 90, 205, 255) , -'grey43' : (110, 110, 110, 255) , -'grey90' : (229, 229, 229, 255) , -'gray35' : (89, 89, 89, 255) , -'turquoise3' : (0, 197, 205, 255) , -'turquoise2' : (0, 229, 238, 255) , -'burlywood3' : (205, 170, 125, 255) , -'burlywood2' : (238, 197, 145, 255) , -'lightcyan4' : (122, 139, 139, 255) , -'rosybrown' : (188, 143, 143, 255) , -'turquoise4' : (0, 134, 139, 255) , -'whitesmoke' : (245, 245, 245, 255) , -'lightblue' : (173, 216, 230, 255) , -'grey40' : (102, 102, 102, 255) , -'gray40' : (102, 102, 102, 255) , -'honeydew3' : (193, 205, 193, 255) , -'dimgray' : (105, 105, 105, 255) , -'grey47' : (120, 120, 120, 255) , -'seagreen' : (46, 139, 87, 255) , -'red4' : (139, 0, 0, 255) , -'grey14' : (36, 36, 36, 255) , -'snow' : (255, 250, 250, 255) , -'darkorchid1' : (191, 62, 255, 255) , -'gray58' : (148, 148, 148, 255) , -'gray59' : (150, 150, 150, 255) , -'cadetblue4' : (83, 134, 139, 255) , -'cadetblue3' : (122, 197, 205, 255) , -'cadetblue2' : (142, 229, 238, 255) , -'cadetblue1' : (152, 245, 255, 255) , -'olivedrab4' : (105, 139, 34, 255) , -'purple4' : (85, 26, 139, 255) , -'gray20' : (51, 51, 51, 255) , -'grey44' : (112, 112, 112, 255) , -'purple1' : (155, 48, 255, 255) , -'olivedrab1' : (192, 255, 62, 255) , -'olivedrab2' : (179, 238, 58, 255) , -'olivedrab3' : (154, 205, 50, 255) , -'orangered3' : (205, 55, 0, 255) , -'orangered2' : (238, 64, 0, 255) , -'orangered1' : (255, 69, 0, 255) , -'darkorchid' : (153, 50, 204, 255) , -'thistle3' : (205, 181, 205, 255) , -'thistle2' : (238, 210, 238, 255) , -'thistle1' : (255, 225, 255, 255) , -'salmon' : (250, 128, 114, 255) , -'gray93' : (237, 237, 237, 255) , -'thistle4' : (139, 123, 139, 255) , -'gray39' : (99, 99, 99, 255) , -'lawngreen' : (124, 252, 0, 255) , -'hotpink3' : (205, 96, 144, 255) , -'hotpink2' : (238, 106, 167, 255) , -'hotpink1' : (255, 110, 180, 255) , -'lightgreen' : (144, 238, 144, 255) , -'hotpink4' : (139, 58, 98, 255) , -'darkseagreen4' : (105, 139, 105, 255) , -'darkseagreen3' : (155, 205, 155, 255) , -'darkseagreen2' : (180, 238, 180, 255) , -'darkseagreen1' : (193, 255, 193, 255) , -'deepskyblue4' : (0, 104, 139, 255) , -'gray44' : (112, 112, 112, 255) , -'navyblue' : (0, 0, 128, 255) , -'darkblue' : (0, 0, 139, 255) , -'forestgreen' : (34, 139, 34, 255) , -'gray53' : (135, 135, 135, 255) , -'grey100' : (255, 255, 255, 255) , -'brown1' : (255, 64, 64, 255) , + "aliceblue": (240, 248, 255, 255), + "antiquewhite": (250, 235, 215, 255), + "antiquewhite1": (255, 239, 219, 255), + "antiquewhite2": (238, 223, 204, 255), + "antiquewhite3": (205, 192, 176, 255), + "antiquewhite4": (139, 131, 120, 255), + "aqua": (0, 255, 255, 255), + "aquamarine": (127, 255, 212, 255), + "aquamarine1": (127, 255, 212, 255), + "aquamarine2": (118, 238, 198, 255), + "aquamarine3": (102, 205, 170, 255), + "aquamarine4": (69, 139, 116, 255), + "azure": (240, 255, 255, 255), + "azure1": (240, 255, 255, 255), + "azure3": (193, 205, 205, 255), + "azure2": (224, 238, 238, 255), + "azure4": (131, 139, 139, 255), + "beige": (245, 245, 220, 255), + "bisque": (255, 228, 196, 255), + "bisque1": (255, 228, 196, 255), + "bisque2": (238, 213, 183, 255), + "bisque3": (205, 183, 158, 255), + "bisque4": (139, 125, 107, 255), + "black": (0, 0, 0, 255), + "blanchedalmond": (255, 235, 205, 255), + "blue": (0, 0, 255, 255), + "blue1": (0, 0, 255, 255), + "blue2": (0, 0, 238, 255), + "blue3": (0, 0, 205, 255), + "blue4": (0, 0, 139, 255), + "blueviolet": (138, 43, 226, 255), + "brown": (165, 42, 42, 255), + "brown1": (255, 64, 64, 255), + "brown2": (238, 59, 59, 255), + "brown3": (205, 51, 51, 255), + "brown4": (139, 35, 35, 255), + "burlywood": (222, 184, 135, 255), + "burlywood1": (255, 211, 155, 255), + "burlywood2": (238, 197, 145, 255), + "burlywood3": (205, 170, 125, 255), + "burlywood4": (139, 115, 85, 255), + "cadetblue": (95, 158, 160, 255), + "cadetblue1": (152, 245, 255, 255), + "cadetblue2": (142, 229, 238, 255), + "cadetblue3": (122, 197, 205, 255), + "cadetblue4": (83, 134, 139, 255), + "chartreuse": (127, 255, 0, 255), + "chartreuse1": (127, 255, 0, 255), + "chartreuse2": (118, 238, 0, 255), + "chartreuse3": (102, 205, 0, 255), + "chartreuse4": (69, 139, 0, 255), + "chocolate": (210, 105, 30, 255), + "chocolate1": (255, 127, 36, 255), + "chocolate2": (238, 118, 33, 255), + "chocolate3": (205, 102, 29, 255), + "chocolate4": (139, 69, 19, 255), + "coral": (255, 127, 80, 255), + "coral1": (255, 114, 86, 255), + "coral2": (238, 106, 80, 255), + "coral3": (205, 91, 69, 255), + "coral4": (139, 62, 47, 255), + "cornflowerblue": (100, 149, 237, 255), + "cornsilk": (255, 248, 220, 255), + "cornsilk1": (255, 248, 220, 255), + "cornsilk2": (238, 232, 205, 255), + "cornsilk3": (205, 200, 177, 255), + "cornsilk4": (139, 136, 120, 255), + "crimson": (220, 20, 60, 255), + "cyan": (0, 255, 255, 255), + "cyan1": (0, 255, 255, 255), + "cyan2": (0, 238, 238, 255), + "cyan3": (0, 205, 205, 255), + "cyan4": (0, 139, 139, 255), + "darkblue": (0, 0, 139, 255), + "darkcyan": (0, 139, 139, 255), + "darkgoldenrod": (184, 134, 11, 255), + "darkgoldenrod1": (255, 185, 15, 255), + "darkgoldenrod2": (238, 173, 14, 255), + "darkgoldenrod3": (205, 149, 12, 255), + "darkgoldenrod4": (139, 101, 8, 255), + "darkgray": (169, 169, 169, 255), + "darkgreen": (0, 100, 0, 255), + "darkgrey": (169, 169, 169, 255), + "darkkhaki": (189, 183, 107, 255), + "darkmagenta": (139, 0, 139, 255), + "darkolivegreen": (85, 107, 47, 255), + "darkolivegreen1": (202, 255, 112, 255), + "darkolivegreen2": (188, 238, 104, 255), + "darkolivegreen3": (162, 205, 90, 255), + "darkolivegreen4": (110, 139, 61, 255), + "darkorange": (255, 140, 0, 255), + "darkorange1": (255, 127, 0, 255), + "darkorange2": (238, 118, 0, 255), + "darkorange3": (205, 102, 0, 255), + "darkorange4": (139, 69, 0, 255), + "darkorchid": (153, 50, 204, 255), + "darkorchid1": (191, 62, 255, 255), + "darkorchid2": (178, 58, 238, 255), + "darkorchid3": (154, 50, 205, 255), + "darkorchid4": (104, 34, 139, 255), + "darkred": (139, 0, 0, 255), + "darksalmon": (233, 150, 122, 255), + "darkseagreen": (143, 188, 143, 255), + "darkseagreen1": (193, 255, 193, 255), + "darkseagreen2": (180, 238, 180, 255), + "darkseagreen3": (155, 205, 155, 255), + "darkseagreen4": (105, 139, 105, 255), + "darkslateblue": (72, 61, 139, 255), + "darkslategray": (47, 79, 79, 255), + "darkslategray1": (151, 255, 255, 255), + "darkslategray2": (141, 238, 238, 255), + "darkslategray3": (121, 205, 205, 255), + "darkslategray4": (82, 139, 139, 255), + "darkslategrey": (47, 79, 79, 255), + "darkturquoise": (0, 206, 209, 255), + "darkviolet": (148, 0, 211, 255), + "deeppink": (255, 20, 147, 255), + "deeppink1": (255, 20, 147, 255), + "deeppink2": (238, 18, 137, 255), + "deeppink3": (205, 16, 118, 255), + "deeppink4": (139, 10, 80, 255), + "deepskyblue": (0, 191, 255, 255), + "deepskyblue1": (0, 191, 255, 255), + "deepskyblue2": (0, 178, 238, 255), + "deepskyblue3": (0, 154, 205, 255), + "deepskyblue4": (0, 104, 139, 255), + "dimgray": (105, 105, 105, 255), + "dimgrey": (105, 105, 105, 255), + "dodgerblue": (30, 144, 255, 255), + "dodgerblue1": (30, 144, 255, 255), + "dodgerblue2": (28, 134, 238, 255), + "dodgerblue3": (24, 116, 205, 255), + "dodgerblue4": (16, 78, 139, 255), + "firebrick": (178, 34, 34, 255), + "firebrick1": (255, 48, 48, 255), + "firebrick2": (238, 44, 44, 255), + "firebrick3": (205, 38, 38, 255), + "firebrick4": (139, 26, 26, 255), + "floralwhite": (255, 250, 240, 255), + "forestgreen": (34, 139, 34, 255), + "fuchsia": (255, 0, 255, 255), + "gainsboro": (220, 220, 220, 255), + "ghostwhite": (248, 248, 255, 255), + "gold": (255, 215, 0, 255), + "gold1": (255, 215, 0, 255), + "gold2": (238, 201, 0, 255), + "gold3": (205, 173, 0, 255), + "gold4": (139, 117, 0, 255), + "goldenrod": (218, 165, 32, 255), + "goldenrod1": (255, 193, 37, 255), + "goldenrod2": (238, 180, 34, 255), + "goldenrod3": (205, 155, 29, 255), + "goldenrod4": (139, 105, 20, 255), + "gray": (190, 190, 190, 255), + "gray0": (0, 0, 0, 255), + "gray1": (3, 3, 3, 255), + "gray2": (5, 5, 5, 255), + "gray3": (8, 8, 8, 255), + "gray4": (10, 10, 10, 255), + "gray5": (13, 13, 13, 255), + "gray6": (15, 15, 15, 255), + "gray7": (18, 18, 18, 255), + "gray8": (20, 20, 20, 255), + "gray9": (23, 23, 23, 255), + "gray10": (26, 26, 26, 255), + "gray11": (28, 28, 28, 255), + "gray12": (31, 31, 31, 255), + "gray13": (33, 33, 33, 255), + "gray14": (36, 36, 36, 255), + "gray15": (38, 38, 38, 255), + "gray16": (41, 41, 41, 255), + "gray17": (43, 43, 43, 255), + "gray18": (46, 46, 46, 255), + "gray19": (48, 48, 48, 255), + "gray20": (51, 51, 51, 255), + "gray21": (54, 54, 54, 255), + "gray22": (56, 56, 56, 255), + "gray23": (59, 59, 59, 255), + "gray24": (61, 61, 61, 255), + "gray25": (64, 64, 64, 255), + "gray26": (66, 66, 66, 255), + "gray27": (69, 69, 69, 255), + "gray28": (71, 71, 71, 255), + "gray29": (74, 74, 74, 255), + "gray30": (77, 77, 77, 255), + "gray31": (79, 79, 79, 255), + "gray32": (82, 82, 82, 255), + "gray33": (84, 84, 84, 255), + "gray34": (87, 87, 87, 255), + "gray35": (89, 89, 89, 255), + "gray36": (92, 92, 92, 255), + "gray37": (94, 94, 94, 255), + "gray38": (97, 97, 97, 255), + "gray39": (99, 99, 99, 255), + "gray40": (102, 102, 102, 255), + "gray41": (105, 105, 105, 255), + "gray42": (107, 107, 107, 255), + "gray43": (110, 110, 110, 255), + "gray44": (112, 112, 112, 255), + "gray45": (115, 115, 115, 255), + "gray46": (117, 117, 117, 255), + "gray47": (120, 120, 120, 255), + "gray48": (122, 122, 122, 255), + "gray49": (125, 125, 125, 255), + "gray50": (127, 127, 127, 255), + "gray51": (130, 130, 130, 255), + "gray52": (133, 133, 133, 255), + "gray53": (135, 135, 135, 255), + "gray54": (138, 138, 138, 255), + "gray55": (140, 140, 140, 255), + "gray56": (143, 143, 143, 255), + "gray57": (145, 145, 145, 255), + "gray58": (148, 148, 148, 255), + "gray59": (150, 150, 150, 255), + "gray60": (153, 153, 153, 255), + "gray61": (156, 156, 156, 255), + "gray62": (158, 158, 158, 255), + "gray63": (161, 161, 161, 255), + "gray64": (163, 163, 163, 255), + "gray65": (166, 166, 166, 255), + "gray66": (168, 168, 168, 255), + "gray67": (171, 171, 171, 255), + "gray68": (173, 173, 173, 255), + "gray69": (176, 176, 176, 255), + "gray70": (179, 179, 179, 255), + "gray71": (181, 181, 181, 255), + "gray72": (184, 184, 184, 255), + "gray73": (186, 186, 186, 255), + "gray74": (189, 189, 189, 255), + "gray75": (191, 191, 191, 255), + "gray76": (194, 194, 194, 255), + "gray77": (196, 196, 196, 255), + "gray78": (199, 199, 199, 255), + "gray79": (201, 201, 201, 255), + "gray80": (204, 204, 204, 255), + "gray81": (207, 207, 207, 255), + "gray82": (209, 209, 209, 255), + "gray83": (212, 212, 212, 255), + "gray84": (214, 214, 214, 255), + "gray85": (217, 217, 217, 255), + "gray86": (219, 219, 219, 255), + "gray87": (222, 222, 222, 255), + "gray88": (224, 224, 224, 255), + "gray89": (227, 227, 227, 255), + "gray90": (229, 229, 229, 255), + "gray91": (232, 232, 232, 255), + "gray92": (235, 235, 235, 255), + "gray93": (237, 237, 237, 255), + "gray94": (240, 240, 240, 255), + "gray95": (242, 242, 242, 255), + "gray96": (245, 245, 245, 255), + "gray97": (247, 247, 247, 255), + "gray98": (250, 250, 250, 255), + "gray99": (252, 252, 252, 255), + "gray100": (255, 255, 255, 255), + "green": (0, 255, 0, 255), + "green1": (0, 255, 0, 255), + "green2": (0, 238, 0, 255), + "green3": (0, 205, 0, 255), + "green4": (0, 139, 0, 255), + "greenyellow": (173, 255, 47, 255), + "grey": (190, 190, 190, 255), + "grey0": (0, 0, 0, 255), + "grey1": (3, 3, 3, 255), + "grey2": (5, 5, 5, 255), + "grey3": (8, 8, 8, 255), + "grey4": (10, 10, 10, 255), + "grey5": (13, 13, 13, 255), + "grey6": (15, 15, 15, 255), + "grey7": (18, 18, 18, 255), + "grey8": (20, 20, 20, 255), + "grey9": (23, 23, 23, 255), + "grey10": (26, 26, 26, 255), + "grey11": (28, 28, 28, 255), + "grey12": (31, 31, 31, 255), + "grey13": (33, 33, 33, 255), + "grey14": (36, 36, 36, 255), + "grey15": (38, 38, 38, 255), + "grey16": (41, 41, 41, 255), + "grey17": (43, 43, 43, 255), + "grey18": (46, 46, 46, 255), + "grey19": (48, 48, 48, 255), + "grey20": (51, 51, 51, 255), + "grey21": (54, 54, 54, 255), + "grey22": (56, 56, 56, 255), + "grey23": (59, 59, 59, 255), + "grey24": (61, 61, 61, 255), + "grey25": (64, 64, 64, 255), + "grey26": (66, 66, 66, 255), + "grey27": (69, 69, 69, 255), + "grey28": (71, 71, 71, 255), + "grey29": (74, 74, 74, 255), + "grey30": (77, 77, 77, 255), + "grey31": (79, 79, 79, 255), + "grey32": (82, 82, 82, 255), + "grey33": (84, 84, 84, 255), + "grey34": (87, 87, 87, 255), + "grey35": (89, 89, 89, 255), + "grey36": (92, 92, 92, 255), + "grey37": (94, 94, 94, 255), + "grey38": (97, 97, 97, 255), + "grey39": (99, 99, 99, 255), + "grey40": (102, 102, 102, 255), + "grey41": (105, 105, 105, 255), + "grey42": (107, 107, 107, 255), + "grey43": (110, 110, 110, 255), + "grey44": (112, 112, 112, 255), + "grey45": (115, 115, 115, 255), + "grey46": (117, 117, 117, 255), + "grey47": (120, 120, 120, 255), + "grey48": (122, 122, 122, 255), + "grey49": (125, 125, 125, 255), + "grey50": (127, 127, 127, 255), + "grey51": (130, 130, 130, 255), + "grey52": (133, 133, 133, 255), + "grey53": (135, 135, 135, 255), + "grey54": (138, 138, 138, 255), + "grey55": (140, 140, 140, 255), + "grey56": (143, 143, 143, 255), + "grey57": (145, 145, 145, 255), + "grey58": (148, 148, 148, 255), + "grey59": (150, 150, 150, 255), + "grey60": (153, 153, 153, 255), + "grey61": (156, 156, 156, 255), + "grey62": (158, 158, 158, 255), + "grey63": (161, 161, 161, 255), + "grey64": (163, 163, 163, 255), + "grey65": (166, 166, 166, 255), + "grey66": (168, 168, 168, 255), + "grey67": (171, 171, 171, 255), + "grey68": (173, 173, 173, 255), + "grey69": (176, 176, 176, 255), + "grey70": (179, 179, 179, 255), + "grey71": (181, 181, 181, 255), + "grey72": (184, 184, 184, 255), + "grey73": (186, 186, 186, 255), + "grey74": (189, 189, 189, 255), + "grey75": (191, 191, 191, 255), + "grey76": (194, 194, 194, 255), + "grey77": (196, 196, 196, 255), + "grey78": (199, 199, 199, 255), + "grey79": (201, 201, 201, 255), + "grey80": (204, 204, 204, 255), + "grey81": (207, 207, 207, 255), + "grey82": (209, 209, 209, 255), + "grey83": (212, 212, 212, 255), + "grey84": (214, 214, 214, 255), + "grey85": (217, 217, 217, 255), + "grey86": (219, 219, 219, 255), + "grey87": (222, 222, 222, 255), + "grey88": (224, 224, 224, 255), + "grey89": (227, 227, 227, 255), + "grey90": (229, 229, 229, 255), + "grey91": (232, 232, 232, 255), + "grey92": (235, 235, 235, 255), + "grey93": (237, 237, 237, 255), + "grey94": (240, 240, 240, 255), + "grey95": (242, 242, 242, 255), + "grey96": (245, 245, 245, 255), + "grey97": (247, 247, 247, 255), + "grey98": (250, 250, 250, 255), + "grey99": (252, 252, 252, 255), + "grey100": (255, 255, 255, 255), + "honeydew": (240, 255, 240, 255), + "honeydew1": (240, 255, 240, 255), + "honeydew2": (224, 238, 224, 255), + "honeydew3": (193, 205, 193, 255), + "honeydew4": (131, 139, 131, 255), + "hotpink": (255, 105, 180, 255), + "hotpink1": (255, 110, 180, 255), + "hotpink2": (238, 106, 167, 255), + "hotpink3": (205, 96, 144, 255), + "hotpink4": (139, 58, 98, 255), + "indianred": (205, 92, 92, 255), + "indianred1": (255, 106, 106, 255), + "indianred2": (238, 99, 99, 255), + "indianred3": (205, 85, 85, 255), + "indianred4": (139, 58, 58, 255), + "indigo": (75, 0, 130, 255), + "ivory": (255, 255, 240, 255), + "ivory1": (255, 255, 240, 255), + "ivory2": (238, 238, 224, 255), + "ivory3": (205, 205, 193, 255), + "ivory4": (139, 139, 131, 255), + "khaki": (240, 230, 140, 255), + "khaki1": (255, 246, 143, 255), + "khaki2": (238, 230, 133, 255), + "khaki3": (205, 198, 115, 255), + "khaki4": (139, 134, 78, 255), + "lavender": (230, 230, 250, 255), + "lavenderblush": (255, 240, 245, 255), + "lavenderblush1": (255, 240, 245, 255), + "lavenderblush2": (238, 224, 229, 255), + "lavenderblush3": (205, 193, 197, 255), + "lavenderblush4": (139, 131, 134, 255), + "lawngreen": (124, 252, 0, 255), + "lemonchiffon": (255, 250, 205, 255), + "lemonchiffon1": (255, 250, 205, 255), + "lemonchiffon2": (238, 233, 191, 255), + "lemonchiffon3": (205, 201, 165, 255), + "lemonchiffon4": (139, 137, 112, 255), + "lightblue": (173, 216, 230, 255), + "lightblue1": (191, 239, 255, 255), + "lightblue2": (178, 223, 238, 255), + "lightblue3": (154, 192, 205, 255), + "lightblue4": (104, 131, 139, 255), + "lightcoral": (240, 128, 128, 255), + "lightcyan": (224, 255, 255, 255), + "lightcyan1": (224, 255, 255, 255), + "lightcyan2": (209, 238, 238, 255), + "lightcyan3": (180, 205, 205, 255), + "lightcyan4": (122, 139, 139, 255), + "lightgoldenrod": (238, 221, 130, 255), + "lightgoldenrod1": (255, 236, 139, 255), + "lightgoldenrod2": (238, 220, 130, 255), + "lightgoldenrod3": (205, 190, 112, 255), + "lightgoldenrod4": (139, 129, 76, 255), + "lightgoldenrodyellow": (250, 250, 210, 255), + "lightgray": (211, 211, 211, 255), + "lightgreen": (144, 238, 144, 255), + "lightgrey": (211, 211, 211, 255), + "lightpink": (255, 182, 193, 255), + "lightpink1": (255, 174, 185, 255), + "lightpink2": (238, 162, 173, 255), + "lightpink3": (205, 140, 149, 255), + "lightpink4": (139, 95, 101, 255), + "lightsalmon": (255, 160, 122, 255), + "lightsalmon1": (255, 160, 122, 255), + "lightsalmon2": (238, 149, 114, 255), + "lightsalmon3": (205, 129, 98, 255), + "lightsalmon4": (139, 87, 66, 255), + "lightseagreen": (32, 178, 170, 255), + "lightskyblue": (135, 206, 250, 255), + "lightskyblue1": (176, 226, 255, 255), + "lightskyblue2": (164, 211, 238, 255), + "lightskyblue3": (141, 182, 205, 255), + "lightskyblue4": (96, 123, 139, 255), + "lightslateblue": (132, 112, 255, 255), + "lightslategray": (119, 136, 153, 255), + "lightslategrey": (119, 136, 153, 255), + "lightsteelblue": (176, 196, 222, 255), + "lightsteelblue1": (202, 225, 255, 255), + "lightsteelblue2": (188, 210, 238, 255), + "lightsteelblue3": (162, 181, 205, 255), + "lightsteelblue4": (110, 123, 139, 255), + "lightyellow": (255, 255, 224, 255), + "lightyellow1": (255, 255, 224, 255), + "lightyellow2": (238, 238, 209, 255), + "lightyellow3": (205, 205, 180, 255), + "lightyellow4": (139, 139, 122, 255), + "linen": (250, 240, 230, 255), + "lime": (0, 255, 0, 255), + "limegreen": (50, 205, 50, 255), + "magenta": (255, 0, 255, 255), + "magenta1": (255, 0, 255, 255), + "magenta2": (238, 0, 238, 255), + "magenta3": (205, 0, 205, 255), + "magenta4": (139, 0, 139, 255), + "maroon": (176, 48, 96, 255), + "maroon1": (255, 52, 179, 255), + "maroon2": (238, 48, 167, 255), + "maroon3": (205, 41, 144, 255), + "maroon4": (139, 28, 98, 255), + "mediumaquamarine": (102, 205, 170, 255), + "mediumblue": (0, 0, 205, 255), + "mediumorchid": (186, 85, 211, 255), + "mediumorchid1": (224, 102, 255, 255), + "mediumorchid2": (209, 95, 238, 255), + "mediumorchid3": (180, 82, 205, 255), + "mediumorchid4": (122, 55, 139, 255), + "mediumpurple": (147, 112, 219, 255), + "mediumpurple1": (171, 130, 255, 255), + "mediumpurple2": (159, 121, 238, 255), + "mediumpurple3": (137, 104, 205, 255), + "mediumpurple4": (93, 71, 139, 255), + "mediumseagreen": (60, 179, 113, 255), + "mediumslateblue": (123, 104, 238, 255), + "mediumspringgreen": (0, 250, 154, 255), + "mediumturquoise": (72, 209, 204, 255), + "mediumvioletred": (199, 21, 133, 255), + "midnightblue": (25, 25, 112, 255), + "mintcream": (245, 255, 250, 255), + "mistyrose": (255, 228, 225, 255), + "mistyrose1": (255, 228, 225, 255), + "mistyrose2": (238, 213, 210, 255), + "mistyrose3": (205, 183, 181, 255), + "mistyrose4": (139, 125, 123, 255), + "moccasin": (255, 228, 181, 255), + "navajowhite": (255, 222, 173, 255), + "navajowhite1": (255, 222, 173, 255), + "navajowhite2": (238, 207, 161, 255), + "navajowhite3": (205, 179, 139, 255), + "navajowhite4": (139, 121, 94, 255), + "navy": (0, 0, 128, 255), + "navyblue": (0, 0, 128, 255), + "oldlace": (253, 245, 230, 255), + "olive": (128, 128, 0, 255), + "olivedrab": (107, 142, 35, 255), + "olivedrab1": (192, 255, 62, 255), + "olivedrab2": (179, 238, 58, 255), + "olivedrab3": (154, 205, 50, 255), + "olivedrab4": (105, 139, 34, 255), + "orange": (255, 165, 0, 255), + "orange1": (255, 165, 0, 255), + "orange2": (238, 154, 0, 255), + "orange3": (205, 133, 0, 255), + "orange4": (139, 90, 0, 255), + "orangered": (255, 69, 0, 255), + "orangered1": (255, 69, 0, 255), + "orangered2": (238, 64, 0, 255), + "orangered3": (205, 55, 0, 255), + "orangered4": (139, 37, 0, 255), + "orchid": (218, 112, 214, 255), + "orchid1": (255, 131, 250, 255), + "orchid2": (238, 122, 233, 255), + "orchid3": (205, 105, 201, 255), + "orchid4": (139, 71, 137, 255), + "palegreen": (152, 251, 152, 255), + "palegreen1": (154, 255, 154, 255), + "palegreen2": (144, 238, 144, 255), + "palegreen3": (124, 205, 124, 255), + "palegreen4": (84, 139, 84, 255), + "palegoldenrod": (238, 232, 170, 255), + "paleturquoise": (175, 238, 238, 255), + "paleturquoise1": (187, 255, 255, 255), + "paleturquoise2": (174, 238, 238, 255), + "paleturquoise3": (150, 205, 205, 255), + "paleturquoise4": (102, 139, 139, 255), + "palevioletred": (219, 112, 147, 255), + "palevioletred1": (255, 130, 171, 255), + "palevioletred2": (238, 121, 159, 255), + "palevioletred3": (205, 104, 137, 255), + "palevioletred4": (139, 71, 93, 255), + "papayawhip": (255, 239, 213, 255), + "peachpuff": (255, 218, 185, 255), + "peachpuff1": (255, 218, 185, 255), + "peachpuff2": (238, 203, 173, 255), + "peachpuff3": (205, 175, 149, 255), + "peachpuff4": (139, 119, 101, 255), + "peru": (205, 133, 63, 255), + "pink": (255, 192, 203, 255), + "pink1": (255, 181, 197, 255), + "pink2": (238, 169, 184, 255), + "pink3": (205, 145, 158, 255), + "pink4": (139, 99, 108, 255), + "plum": (221, 160, 221, 255), + "plum1": (255, 187, 255, 255), + "plum2": (238, 174, 238, 255), + "plum3": (205, 150, 205, 255), + "plum4": (139, 102, 139, 255), + "powderblue": (176, 224, 230, 255), + "purple": (160, 32, 240, 255), + "purple1": (155, 48, 255, 255), + "purple2": (145, 44, 238, 255), + "purple3": (125, 38, 205, 255), + "purple4": (85, 26, 139, 255), + "red": (255, 0, 0, 255), + "red1": (255, 0, 0, 255), + "red2": (238, 0, 0, 255), + "red3": (205, 0, 0, 255), + "red4": (139, 0, 0, 255), + "rosybrown": (188, 143, 143, 255), + "rosybrown1": (255, 193, 193, 255), + "rosybrown2": (238, 180, 180, 255), + "rosybrown3": (205, 155, 155, 255), + "rosybrown4": (139, 105, 105, 255), + "royalblue": (65, 105, 225, 255), + "royalblue1": (72, 118, 255, 255), + "royalblue2": (67, 110, 238, 255), + "royalblue3": (58, 95, 205, 255), + "royalblue4": (39, 64, 139, 255), + "salmon": (250, 128, 114, 255), + "salmon1": (255, 140, 105, 255), + "salmon2": (238, 130, 98, 255), + "salmon3": (205, 112, 84, 255), + "salmon4": (139, 76, 57, 255), + "saddlebrown": (139, 69, 19, 255), + "sandybrown": (244, 164, 96, 255), + "seagreen": (46, 139, 87, 255), + "seagreen1": (84, 255, 159, 255), + "seagreen2": (78, 238, 148, 255), + "seagreen3": (67, 205, 128, 255), + "seagreen4": (46, 139, 87, 255), + "seashell": (255, 245, 238, 255), + "seashell1": (255, 245, 238, 255), + "seashell2": (238, 229, 222, 255), + "seashell3": (205, 197, 191, 255), + "seashell4": (139, 134, 130, 255), + "sienna": (160, 82, 45, 255), + "sienna1": (255, 130, 71, 255), + "sienna2": (238, 121, 66, 255), + "sienna3": (205, 104, 57, 255), + "sienna4": (139, 71, 38, 255), + "silver": (192, 192, 192, 255), + "skyblue": (135, 206, 235, 255), + "skyblue1": (135, 206, 255, 255), + "skyblue2": (126, 192, 238, 255), + "skyblue3": (108, 166, 205, 255), + "skyblue4": (74, 112, 139, 255), + "slateblue": (106, 90, 205, 255), + "slateblue1": (131, 111, 255, 255), + "slateblue2": (122, 103, 238, 255), + "slateblue3": (105, 89, 205, 255), + "slateblue4": (71, 60, 139, 255), + "slategray": (112, 128, 144, 255), + "slategray1": (198, 226, 255, 255), + "slategray2": (185, 211, 238, 255), + "slategray3": (159, 182, 205, 255), + "slategray4": (108, 123, 139, 255), + "slategrey": (112, 128, 144, 255), + "snow": (255, 250, 250, 255), + "snow1": (255, 250, 250, 255), + "snow2": (238, 233, 233, 255), + "snow3": (205, 201, 201, 255), + "snow4": (139, 137, 137, 255), + "springgreen": (0, 255, 127, 255), + "springgreen1": (0, 255, 127, 255), + "springgreen2": (0, 238, 118, 255), + "springgreen3": (0, 205, 102, 255), + "springgreen4": (0, 139, 69, 255), + "steelblue": (70, 130, 180, 255), + "steelblue1": (99, 184, 255, 255), + "steelblue2": (92, 172, 238, 255), + "steelblue3": (79, 148, 205, 255), + "steelblue4": (54, 100, 139, 255), + "tan": (210, 180, 140, 255), + "tan1": (255, 165, 79, 255), + "tan2": (238, 154, 73, 255), + "tan3": (205, 133, 63, 255), + "tan4": (139, 90, 43, 255), + "teal": (0, 128, 128, 255), + "thistle": (216, 191, 216, 255), + "thistle1": (255, 225, 255, 255), + "thistle2": (238, 210, 238, 255), + "thistle3": (205, 181, 205, 255), + "thistle4": (139, 123, 139, 255), + "tomato": (255, 99, 71, 255), + "tomato1": (255, 99, 71, 255), + "tomato2": (238, 92, 66, 255), + "tomato3": (205, 79, 57, 255), + "tomato4": (139, 54, 38, 255), + "turquoise": (64, 224, 208, 255), + "turquoise1": (0, 245, 255, 255), + "turquoise2": (0, 229, 238, 255), + "turquoise3": (0, 197, 205, 255), + "turquoise4": (0, 134, 139, 255), + "violet": (238, 130, 238, 255), + "violetred": (208, 32, 144, 255), + "violetred1": (255, 62, 150, 255), + "violetred2": (238, 58, 140, 255), + "violetred3": (205, 50, 120, 255), + "violetred4": (139, 34, 82, 255), + "wheat": (245, 222, 179, 255), + "wheat1": (255, 231, 186, 255), + "wheat2": (238, 216, 174, 255), + "wheat3": (205, 186, 150, 255), + "wheat4": (139, 126, 102, 255), + "white": (255, 255, 255, 255), + "whitesmoke": (245, 245, 245, 255), + "yellow": (255, 255, 0, 255), + "yellow1": (255, 255, 0, 255), + "yellow2": (238, 238, 0, 255), + "yellow3": (205, 205, 0, 255), + "yellow4": (139, 139, 0, 255), + "yellowgreen": (154, 205, 50, 255), } - -for k,v in THECOLORS.items(): - THECOLORS[unicode_(k)] = v diff --git a/venv/Lib/site-packages/pygame/compat.py b/venv/Lib/site-packages/pygame/compat.py deleted file mode 100644 index 58e5c5478af325146234baf1c05f670acffb4880..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/compat.py +++ /dev/null @@ -1,103 +0,0 @@ -# coding: ascii -"""Python 2.x/3.x compatibility tools""" - -import sys - -__all__ = ['geterror', 'long_', 'xrange_', 'ord_', 'unichr_', - 'unicode_', 'raw_input_', 'as_bytes', 'as_unicode', - 'bytes_', 'imap_', 'PY_MAJOR_VERSION'] - -PY_MAJOR_VERSION = sys.version_info[0] - - -def geterror(): - return sys.exc_info()[1] - -# Python 3 -if PY_MAJOR_VERSION >= 3: - long_ = int - xrange_ = range - from io import StringIO - from io import BytesIO - unichr_ = chr - unicode_ = str - bytes_ = bytes - raw_input_ = input - imap_ = map - - # Represent escaped bytes and strings in a portable way. - # - # as_bytes: Allow a Python 3.x string to represent a bytes object. - # e.g.: as_bytes("a\x01\b") == b"a\x01b" # Python 3.x - # as_bytes("a\x01\b") == "a\x01b" # Python 2.x - # as_unicode: Allow a Python "r" string to represent a unicode string. - # e.g.: as_unicode(r"Bo\u00F6tes") == u"Bo\u00F6tes" # Python 2.x - # as_unicode(r"Bo\u00F6tes") == "Bo\u00F6tes" # Python 3.x - def as_bytes(string): - """ '' => b'' """ - return string.encode('latin-1', 'strict') - - def as_unicode(rstring): - """ r'' => '' """ - return rstring.encode('ascii', 'strict').decode('unicode_escape', - 'strict') - -# Python 2 -else: - long_ = long - xrange_ = xrange - from cStringIO import StringIO - BytesIO = StringIO - unichr_ = unichr - unicode_ = unicode - bytes_ = str - raw_input_ = raw_input - from itertools import imap as imap_ - - # Represent escaped bytes and strings in a portable way. - # - # as_bytes: Allow a Python 3.x string to represent a bytes object. - # e.g.: as_bytes("a\x01\b") == b"a\x01b" # Python 3.x - # as_bytes("a\x01\b") == "a\x01b" # Python 2.x - # as_unicode: Allow a Python "r" string to represent a unicode string. - # e.g.: as_unicode(r"Bo\u00F6tes") == u"Bo\u00F6tes" # Python 2.x - # as_unicode(r"Bo\u00F6tes") == "Bo\u00F6tes" # Python 3.x - def as_bytes(string): - """ '' => '' """ - return string - - def as_unicode(rstring): - """ r'' => u'' """ - return rstring.decode('unicode_escape', 'strict') - - -def get_BytesIO(): - return BytesIO - - -def get_StringIO(): - return StringIO - - -def ord_(o): - try: - return ord(o) - except TypeError: - return o - -if sys.platform == 'win32': - filesystem_errors = "replace" -elif PY_MAJOR_VERSION >= 3: - filesystem_errors = "surrogateescape" -else: - filesystem_errors = "strict" - - -def filesystem_encode(u): - fsencoding = sys.getfilesystemencoding() - if fsencoding.lower() in ['ascii', 'ansi_x3.4-1968'] and sys.platform.startswith('linux'): - # Don't believe Linux systems claiming ASCII-only filesystems. In - # practice, arbitrary bytes are allowed, and most things expect UTF-8. - fsencoding = 'utf-8' - return u.encode(fsencoding, filesystem_errors) - diff --git a/venv/Lib/site-packages/pygame/constants.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/constants.cp37-win_amd64.pyd deleted file mode 100644 index 17ec9a6ae592c7a855d2831a14104bd4f7a729a9..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/constants.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/cursors.py b/venv/Lib/site-packages/pygame/cursors.py index 944fc734280d84f27d55e8038969c917575e7066..0ba3ac422a07cb3290236c7fdb8e6ad7d3d93230 100644 --- a/venv/Lib/site-packages/pygame/cursors.py +++ b/venv/Lib/site-packages/pygame/cursors.py @@ -1,287 +1,821 @@ -## pygame - Python Game Library -## Copyright (C) 2000-2003 Pete Shinners -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Library General Public -## License as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Library General Public License for more details. -## -## You should have received a copy of the GNU Library General Public -## License along with this library; if not, write to the Free -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -## -## Pete Shinners -## pete@shinners.org +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org """Set of cursor resources available for use. These cursors come in a sequence of values that are needed as the arguments for -pygame.mouse.set_cursor(). to dereference the sequence in place -and create the cursor in one step, call like this; -pygame.mouse.set_cursor(*pygame.cursors.arrow). +pygame.mouse.set_cursor(). To dereference the sequence in place +and create the cursor in one step, call like this: + pygame.mouse.set_cursor(*pygame.cursors.arrow). -Here is a list of available cursors; arrow, diamond, ball, - broken_x, tri_left, tri_right +Here is a list of available cursors: + arrow, diamond, ball, broken_x, tri_left, tri_right There is also a sample string cursor named 'thickarrow_strings'. -The compile() function can convert these string cursors into cursor byte data. +The compile() function can convert these string cursors into cursor byte data that can be used to +create Cursor objects. + +Alternately, you can also create Cursor objects using surfaces or cursors constants, +such as pygame.SYSTEM_CURSOR_ARROW. """ -#default pygame black arrow -arrow = ((16, 16), (0, 0), - (0x00,0x00,0x40,0x00,0x60,0x00,0x70,0x00,0x78,0x00,0x7C,0x00,0x7E,0x00,0x7F,0x00, - 0x7F,0x80,0x7C,0x00,0x6C,0x00,0x46,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x00,0x00), - (0x40,0x00,0xE0,0x00,0xF0,0x00,0xF8,0x00,0xFC,0x00,0xFE,0x00,0xFF,0x00,0xFF,0x80, - 0xFF,0xC0,0xFF,0x80,0xFE,0x00,0xEF,0x00,0x4F,0x00,0x07,0x80,0x07,0x80,0x03,0x00)) - -diamond = ((16, 16), (7, 7), - (0, 0, 1, 0, 3, 128, 7, 192, 14, 224, 28, 112, 56, 56, 112, 28, 56, - 56, 28, 112, 14, 224, 7, 192, 3, 128, 1, 0, 0, 0, 0, 0), - (1, 0, 3, 128, 7, 192, 15, 224, 31, 240, 62, 248, 124, 124, 248, 62, - 124, 124, 62, 248, 31, 240, 15, 224, 7, 192, 3, 128, 1, 0, 0, 0)) - -ball = ((16, 16), (7, 7), - (0, 0, 3, 192, 15, 240, 24, 248, 51, 252, 55, 252, 127, 254, 127, 254, - 127, 254, 127, 254, 63, 252, 63, 252, 31, 248, 15, 240, 3, 192, 0, 0), - (3, 192, 15, 240, 31, 248, 63, 252, 127, 254, 127, 254, 255, 255, 255, - 255, 255, 255, 255, 255, 127, 254, 127, 254, 63, 252, 31, 248, 15, 240, - 3, 192)) - -broken_x = ((16, 16), (7, 7), - (0, 0, 96, 6, 112, 14, 56, 28, 28, 56, 12, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 12, 48, 28, 56, 56, 28, 112, 14, 96, 6, 0, 0), - (224, 7, 240, 15, 248, 31, 124, 62, 62, 124, 30, 120, 14, 112, 0, 0, 0, - 0, 14, 112, 30, 120, 62, 124, 124, 62, 248, 31, 240, 15, 224, 7)) - - -tri_left = ((16, 16), (1, 1), - (0, 0, 96, 0, 120, 0, 62, 0, 63, 128, 31, 224, 31, 248, 15, 254, 15, 254, - 7, 128, 7, 128, 3, 128, 3, 128, 1, 128, 1, 128, 0, 0), - (224, 0, 248, 0, 254, 0, 127, 128, 127, 224, 63, 248, 63, 254, 31, 255, - 31, 255, 15, 254, 15, 192, 7, 192, 7, 192, 3, 192, 3, 192, 1, 128)) - -tri_right = ((16, 16), (14, 1), - (0, 0, 0, 6, 0, 30, 0, 124, 1, 252, 7, 248, 31, 248, 127, 240, 127, 240, - 1, 224, 1, 224, 1, 192, 1, 192, 1, 128, 1, 128, 0, 0), - (0, 7, 0, 31, 0, 127, 1, 254, 7, 254, 31, 252, 127, 252, 255, 248, 255, - 248, 127, 240, 3, 240, 3, 224, 3, 224, 3, 192, 3, 192, 1, 128)) - - - -#here is an example string resource cursor. to use this; -# curs, mask = pygame.cursors.compile_cursor(pygame.cursors.thickarrow_strings, 'X', '.') -# pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask) - -thickarrow_strings = ( #sized 24x24 - "XX ", - "XXX ", - "XXXX ", - "XX.XX ", - "XX..XX ", - "XX...XX ", - "XX....XX ", - "XX.....XX ", - "XX......XX ", - "XX.......XX ", - "XX........XX ", - "XX........XXX ", - "XX......XXXXX ", - "XX.XXX..XX ", - "XXXX XX..XX ", - "XX XX..XX ", - " XX..XX ", - " XX..XX ", - " XX..XX ", - " XXXX ", - " XX ", - " ", - " ", - " ", +import pygame + +_cursor_id_table = { + pygame.SYSTEM_CURSOR_ARROW: "SYSTEM_CURSOR_ARROW", + pygame.SYSTEM_CURSOR_IBEAM: "SYSTEM_CURSOR_IBEAM", + pygame.SYSTEM_CURSOR_WAIT: "SYSTEM_CURSOR_WAIT", + pygame.SYSTEM_CURSOR_CROSSHAIR: "SYSTEM_CURSOR_CROSSHAIR", + pygame.SYSTEM_CURSOR_WAITARROW: "SYSTEM_CURSOR_WAITARROW", + pygame.SYSTEM_CURSOR_SIZENWSE: "SYSTEM_CURSOR_SIZENWSE", + pygame.SYSTEM_CURSOR_SIZENESW: "SYSTEM_CURSOR_SIZENESW", + pygame.SYSTEM_CURSOR_SIZEWE: "SYSTEM_CURSOR_SIZEWE", + pygame.SYSTEM_CURSOR_SIZENS: "SYSTEM_CURSOR_SIZENS", + pygame.SYSTEM_CURSOR_SIZEALL: "SYSTEM_CURSOR_SIZEALL", + pygame.SYSTEM_CURSOR_NO: "SYSTEM_CURSOR_NO", + pygame.SYSTEM_CURSOR_HAND: "SYSTEM_CURSOR_HAND", +} + + +class Cursor: + def __init__(self, *args): + """Cursor(size, hotspot, xormasks, andmasks) -> Cursor + Cursor(hotspot, Surface) -> Cursor + Cursor(constant) -> Cursor + Cursor(Cursor) -> copies the Cursor object passed as an argument + Cursor() -> Cursor + + pygame object for representing cursors + + You can initialize a cursor from a system cursor or use the + constructor on an existing Cursor object, which will copy it. + Providing a Surface instance will render the cursor displayed + as that Surface when used. + + These Surfaces may use other colors than black and white.""" + if len(args) == 0: + self.type = "system" + self.data = (pygame.SYSTEM_CURSOR_ARROW,) + elif len(args) == 1 and args[0] in _cursor_id_table: + self.type = "system" + self.data = (args[0],) + elif len(args) == 1 and isinstance(args[0], Cursor): + self.type = args[0].type + self.data = args[0].data + elif ( + len(args) == 2 and len(args[0]) == 2 and isinstance(args[1], pygame.Surface) + ): + self.type = "color" + self.data = tuple(args) + elif len(args) == 4 and len(args[0]) == 2 and len(args[1]) == 2: + self.type = "bitmap" + # pylint: disable=consider-using-generator + # See https://github.com/pygame/pygame/pull/2509 for analysis + self.data = tuple(tuple(arg) for arg in args) + else: + raise TypeError("Arguments must match a cursor specification") + + def __len__(self): + return len(self.data) + + def __iter__(self): + return iter(self.data) + + def __getitem__(self, index): + return self.data[index] + + def __eq__(self, other): + return isinstance(other, Cursor) and self.data == other.data + + def __ne__(self, other): + return not self.__eq__(other) + + def __copy__(self): + """Clone the current Cursor object. + You can do the same thing by doing Cursor(Cursor).""" + return self.__class__(self) + + copy = __copy__ + + def __hash__(self): + return hash(tuple([self.type] + list(self.data))) + + def __repr__(self): + if self.type == "system": + id_string = _cursor_id_table.get(self.data[0], "constant lookup error") + return f"" + if self.type == "bitmap": + size = f"size: {self.data[0]}" + hotspot = f"hotspot: {self.data[1]}" + return f"" + if self.type == "color": + hotspot = f"hotspot: {self.data[0]}" + surf = repr(self.data[1]) + return f"" + raise TypeError("Invalid Cursor") + + +# Python side of the set_cursor function: C side in mouse.c +def set_cursor(*args): + """set_cursor(pygame.cursors.Cursor OR args for a pygame.cursors.Cursor) -> None + set the mouse cursor to a new cursor""" + cursor = Cursor(*args) + pygame.mouse._set_cursor(**{cursor.type: cursor.data}) + + +pygame.mouse.set_cursor = set_cursor +del set_cursor # cleanup namespace + + +# Python side of the get_cursor function: C side in mouse.c +def get_cursor(): + """get_cursor() -> pygame.cursors.Cursor + get the current mouse cursor""" + return Cursor(*pygame.mouse._get_cursor()) + + +pygame.mouse.get_cursor = get_cursor +del get_cursor # cleanup namespace + +arrow = Cursor( + (16, 16), + (0, 0), + ( + 0x00, + 0x00, + 0x40, + 0x00, + 0x60, + 0x00, + 0x70, + 0x00, + 0x78, + 0x00, + 0x7C, + 0x00, + 0x7E, + 0x00, + 0x7F, + 0x00, + 0x7F, + 0x80, + 0x7C, + 0x00, + 0x6C, + 0x00, + 0x46, + 0x00, + 0x06, + 0x00, + 0x03, + 0x00, + 0x03, + 0x00, + 0x00, + 0x00, + ), + ( + 0x40, + 0x00, + 0xE0, + 0x00, + 0xF0, + 0x00, + 0xF8, + 0x00, + 0xFC, + 0x00, + 0xFE, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x80, + 0xFF, + 0xC0, + 0xFF, + 0x80, + 0xFE, + 0x00, + 0xEF, + 0x00, + 0x4F, + 0x00, + 0x07, + 0x80, + 0x07, + 0x80, + 0x03, + 0x00, + ), +) + +diamond = Cursor( + (16, 16), + (7, 7), + ( + 0, + 0, + 1, + 0, + 3, + 128, + 7, + 192, + 14, + 224, + 28, + 112, + 56, + 56, + 112, + 28, + 56, + 56, + 28, + 112, + 14, + 224, + 7, + 192, + 3, + 128, + 1, + 0, + 0, + 0, + 0, + 0, + ), + ( + 1, + 0, + 3, + 128, + 7, + 192, + 15, + 224, + 31, + 240, + 62, + 248, + 124, + 124, + 248, + 62, + 124, + 124, + 62, + 248, + 31, + 240, + 15, + 224, + 7, + 192, + 3, + 128, + 1, + 0, + 0, + 0, + ), +) + +ball = Cursor( + (16, 16), + (7, 7), + ( + 0, + 0, + 3, + 192, + 15, + 240, + 24, + 248, + 51, + 252, + 55, + 252, + 127, + 254, + 127, + 254, + 127, + 254, + 127, + 254, + 63, + 252, + 63, + 252, + 31, + 248, + 15, + 240, + 3, + 192, + 0, + 0, + ), + ( + 3, + 192, + 15, + 240, + 31, + 248, + 63, + 252, + 127, + 254, + 127, + 254, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 127, + 254, + 127, + 254, + 63, + 252, + 31, + 248, + 15, + 240, + 3, + 192, + ), +) + +broken_x = Cursor( + (16, 16), + (7, 7), + ( + 0, + 0, + 96, + 6, + 112, + 14, + 56, + 28, + 28, + 56, + 12, + 48, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12, + 48, + 28, + 56, + 56, + 28, + 112, + 14, + 96, + 6, + 0, + 0, + ), + ( + 224, + 7, + 240, + 15, + 248, + 31, + 124, + 62, + 62, + 124, + 30, + 120, + 14, + 112, + 0, + 0, + 0, + 0, + 14, + 112, + 30, + 120, + 62, + 124, + 124, + 62, + 248, + 31, + 240, + 15, + 224, + 7, + ), +) + +tri_left = Cursor( + (16, 16), + (1, 1), + ( + 0, + 0, + 96, + 0, + 120, + 0, + 62, + 0, + 63, + 128, + 31, + 224, + 31, + 248, + 15, + 254, + 15, + 254, + 7, + 128, + 7, + 128, + 3, + 128, + 3, + 128, + 1, + 128, + 1, + 128, + 0, + 0, + ), + ( + 224, + 0, + 248, + 0, + 254, + 0, + 127, + 128, + 127, + 224, + 63, + 248, + 63, + 254, + 31, + 255, + 31, + 255, + 15, + 254, + 15, + 192, + 7, + 192, + 7, + 192, + 3, + 192, + 3, + 192, + 1, + 128, + ), ) -sizer_x_strings = ( #sized 24x16 - " X X ", - " XX XX ", - " X.X X.X ", - " X..X X..X ", - " X...XXXXXXXX...X ", - "X................X ", - " X...XXXXXXXX...X ", - " X..X X..X ", - " X.X X.X ", - " XX XX ", - " X X ", - " ", - " ", - " ", - " ", - " ", +tri_right = Cursor( + (16, 16), + (14, 1), + ( + 0, + 0, + 0, + 6, + 0, + 30, + 0, + 124, + 1, + 252, + 7, + 248, + 31, + 248, + 127, + 240, + 127, + 240, + 1, + 224, + 1, + 224, + 1, + 192, + 1, + 192, + 1, + 128, + 1, + 128, + 0, + 0, + ), + ( + 0, + 7, + 0, + 31, + 0, + 127, + 1, + 254, + 7, + 254, + 31, + 252, + 127, + 252, + 255, + 248, + 255, + 248, + 127, + 240, + 3, + 240, + 3, + 224, + 3, + 224, + 3, + 192, + 3, + 192, + 1, + 128, + ), ) -sizer_y_strings = ( #sized 16x24 - " X ", - " X.X ", - " X...X ", - " X.....X ", - " X.......X ", - "XXXXX.XXXXX ", - " X.X ", - " X.X ", - " X.X ", - " X.X ", - " X.X ", - " X.X ", - " X.X ", - "XXXXX.XXXXX ", - " X.......X ", - " X.....X ", - " X...X ", - " X.X ", - " X ", - " ", - " ", - " ", - " ", - " ", + + +# Here is an example string resource cursor. To use this: +# curs, mask = pygame.cursors.compile_cursor(pygame.cursors.thickarrow_strings, 'X', '.') +# pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask) +# Be warned, though, that cursors created from compiled strings do not support colors. + +# sized 24x24 +thickarrow_strings = ( + "XX ", + "XXX ", + "XXXX ", + "XX.XX ", + "XX..XX ", + "XX...XX ", + "XX....XX ", + "XX.....XX ", + "XX......XX ", + "XX.......XX ", + "XX........XX ", + "XX........XXX ", + "XX......XXXXX ", + "XX.XXX..XX ", + "XXXX XX..XX ", + "XX XX..XX ", + " XX..XX ", + " XX..XX ", + " XX..XX ", + " XXXX ", + " XX ", + " ", + " ", + " ", ) -sizer_xy_strings = ( #sized 24x16 - "XXXXXXXX ", - "X.....X ", - "X....X ", - "X...X ", - "X..X.X ", - "X.X X.X ", - "XX X.X X ", - "X X.X XX ", - " X.XX.X ", - " X...X ", - " X...X ", - " X....X ", - " X.....X ", - " XXXXXXXX ", - " ", - " ", + +# sized 24x16 +sizer_x_strings = ( + " X X ", + " XX XX ", + " X.X X.X ", + " X..X X..X ", + " X...XXXXXXXX...X ", + "X................X ", + " X...XXXXXXXX...X ", + " X..X X..X ", + " X.X X.X ", + " XX XX ", + " X X ", + " ", + " ", + " ", + " ", + " ", ) -textmarker_strings = ( #sized 8x16 - "ooo ooo ", - " o ", - " o ", - " o ", - " o ", - " o ", - " o ", - " o ", - " o ", - " o ", - " o ", - "ooo ooo ", - " ", - " ", - " ", - " ", + +# sized 16x24 +sizer_y_strings = ( + " X ", + " X.X ", + " X...X ", + " X.....X ", + " X.......X ", + "XXXXX.XXXXX ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + " X.X ", + "XXXXX.XXXXX ", + " X.......X ", + " X.....X ", + " X...X ", + " X.X ", + " X ", + " ", + " ", + " ", + " ", + " ", ) +# sized 24x16 +sizer_xy_strings = ( + "XXXXXXXX ", + "X.....X ", + "X....X ", + "X...X ", + "X..X.X ", + "X.X X.X ", + "XX X.X X ", + "X X.X XX ", + " X.XX.X ", + " X...X ", + " X...X ", + " X....X ", + " X.....X ", + " XXXXXXXX ", + " ", + " ", +) +# sized 8x16 +textmarker_strings = ( + "ooo ooo ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + " o ", + "ooo ooo ", + " ", + " ", + " ", + " ", +) -def compile(strings, black='X', white='.',xor='o'): - """pygame.cursors.compile(strings, black, white,xor) -> data, mask -compile cursor strings into cursor data -This takes a set of strings with equal length and computes -the binary data for that cursor. The string widths must be -divisible by 8. +def compile(strings, black="X", white=".", xor="o"): + """pygame.cursors.compile(strings, black, white, xor) -> data, mask + compile cursor strings into cursor data -The black and white arguments are single letter strings that -tells which characters will represent black pixels, and which -characters represent white pixels. All other characters are -considered clear. + This takes a set of strings with equal length and computes + the binary data for that cursor. The string widths must be + divisible by 8. -This returns a tuple containing the cursor data and cursor mask -data. Both these arguments are used when setting a cursor with -pygame.mouse.set_cursor(). -""" + The black and white arguments are single letter strings that + tells which characters will represent black pixels, and which + characters represent white pixels. All other characters are + considered clear. - #first check for consistent lengths - size = len(strings[0]), len(strings) - if size[0] % 8 or size[1] % 8: - raise ValueError("cursor string sizes must be divisible by 8 %s" % - size) - for s in strings[1:]: - if len(s) != size[0]: - raise ValueError("Cursor strings are inconsistent lengths") - - #create the data arrays. - #this could stand a little optimizing - maskdata = [] - filldata = [] - maskitem = fillitem = 0 - step = 8 - for s in strings: - for c in s: - maskitem = maskitem << 1 - fillitem = fillitem << 1 - step = step - 1 - if c == black: - maskitem = maskitem | 1 - fillitem = fillitem | 1 - elif c == white: - maskitem = maskitem | 1 - elif c == xor: - fillitem = fillitem | 1 - if not step: - maskdata.append(maskitem) - filldata.append(fillitem) - maskitem = fillitem = 0 - step = 8 - return tuple(filldata), tuple(maskdata) + Some systems allow you to set a special toggle color for the + system color, this is also called the xor color. If the system + does not support xor cursors, that color will simply be black. + This returns a tuple containing the cursor data and cursor mask + data. Both these arguments are used when setting a cursor with + pygame.mouse.set_cursor(). + """ + # first check for consistent lengths + size = len(strings[0]), len(strings) + if size[0] % 8 or size[1] % 8: + raise ValueError(f"cursor string sizes must be divisible by 8 {size}") + for s in strings[1:]: + if len(s) != size[0]: + raise ValueError("Cursor strings are inconsistent lengths") + + # create the data arrays. + # this could stand a little optimizing + maskdata = [] + filldata = [] + maskitem = fillitem = 0 + step = 8 + for s in strings: + for c in s: + maskitem = maskitem << 1 + fillitem = fillitem << 1 + step = step - 1 + if c == black: + maskitem = maskitem | 1 + fillitem = fillitem | 1 + elif c == white: + maskitem = maskitem | 1 + elif c == xor: + fillitem = fillitem | 1 + + if not step: + maskdata.append(maskitem) + filldata.append(fillitem) + maskitem = fillitem = 0 + step = 8 + + return tuple(filldata), tuple(maskdata) def load_xbm(curs, mask): """pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args -reads a pair of XBM files into set_cursor arguments + reads a pair of XBM files into set_cursor arguments + + Arguments can either be filenames or filelike objects + with the readlines method. Not largely tested, but + should work with typical XBM files. + """ -Arguments can either be filenames or filelike objects -with the readlines method. Not largely tested, but -should work with typical XBM files. -""" def bitswap(num): val = 0 for x in range(8): - b = num&(1< Pygame Logos - - - - - -

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

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

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

-
- pygame_small.gif - 338 x 100
-

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

-
-pygame_powered.gif - 250 x 100
-


 
- - - - - - - diff --git a/venv/Lib/site-packages/pygame/docs/pygame_logo.gif b/venv/Lib/site-packages/pygame/docs/pygame_logo.gif deleted file mode 100644 index 63d2e77273839ba1a50bb5d146a1323c4dce1b28..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/docs/pygame_logo.gif and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/docs/pygame_powered.gif b/venv/Lib/site-packages/pygame/docs/pygame_powered.gif deleted file mode 100644 index 5a2bb5f96dc72a4feac9eef8bbef158ed8f4332c..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/docs/pygame_powered.gif and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/docs/pygame_small.gif b/venv/Lib/site-packages/pygame/docs/pygame_small.gif deleted file mode 100644 index 4916dbf7a87fabbca307032243439db6243f315c..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/docs/pygame_small.gif and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/docs/pygame_tiny.gif b/venv/Lib/site-packages/pygame/docs/pygame_tiny.gif deleted file mode 100644 index f9aa5177cb613b3b4858b90b1e9dccd28d377125..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/docs/pygame_tiny.gif and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/docs/ref/docscomments.json b/venv/Lib/site-packages/pygame/docs/ref/docscomments.json deleted file mode 100644 index 09a646a209872e5bc1e6d27e55b6395c50558dd4..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/docs/ref/docscomments.json +++ /dev/null @@ -1 +0,0 @@ -[{"content": "How To Get ALL The Mouse Clicks.\n\nTook me hours to figure this out. Also note that button 1 and 3 pressed at the same time shows up as button 2, at least on my ubuntu computer.\n\ne=pygame.event.wait()\nif e.type == MOUSEBUTTONDOWN and e.button == 4 : do something mousey", "user_title": "Douglas Smith", "datetimeon": "2005-11-11T14:05:52", "link": "pygame.mouse.get_pressed", "id": 3}, {"content": "If you're trying to create a surface with per-pixel alphas, and\n\n my_surface = pygame.Surface((w, h), SRCALPHA)\n\ncreates a regular surface instead, try\n\n my_surface = pygame.Surface((w, h)).convert_alpha()", "user_title": "Marius Gedminas", "datetimeon": "2006-01-05T16:07:06", "link": "pygame.Surface", "id": 39}, {"content": "Interestingly, pygame.font.get_default_font() returns a font name ('freesansbold.ttf') which is not among the 189 listed by pygame.font.get_fonts().", "user_title": "Dave Burton", "datetimeon": "2011-01-03T08:47:41", "link": "pygame.font.get_fonts", "id": 3698}, {"content": "The font name is not a list! It is a single string.\n\nThe string can contain multiple font names with commas between them,\nbut if you pass a Python list (or tuple) you'll get an error.", "user_title": "Dave Burton", "datetimeon": "2011-01-03T09:13:14", "link": "pygame.font.SysFont", "id": 3699}, {"content": "Re: \"During its lifetime, the PixelArray locks the surface, thus you explicitly have to delete it once its not used anymore and the surface should perform operations in the same scope.\"\n\n1. Grammer: s/its/it's/\n\n2. s/you explicitly have to delete/you have to explicitly delete/\n\n3. I assume that to explicitly delete it you can either use \"del pxarray\"\nor else simply exit the function to which pxarray is local. Is that correct?\n\n4. What does \"and the surface should perform operations in the same scope\" mean?\nIs it saying something about the surface returned by px.make_surface(), i.e.,\nthat it should be a local variable in the same function to which pxarray is local?\nOr is it saying something about the surface that is passed to pygame.PixelArray()\nto create the pxarray object, and if so WHAT is it saying?", "user_title": "Dave Burton", "datetimeon": "2011-01-07T03:08:20", "link": "pygame.PixelArray", "id": 3703}, {"content": "On my Windows Vista machine running Python 3.1.2 and pygame 1.9.1, pgame.font.get_fonts() returns a list of 189 fonts. All the font names are lower case, and there are no special characters (like hyphens) in the names. The expected 'timesnewroman', 'arial', 'arialblack', 'couriernew', 'veranda', 'microsoftsansserif', 'symbol' and 'wingdings' are there (but not 'times' or 'roman' or 'helvetica'), but also many obscure fonts that I've never heard of.", "user_title": "Dave Burton", "datetimeon": "2011-01-03T08:43:54", "link": "pygame.font.get_fonts", "id": 3697}, {"content": "Pretty cool demo Mr. Anony", "user_title": "Robert Leachman", "datetimeon": "2010-12-10T22:09:50", "link": "pygame.key.get_pressed", "id": 3683}, {"content": "If you want to see a list of attributes, do a help(pygame) and it'll show you", "user_title": "Alex Polosky", "datetimeon": "2010-12-15T23:46:38", "link": "pygame.locals", "id": 3686}, {"content": "Works fine for me on OS X 10.6.5, though yes it does need to brought up to Quartz", "user_title": "Robert Leachman", "datetimeon": "2010-12-04T21:54:48", "link": "pygame.display.init", "id": 3675}, {"content": "See tutorials. \nAfter each line \n pygame.image.load(\"<>\")\nMake it\n pygame.image.load(\"<>\").convert()\nNo matter what, this will increase your speed by 600%!\nThanks to whoever put in that tutorial!\n -P.Z.", "user_title": "Ian Mallett", "datetimeon": "2007-03-05T00:13:41", "link": "pygame.draw", "id": 403}, {"content": "see:\nhttp://www.pygame.org/docs/tut/newbieguide.html\n#4", "user_title": "Ian Mallett", "datetimeon": "2007-03-17T13:13:59", "link": "pygame.draw", "id": 439}, {"content": "Dear readers, here is a working example of MPEG playing.\n-tgfcoder\n\n\nimport pygame, time\n\npygame.init()\n\ncine = pygame.movie.Movie('a-movie.mpg')\nsz=cine.get_size()\npygame.display.set_mode(sz)\nscreen = pygame.display.get_surface()\ncine.set_display(screen)\ncine.play()\nwhile True:\n time.sleep(1)", "user_title": "Jordan Trudgett", "datetimeon": "2008-01-01T09:40:25", "link": "pygame.movie", "id": 1349}, {"content": "Oh, please replace pygame.init() with pygame.display.init()\nBecause we don't want the mixer to be initialised.", "user_title": "Jordan Trudgett", "datetimeon": "2008-01-01T09:46:24", "link": "pygame.movie", "id": 1350}, {"content": "Well, actually it's not even that (x,y) needs to be in the referential of the Rect, because if it was true, then (0,0) would return 1, and it doesn't. It is really a bug.", "user_title": "Guillaume Rava", "datetimeon": "2007-04-20T18:04:08", "link": "Rect.collidepoint", "id": 503}, {"content": "# Ellipse example:\n# When border=0 ellipse is filled\n# (screen, (rgb colour) (Xpos,Ypos,width,height),border width)\npygame.draw.ellipse(screen, (0, 127, 0), (300, 150, 80, 40), 0)", "user_title": "Miroslav Cika", "datetimeon": "2008-01-10T10:08:04", "link": "pygame.draw.ellipse", "id": 1392}, {"content": "# Circle example:\n# When border=0 circle is filled\n# (screen, (rgb colour), (Xpos,Ypos),Diameter,border width)\npygame.draw.circle(screen, (0, 127, 255), (300, 140), 50, 4)", "user_title": "Miroslav Cika", "datetimeon": "2008-01-10T10:20:38", "link": "pygame.draw.circle", "id": 1393}, {"content": "thank you Trudget for the working code", "user_title": "vishwanath", "datetimeon": "2008-01-13T13:38:10", "link": "pygame.movie", "id": 1407}, {"content": "\"\"\"It seems that this method does not detect point collisions that fall anywhere \nalong the right wall or bottom wall of the rect used. The following program\ncreates a rect with a width and height of 4, and a topleft corner at [0,0]. \nThe program then moves along each row of the rect area from left to right and\ntop to bottom by 1 unit, creating a new point and checking to see if the point\ncollides with the rect. If the point collides, a 1 is printed, and if the\npoint doesn't collide, a 0 is printed.\"\"\"\n\n# import\nimport pygame\n\n# main\ny = 4\nr = pygame.Rect(0,0,y,y)\np = [0,0]\npList = []\nwhile p != [0,y+1]:\n\tfor n in range(0,y+1):\n\t\tp[0] = n\n\t\tif r.collidepoint(p):\n\t\t\tpList.append(1)\n\t\telse:\n\t\t\tpList.append(0)\n\n\tprint '%d %d %d %d %d' % (pList[0],pList[1],pList[2],pList[3],pList[4])\n\t\n\tpList = []\n\tp[0] = 0\n\tp[1] += 1\n\n# wait for user to manually exit program\ninput('press enter to exit')\n\n\"\"\"Here is the output:\"\"\"\n1 1 1 1 0\n1 1 1 1 0\n1 1 1 1 0\n1 1 1 1 0\n0 0 0 0 0\npress enter to exit\n\n\"\"\"Even if you were to directly reference the topright, bottomleft, or bottomright\npoint of the rect as the argument to the collidepoint function, the rect still \nwould not detect a collision. The rect does, however, detect collision with its\ntopleft point:\"\"\"\n\n>>>r.collidepoint(r.bottomleft)\n0\n>>>r.collidepoint(r.topright)\n0\n>>>r.collidepoint(r.bottomright)\n0\n>>>r.collidepoint(r.topleft)\n1", "user_title": "Tim Winter", "datetimeon": "2008-01-16T15:33:48", "link": "Rect.collidepoint", "id": 1420}, {"content": "To use the scrollwheel:\nfor event in pygame.event.get():\n if event.type == MOUSEBUTTONDOWN:\n if event.button == 4:\n #Zoom Out\n elif event.button == 5:\n #Zoom In", "user_title": "Ian Mallett", "datetimeon": "2008-01-25T15:59:11", "link": "pygame.mouse", "id": 1442}, {"content": "Use the following class to generate a bezier curve that can be drawn with aalines:\n\n## Class begins here\nclass Bezier:\n\n\tclass SmoothnessError(Exception): pass\n\tclass CurveError(Exception): pass\n\n\tdef __init__(self):\n\t\t\"\"\"\n\t\tA Python class for generating bezier curves\n\t\t\n\t\tAn implementation of an algorithm presented by Nils Pipenbrinck\n\t\thttp://www.cubic.org/docs/bezier.htm\n\t\t\"\"\"\n\t\n\tdef __lerp(self, ptA, ptB, t):\n\t\t\"\"\"\n\t\tReturns the linear interp between two points as a list\n\t\tptA and ptB are a list of xy coords, t is the point on the curve\n\t\t\"\"\"\n\t\tdest = []\n\t\tdest.append(ptA[0]+float(ptB[0]-ptA[0])*t)\n\t\tdest.append(ptA[1]+float(ptB[1]-ptA[1])*t)\n\t\treturn dest\n\t\n\tdef bezierPt(self, ctrlPts, t):\n\t\t\"\"\"A recursive function for finding point t along a bezier curve\"\"\"\n\t\tif len(ctrlPts) == 1:\n\t\t\t#print \"Len is 1\", ctrlPts\n\t\t\treturn ctrlPts[0]\n\t\tlerpList = []\n\t\tfor i in xrange(len(ctrlPts)-1):\n\t\t\tptA = [ctrlPts[i][0],ctrlPts[i][1]]\n\t\t\tptB = [ctrlPts[i+1][0],ctrlPts[i+1][1]]\n\t\t\tlerpList.append(self.__lerp(ptA,ptB,t))\n\t\t#print len(lerpList)\n\t\treturn self.bezierPt(lerpList, t)\n\t\n\tdef makeBezier(self, ctrlPts, smoothness):\n\t\t\"\"\"\n\t\tReturns a list of points on a bezier curve\n\t\t\n\t\tctrlPts is a list of 2d Points that define the curve, in most cases these\n\t\tconsist of control point locations and their handles, except in a 3 point\n\t\tcurve where it's just defined by the three control points.\n\t\t\n\t\tsmoothness is the number of points on the curve that should be generated.\n\t\tThis should always be more than two points or generating the bezier curve is\n\t\tpointless and the script dies in a fire (or throws an exception)\n\t\t\"\"\"\n\t\t\n\t\tif len(ctrlPts) < 2:\n\t\t\traise self.CurverError(\"Curve list must contain more than one point\")\n\t\tif smoothness < 3:\n\t\t\traise self.SmoothnessError(\"Smoothness must be more than two\")\n\t\titeration = smoothness\n\t\tbezierList = []\n\t\tsubtract=1.0/smoothness\n\t\tfor i in xrange(0,iteration):\n\t\t\tt = 1.0-(subtract*i)\n\t\t\tif t < subtract:\n\t\t\t\tt = 0\n\t\t\tbPt = self.bezierPt(ctrlPts,t)\n\t\t\t#print bPt\n\t\t\tbezierList.append(bPt)\n\t\treturn bezierLis\n## Class ends\n\n###################\n# An example of how to use the class with pygame\n\n\n## Pygame Example\nimport math, pygame\nfrom pygame.locals import *\nimport bezier\n\ndef main():\n pygame.init()\n screen = pygame.display.set_mode((640,480))\n clock = pygame.time.Clock()\n \n b = bezier.Bezier()\n \"\"\"\n\tA bezier curve definition, a list of 2d poins, simple innit\n\tIt's basically control points with control handle locations before or\n\tafter the control point.\n\t\n Read http://www.cubic.org/docs/bezier.htm for more info\n \"\"\"\n bezierPts = [[40,100],[80,20],[150,180],[260,100]]\n bLine = b.makeBezier(bezierPts, 10)\n screen.fill((255,255,255))\n pygame.draw.aalines(screen, (1,1,1), False, bLine, 1)\n pygame.display.flip()\n bounce = False\n \n while True:\n clock.tick(60)\n pygame.event.pump()\n event = pygame.event.poll()\n if event.type == QUIT:\n return\n if event.type == KEYDOWN:\n if event.key == K_ESCAPE:\n return\n setTo = pygame.time.get_ticks()/20\n bezierPts[1][1] = setTo\n bLine = b.makeBezier(bezierPts,20)\n screen.fill((255,255,255))\n pygame.draw.aalines(screen, (1,1,1), False, bLine, 1)\n pygame.display.flip()\n\nif __name__ == \"__main__\":\n m = main()\n## End example", "user_title": "Jeiel Aranal", "datetimeon": "2008-02-06T10:50:10", "link": "pygame.draw.aalines", "id": 1502}, {"content": "Play can return None. So be sure to check the channel before using it. Something like this...\n\n channel = self.bounce_sound.play()\n if channel is not None:\n channel.set_volume(1.0 - stereo, stereo)", "user_title": "Will McGugan", "datetimeon": "2007-06-14T12:13:28", "link": "Sound.play", "id": 635}, {"content": "Should have an optional option \nfor sound playback speed...", "user_title": "Ian Mallett", "datetimeon": "2007-06-30T19:41:18", "link": "Sound.play", "id": 676}, {"content": "When antialias is enabled, rendering it on a black background makes it look bold.", "user_title": "Ian Mallett", "datetimeon": "2007-07-03T01:18:12", "link": "Font.render", "id": 689}, {"content": "Should have an Anti-alias option...", "user_title": "Ian Mallett", "datetimeon": "2008-02-25T23:09:09", "link": "pygame.draw.circle", "id": 1646}, {"content": "mods = pygame.key.get_mods()\nif mods & KMOD_LSHIFT: #use whatever KMOD_ constant you want;)\n print \"left shift pressed\"", "user_title": "Isaiah Heyer", "datetimeon": "2008-03-29T16:22:04", "link": "pygame.key.get_mods", "id": 1716}, {"content": "I would like to have a method of telling which side of a rect a point collides. \nIn other words, which side is the point closest to?", "user_title": "Ian Mallett", "datetimeon": "2008-03-29T23:08:21", "link": "Rect.collidepoint", "id": 1718}, {"content": "Right. Unfortunately, That's the way it is. A width or height of 0 should also\nbe allowed, for rectangles of changing sizes (think progressbar at 0%)", "user_title": "Ian Mallett", "datetimeon": "2007-08-01T17:48:16", "link": "pygame.draw.rect", "id": 785}, {"content": "Instead of drawing a circle with zero radius, you can use the method set_at on the surface to set the color of a single pixel: http://www.pygame.org/docs/ref/surface.html#Surface.set_at", "user_title": "Victor Blomqvist", "datetimeon": "2007-09-23T08:07:45", "link": "pygame.draw.circle", "id": 873}, {"content": "'dummy' driver is missing ;-)", "user_title": "DR0ID", "datetimeon": "2007-09-23T12:08:05", "link": "pygame.display.init", "id": 875}, {"content": "please able my display mode with opengl acceleration", "user_title": "aaron pedralvez", "datetimeon": "2007-09-27T02:05:16", "link": "pygame.display.init", "id": 882}, {"content": "In the event MOUSEBUTTONDOWN, if you're using a mouse with a rotating wheel,\nevent.button returns 4 when it is rotated forward (counterclockwise) and 5 when\nit is rotating backward (clockwise). I used a print statement to discover this.", "user_title": "Andy Hanson", "datetimeon": "2007-10-02T20:43:15", "link": "pygame.event", "id": 900}, {"content": "In the event MOUSEBUTTONDOWN, if you're using a mouse with a rotating wheel,\nevent.button returns 4 when it is rotated forward (counterclockwise) and 5 when\nit is rotating backward (clockwise). I used a print statement to discover this.", "user_title": "Andy Hanson", "datetimeon": "2007-10-02T20:43:21", "link": "pygame.event", "id": 901}, {"content": "You can request fullscreen, but there doesn't seem to be a way to\ndetermine whether it's on. Meaning, there ought to be a 'get_mode()'.", "user_title": "Andy Sommerville", "datetimeon": "2007-12-04T14:43:28", "link": "pygame.display.set_mode", "id": 1206}, {"content": "http://www.pygame.org/docs/ref/sndarray.html#pygame.sndarray.make_sound\ncan be used to synthesize a sound object from sound samples.", "user_title": "Ian Mallett", "datetimeon": "2008-05-26T20:16:58", "link": "pygame.mixer.Sound", "id": 1953}, {"content": "The movie module in Pygame 1.8 works on Windows.\nThe statement that it doesn't work is out-of-date.", "user_title": "Jason M. Marshall", "datetimeon": "2008-05-21T14:15:44", "link": "pygame.movie", "id": 1917}, {"content": "return bezierLis -> return bezierList (line 65)", "user_title": "Jordan Trudgett", "datetimeon": "2008-06-18T02:46:43", "link": "pygame.draw.aalines", "id": 2060}, {"content": "pygame.mixer.get_num_channels(): return count", "user_title": "Jordan Trudgett", "datetimeon": "2008-07-10T13:15:18", "link": "pygame.mixer.get_num_channels", "id": 2150}, {"content": "Calling Surface.lock() before many calls to Surface.set_at() and Surface.unlock() after is a great and easy optimization.", "user_title": "Ian Mallett", "datetimeon": "2008-07-11T23:25:11", "link": "Surface.set_at", "id": 2156}, {"content": "Just set the delay to something really big.", "user_title": "Ian Mallett", "datetimeon": "2008-08-19T06:01:04", "link": "pygame.key.set_repeat", "id": 2265}, {"content": "Is this thread-safe? Can I safely post messages from a different thread\nfrom the one that's processing events and rendering?", "user_title": "Weeble", "datetimeon": "2008-11-28T19:09:39", "link": "pygame.event.post", "id": 2339}, {"content": "''' Change alpha for surfaces with per-pixel alpha; only for small surfaces '''\ndef change_alpha(surface,alpha=0.5):\n\tsize = surface.get_size()\n\ttry:\n\t\tfor y in xrange(size[1]):\n\t\t\tfor x in xrange(size[0]):\n\t\t\t\tr,g,b,a = surface.get_at((x,y))\n\t\t\t\tsurface.set_at((x,y),(r,g,b,int(a*alpha)))\n\texcept:\n\t\treturn surface\n\treturn surface", "user_title": "Josef Vanzura", "datetimeon": "2010-11-19T09:47:18", "link": "Surface.set_alpha", "id": 3245}, {"content": "You can also do it with surfarray (faster).", "user_title": "Josef Vanzura", "datetimeon": "2010-11-19T09:49:02", "link": "Surface.set_alpha", "id": 3246}, {"content": "Sorry. I didn't read the previous comment, which is a better way.", "user_title": "Josef Vanzura", "datetimeon": "2010-11-19T09:56:18", "link": "Surface.set_alpha", "id": 3247}, {"content": "present in pygame 1.9.1 but not in pygame 1.8.1, which is currently the last binary release on Linux.", "user_title": "Shanti Pothapragada", "datetimeon": "2010-11-22T17:04:34", "link": "Rect.copy", "id": 3249}, {"content": "Also includes the attributes: x, y.", "user_title": "Sam Bull", "datetimeon": "2010-10-26T07:40:18", "link": "pygame.Rect", "id": 3225}, {"content": "Works like a charm. Thanks whoever you are.", "user_title": "Bartosz Debski", "datetimeon": "2010-09-29T19:26:57", "link": "Surface.fill", "id": 3211}, {"content": "The code snippet works perfectly; thanks!\nI think the documentation is sorely in need of an update.\nWishlist: other video formats, like .avi?", "user_title": "Ian Mallett", "datetimeon": "2009-01-01T16:05:17", "link": "pygame.movie", "id": 2360}, {"content": "An example to use this:\nscreen = pygame.display.set_mode(SCREENRECT.size) # SCREENRECT is a rect variable...\n # ...with screen dimension\n\ndoggie = pygame.sprite.RenderUpdates() #We create the group\nDog.containers = doggie \n# class Dog: Needs 'pygame.sprite.Sprite.__init__(self,self.containers)'\n# inside def __init__(self, ...):\n\ndog1 = Dog(...) #Class Dog\ndog2 = Dog(...)\n...\ndogN = Dog(...)\n\n... #Some move actions and things\n\n#Now, time to re-paint them all\ndoggie.clear(screen, Background)\nchanges = doggie.draw(screen)\npygame.display.update(changes)\n#Now we have all dogs updated in screen\n\n#---------\nEasy, quick and effortless", "user_title": "Patata", "datetimeon": "2009-01-07T12:38:26", "link": "pygame.sprite.RenderUpdates", "id": 2366}, {"content": "# A better loading script:\n\nimport os, pygame\n\ndef load_image(file_name, colorkey=False, image_directory='images'):\n 'Loads an image, file_name, from image_directory, for use in pygame'\n file = os.path.join(image_directory, file_name)\n _image = pygame.image.load(file)\n if colorkey:\n if colorkey == -1: \n # If the color key is -1, set it to color of upper left corner\n colorkey = _image.get_at((0, 0))\n _image.set_colorkey(colorkey)\n _image = _image.convert()\n else: # If there is no colorkey, preserve the image's alpha per pixel.\n _image = _image.convert_alpha()\n return _image", "user_title": "Nekody Lenkner", "datetimeon": "2009-03-20T21:58:08", "link": "pygame.image.load", "id": 2399}, {"content": "what does it mean by font name? can it be a path to a font?", "user_title": "Mad Cloud Games", "datetimeon": "2010-07-02T01:21:30", "link": "pygame.font.SysFont", "id": 3151}, {"content": "You can use multiple screens, but you'll need to make a separate process for each.", "user_title": "Ian Mallett", "datetimeon": "2009-08-01T23:26:07", "link": "pygame.display.set_mode", "id": 2900}, {"content": "VIDEORESIZE size, w, h\nsize == (w, h) # same data, different access", "user_title": "DR0ID", "datetimeon": "2009-04-04T12:27:05", "link": "pygame.event", "id": 2411}, {"content": "je ne sais pas pourquoi, mais; si vous utiliser une surface pour effacer le display au lieu d'utiliser un 'fill',\nil sera beaucoup plus rapide de blitter une copie du display :\n\ndisplay = pygame.display.set_mode((500,500))\nbackground = pygame.image.load('blablabla...')\ndisplay.blit(background,(0,0))\nbackground = display.copy() ----> utiliser cette copie pour multi-blitter plus rapidement une image de fond.", "user_title": "josmiley", "datetimeon": "2009-08-03T01:50:34", "link": "Surface.copy", "id": 2902}, {"content": "This doesn't say anything about the type attribute.\nYou can compare it to MOUSEBUTTONUP, KEYDOWN, etc to find out what the events\ntype is.", "user_title": "Daniel Westbrook", "datetimeon": "2009-07-29T00:20:38", "link": "pygame.event.Event", "id": 2896}, {"content": "is it a Rect object???", "user_title": "Alex", "datetimeon": "2010-05-04T17:47:38", "link": "pygame.display.update", "id": 3117}, {"content": "TIP:\nIf sound has noise/noisy is choppy or has static, the solution:\n\npygame.mixer.quit() #Make sure you all this before .init()\npygame.mixer.init()", "user_title": "Chris Goldie", "datetimeon": "2009-08-11T05:44:03", "link": "Sound.play", "id": 2911}, {"content": "The convert_alpha function prepares a surface for usage with per-pixel alphas. That is, for example, if you have a PNG or TGA image with an alpha channel controlling opacity of individual pixels, you would want to use this function on your surface after loading the image to speed up the blitting process.", "user_title": "Brad Smithee", "datetimeon": "2010-04-29T02:55:18", "link": "Surface.convert_alpha", "id": 3113}, {"content": "This will be extremely useful!", "user_title": "Ian Mallett", "datetimeon": "2009-08-15T19:21:45", "link": "pygame.transform.average_surfaces", "id": 2917}, {"content": "Some basic sample code for (approximately) constraining a bunch of text to a given width:\n\nwordsToWrite = toWrite.rstrip().split(\" \") #Get rid of the newline char and split on spaces\ncurrLine = \"\"\nnumLines = 0\nmaxWidthFound = 0\nfor word in wordsToWrite:\n currLine = currLine + \" \" + word #Add the next word to the line\n\n if ((textFont.size(currLine))[0] > maxAllowedWidth): #Check if the width of the line exceeds the set limit\n\n if (textFont.size(currLine))[0] > maxWidthFound: #Get the maximum line width found\n maxWidthFound = (textFont.size(currLine))[0]\n\n lines.append (textFont.render(currLine, 1, color, bgcolor)) #Add the rendered line to a list\n currLine = \"\"\n numLines = numLines + 1\n\nif currLine != \"\": #Once we exit the loop, we will probably still have a line to be rendered\n lines.append (textFont.render(currLine, 1, color, bgcolor))\n currLine = \"\"\n numLines = numLines + 1\n\nself.image = pygame.Surface((maxWidthFound + 20, numLines * textFont.get_height() + 20)) #Create a surface of the appropriate size\n\nfor lineNum in range(numLines): \n self.image.blit(lines[lineNum], (10,lineNum * textFont.get_height() + 10))", "user_title": "Aditya Keswani", "datetimeon": "2009-07-20T07:29:06", "link": "Font.render", "id": 2887}, {"content": "For all of these drawing functions, the coordinates are relative to the surface\nyou are drawing to. i.e. if you are drawing to a surface somewhere in the middle of\nthe screen, and you draw a circle at (0,0), its center will be the top-left corner\nof the surface being drawn to, not the top-left corner of the screen", "user_title": "Aditya Keswani", "datetimeon": "2009-07-20T07:23:36", "link": "pygame.draw", "id": 2886}, {"content": "The messages here:\nhttp://www.mail-archive.com/pygame-users@seul.org/msg10616.html\n\nimply that GL_SWAP_CONTROL can also be passed to gl_set_attribute to control whether\ndisplay swaps honor vsync.", "user_title": "Jonathan Hartley", "datetimeon": "2010-03-25T15:42:06", "link": "pygame.display.gl_set_attribute", "id": 3087}, {"content": "Pygame THECOLORS as HTML\nhttps://sites.google.com/site/meticulosslacker/pygame-thecolors", "user_title": "Meticulos Slacker", "datetimeon": "2010-03-18T03:10:26", "link": "pygame.Color", "id": 3078}, {"content": "Should be \"buffer\", not \"buffersize\"", "user_title": "Ian Mallett", "datetimeon": "2010-03-19T00:36:48", "link": "pygame.mixer.pre_init", "id": 3079}, {"content": "Put this first:\nfor e in pygame.event.get()", "user_title": "Ian Mallett", "datetimeon": "2010-03-20T19:51:20", "link": "pygame.key.set_repeat", "id": 3080}, {"content": "mod is the bitfield of KMOD_* constants:\npygame.KMOD_NONE\t0\npygame.KMOD_LSHIFT\t1\npygame.KMOD_RSHIFT\t2\npygame.KMOD_SHIFT\t3\npygame.KMOD_LCTRL\t64\npygame.KMOD_RCTRL\t128\npygame.KMOD_CTRL\t192\npygame.KMOD_LALT\t256\npygame.KMOD_RALT\t512\npygame.KMOD_ALT\t\t768\npygame.KMOD_LMETA\t1024\npygame.KMOD_RMETA\t2048\npygame.KMOD_META\t3072\npygame.KMOD_NUM\t\t4096\npygame.KMOD_CAPS\t8192\npygame.KMOD_MODE\t16384", "user_title": "Vladar", "datetimeon": "2010-03-23T06:16:44", "link": "pygame.event", "id": 3081}, {"content": "If you try to use fadeout, the queued sound will begin, as opposed to stop and pause.", "user_title": "Andy Hanson", "datetimeon": "2009-07-12T22:43:31", "link": "Channel.queue", "id": 2875}, {"content": "I ran into that problem -- the solution is to initialize pygame first :)\n\nimport pygame\npygame.init()\nprint pygame.key.name(pygame.K_UP)", "user_title": "Jared", "datetimeon": "2009-08-29T04:11:27", "link": "pygame.key.name", "id": 2928}, {"content": "It appears that when the delay is set to zero, \nkey.set_repeat is returned to the default, disabled state.\nTo set it to a minimum, essentially no delay, just set it to 1.\n\npygame.key.set_repeat(0,50) #Doesn't work.\npygame.key.set_repeat(1,50) #Works with essentially no delay.", "user_title": "David Khono Hackland", "datetimeon": "2010-03-02T22:20:03", "link": "pygame.key.set_repeat", "id": 3065}, {"content": "It appears that when the delay is set to zero, \nkey.set_repeat is returned to the default, disabled state.\nTo set it to a minimum, essentially no delay, just set it to 1.\n\npygame.key.set_repeat(0,50) #Doesn't work.\npygame.key.set_repeat(1,50) #Works with essentially no delay.", "user_title": "David Khono Hackland", "datetimeon": "2010-03-02T22:19:26", "link": "pygame.key.set_repeat", "id": 3064}, {"content": "Does it matter if you tick at the start or at the end?", "user_title": "Mitchell K", "datetimeon": "2009-09-12T21:09:15", "link": "Clock.tick", "id": 2944}, {"content": "The example's .flip(..) below won't work - maybe I should have checked it before posting...\nHere is a better Version, it should work now.\n\nfrom pygame import Rect, Surface\nclass Sprites():\n def __init__(self, spritesheet, size):\n self.sheet = spritesheet\n self.sheet.convert_alpha()\n self.size = size\n \n self.sprites = []\n for x in xrange(spritesheet.get_width() / size[0]):\n list = []\n for y in xrange(spritesheet.get_height() / size[1]):\n list.append(spritesheet.subsurface(Rect((x*size[0], y*size[1]) , size)))\n self.sprites.append(list)\n def flip(self, xbool, ybool):\n new = Surface(self.sheet.get_size())\n new.fill((0, 0, 0, 0))\n for row in self.sprites:\n for sprite in row:\n new.blit(flip(sprite, xbool, ybool), sprite.get_offset())\n self.sheet.fill((0, 0, 0, 0))\n self.sheet.blit(new, (0, 0))\n def __getitem__(self, x=None, y=None):\n # not very tested, .flip(y=7) won't work\n # the if conditions should allow you to access a sheet with one row/col more easily .flip(5, 0) == .flip(5)\n if x is not None:\n if y is None:\n if len(self.sprites) > x:\n y = 0\n else:\n y = x\n x = 0\n elif y is None:\n raise IndexError\n \n return self.sprites[x][y]\n\n@any Developer/Moderator - it would be nice if my wrong post, \"The example below won't work\" and this Notice would be removed.", "user_title": "Rolf Sievers", "datetimeon": "2009-11-06T11:31:59", "link": "Surface.subsurface", "id": 2998}, {"content": "Here is a simple Sprite-sheet Class I wrote for an application, maybe someone can use it.\n\nfrom pygame import Rect\nclass Sprites():\n def __init__(self, spritesheet, size):\n self.sheet = spritesheet\n self.sheet.convert_alpha()\n self.size = size\n \n self.sprites = []\n for x in xrange(spritesheet.get_width() / size[0]):\n list = []\n for y in xrange(spritesheet.get_height() / size[1]):\n list.append(spritesheet.subsurface(Rect((x*size[0], y*size[1]) , size)))\n self.sprites.append(list)\n print list\n print self.sprites\n def flip(self, xbool, ybool):\n self.sheet.fill((0, 0, 0, 0))\n for row in self.sprites:\n for sprite in row:\n sprite.blit(flip(sprite, xbool, ybool), (0, 0))\n def __getitem__(self, x=None, y=None):\n if x is not None:\n if y is None:\n if len(self.sprites) > x:\n y = 0\n else:\n y = x\n x = 0\n elif y is None:\n raise IndexError\n \n return self.sprites[x][y]", "user_title": "Rolf Sievers", "datetimeon": "2009-11-06T11:14:23", "link": "Surface.subsurface", "id": 2997}, {"content": "Is there anyway to get the rect of a polygon without having to create a surface greater than or equal to the polygon, and then gather the rect from the polygon?", "user_title": "Luke Endres", "datetimeon": "2009-08-08T21:50:46", "link": "pygame.draw.polygon", "id": 2907}, {"content": "This is twice the work because the image is rotated and then resized (subrect\nis copied) but it doesn't hurt my math-addled brain.\n\ndef rot_center(image, angle):\n \"\"\"rotate an image while keeping its center and size\"\"\"\n orig_rect = image.get_rect()\n rot_image = pygame.transform.rotate(image, angle)\n rot_rect = orig_rect.copy()\n rot_rect.center = rot_image.get_rect().center\n rot_image = rot_image.subsurface(rot_rect).copy()\n return rot_image", "user_title": "Gummbum", "datetimeon": "2010-01-17T01:17:23", "link": "pygame.transform.rotate", "id": 3034}, {"content": "Use pygame.transform.rotate(Surface, angle)", "user_title": "Francesco Pasa", "datetimeon": "2010-01-17T09:30:05", "link": "pygame.Surface", "id": 3035}, {"content": "Does the returned Boolean value indicate success/failure at toggling fullscreen mode,\nor current status of the display (e.g., fullscreen = True, windowed = False)?", "user_title": "Chris L", "datetimeon": "2010-01-27T16:33:12", "link": "pygame.display.toggle_fullscreen", "id": 3041}, {"content": "set_blocked() clear queue from ALL events", "user_title": "ploutos", "datetimeon": "2009-10-23T11:07:21", "link": "pygame.event.set_blocked", "id": 2980}, {"content": "If they play in a CD player, they were burned properly.", "user_title": "Jeffrey Aylesworth", "datetimeon": "2009-10-21T19:51:47", "link": "pygame.cdrom", "id": 2975}, {"content": "This is very important for mac because it shows the icon in the dock.. when I use this, it flashes the snake image for a second before changing, and it also gets smaller then the application icon which is the same (using py2app).", "user_title": "Mitchell K", "datetimeon": "2009-09-24T16:05:59", "link": "pygame.display.set_icon", "id": 2959}, {"content": "\"\"\"\n\t # This is a get_ticks() function simple example\n\t # This script should return 10 as a result\n\"\"\"\n# Standard library imports\nimport time\n# Related third party imports\nimport pygame\n#Pygame start function\n\npygame.init()\n# Create the clock\nclock = pygame.time.Clock()\n# A simple loop of 10 stages\nfor i in range(10):\n\t# Update the clock\n\tclock.tick(1)\n# Print the seconds\nprint int(round(pygame.time.get_ticks()/1000))", "user_title": "Sergio Milardovich", "datetimeon": "2010-06-08T15:33:04", "link": "pygame.time.get_ticks", "id": 3138}, {"content": "Note that pygame.scrap seems to be unimplemented in pygame-1.9.1.win32-py3.1.msi\n\nDefine testscrap.py, like this:\n\nimport pygame\npygame.init()\npygame.scrap.init()\n\n\nRun it, like this:\n\nC:\\Users\\Dave\\Documents\\Python>testscrap.py\nC:\\Users\\Dave\\Documents\\Python\\testscrap.py:3: RuntimeWarning: use scrap: No module named scrap\n(ImportError: No module named scrap)\n pygame.scrap.init()\nTraceback (most recent call last):\n File \"C:\\Users\\Dave\\Documents\\Python\\testscrap.py\", line 3, in \n pygame.scrap.init()\n File \"C:\\Python31\\lib\\site-packages\\pygame\\__init__.py\", line 70, in __getattr__\n raise NotImplementedError(MissingPygameModule)\nNotImplementedError: scrap module not available\n(ImportError: No module named scrap)", "user_title": "Dave Burton", "datetimeon": "2011-01-16T00:15:21", "link": "pygame.scrap", "id": 3731}, {"content": "BTW, the same error occurs if you have initialized a display surface, too:\n\nimport pygame\npygame.init()\nscreen=pygame.display.set_mode((640,360),0,32)\npygame.scrap.init()", "user_title": "Dave Burton", "datetimeon": "2011-01-16T00:18:19", "link": "pygame.scrap", "id": 3732}, {"content": "Hey, July 7 2009 Anonymous, that's a nice demo!\n\nFor Python 3 compatibility, just change the last line to:\n\n pygame.time.delay(1000//50)", "user_title": "Dave Burton", "datetimeon": "2011-01-24T10:00:06", "link": "Surface.subsurface", "id": 3746}, {"content": "Note that the order of the tuple members in virtual attributes like .topleft\nis always (x,y) [or (left,top) or (width,height)] even if the name of the\nvirtual attribute seems to suggest the opposite order. E.g.,\nrect1.topleft == (rect1.left,rect1.top)", "user_title": "Dave Burton", "datetimeon": "2011-01-25T01:19:14", "link": "pygame.Rect", "id": 3747}, {"content": "There's no 'code' member for type USEREVENT, unless you create one yourself\nwhen you create the event.", "user_title": "Dave Burton", "datetimeon": "2011-01-25T21:33:33", "link": "pygame.event", "id": 3750}, {"content": "July 15 2010 Anonymous, here's your example of a resizeable pygame window.\n\nI don't know what you mean by \"window itself as well as the display.\"\nIf you want to resize something WITHIN the pygame window, just blit something\ndifferent onto it.\n\nDave\n\n\nimport sys, os, pygame\npygame.init()\n\nclock = pygame.time.Clock()\n\nscrsize = width,height = 600,400\nblack = 0,0,0\nbgcolor = (240,240,220) # light grey\n\n# to get the true full-screen size, do this BEFORE pygame.display.set_mode:\nfullscreen_sz = pygame.display.Info().current_w, pygame.display.Info().current_h\nprint( 'screen size =', fullscreen_sz )\n\n\n# ---------- This works under Windows Vista, no promises elsewhere! ----------\n# initially center the pygame window by setting %SDL_VIDEO_WINDOW_POS%\nwin_pos_left = 1 + ((fullscreen_sz[0] - width) // 2)\nwin_pos_top = 1 + ((fullscreen_sz[1] - height) // 2)\nos.environ['SDL_VIDEO_WINDOW_POS'] = '{0},{1}'.format(win_pos_left, win_pos_top)\n# ----------------------------------------------------------------------------\n\nscreen = pygame.display.set_mode(scrsize, pygame.RESIZABLE)\n\n# ----------------------------------------------------------------------------\nos.environ['SDL_VIDEO_WINDOW_POS'] = ''\n# if you don't clear the environment variable, the window will reposition\n# every time pygame.display.set_mode() gets called due to a VIDEORESIZE event.\n# ----------------------------------------------------------------------------\n\narial = pygame.font.SysFont( 'arial,microsoftsansserif,courier', 14 )\ntxt2display = arial.render( \"This window is resizeable\", True, black )\ntxt2display_w = txt2display.get_size()[0]\n\nwhile True:\n changed = False\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n pygame.quit()\n sys.exit(0)\n elif event.type == pygame.VIDEORESIZE:\n scrsize = event.size # or event.w, event.h\n screen = pygame.display.set_mode(scrsize,RESIZABLE)\n changed = True\n\n screen.fill( bgcolor )\n screen.blit( txt2display, ((scrsize[0]+1-txt2display_w)//2,1) ) # at top-center of screen\n pygame.display.update()\n if not changed:\n clock.tick(60) # limit to 60 fps", "user_title": "Dave Burton", "datetimeon": "2011-01-25T23:10:35", "link": "pygame.display.init", "id": 3751}, {"content": "Oops! Tiny correction... the 8th-to-last line should be\n\n screen = pygame.display.set_mode(scrsize,pygame.RESIZABLE)\n\n(Or else you can \"from pygame.locals import *\")", "user_title": "Dave Burton", "datetimeon": "2011-01-25T23:16:00", "link": "pygame.display.init", "id": 3752}, {"content": "from pygame.locals import *\n\n_evnames = {} # from SDL-1.2.14\\include\\SDL_events.h\n_evnames[NOEVENT] = 'NOEVENT' # 0 SDL_NOEVENT\n_evnames[ACTIVEEVENT] = 'ACTIVEEVENT' # 1 SDL_ACTIVEEVENT\n_evnames[KEYDOWN] = 'KEYDOWN' # 2 SDL_KEYDOWN\n_evnames[KEYUP] = 'KEYUP' # 3 SDL_KEYUP\n_evnames[MOUSEMOTION] = 'MOUSEMOTION' # 4 SDL_MOUSEMOTION\n_evnames[MOUSEBUTTONDOWN] = 'MOUSEBUTTONDOWN' # 5 SDL_MOUSEBUTTONDOWN\n_evnames[MOUSEBUTTONUP] = 'MOUSEBUTTONUP' # 6 SDL_MOUSEBUTTONUP\n_evnames[JOYAXISMOTION] = 'JOYAXISMOTION' # 7 SDL_JOYAXISMOTION\n_evnames[JOYBALLMOTION] = 'JOYBALLMOTION' # 8 SDL_JOYBALLMOTION\n_evnames[JOYHATMOTION] = 'JOYHATMOTION' # 9 SDL_JOYHATMOTION\n_evnames[JOYBUTTONDOWN] = 'JOYBUTTONDOWN' # 10 SDL_JOYBUTTONDOWN\n_evnames[JOYBUTTONUP] = 'JOYBUTTONUP' # 11 SDL_JOYBUTTONUP\n_evnames[QUIT] = 'QUIT' # 12 SDL_QUIT\n_evnames[SYSWMEVENT] = 'SYSWMEVENT' # 13 SDL_SYSWMEVENT\n # 14 SDL_EVENT_RESERVEDA\n # 15 SDL_EVENT_RESERVEDB\n_evnames[VIDEORESIZE] = 'VIDEORESIZE' # 16 SDL_VIDEORESIZE\n_evnames[VIDEOEXPOSE] = 'VIDEOEXPOSE' # 17 SDL_VIDEOEXPOSE\n # 18 SDL_EVENT_RESERVED2\n # 19 SDL_EVENT_RESERVED3\n # 20 SDL_EVENT_RESERVED4\n # 21 SDL_EVENT_RESERVED5\n # 22 SDL_EVENT_RESERVED6\n # 23 SDL_EVENT_RESERVED7\n_evnames[USEREVENT] = 'USEREVENT' # 24 SDL_USEREVENT\n_evnames[NUMEVENTS] = 'NUMEVENTS' # 32 SDL_NUMEVENTS\n\n\ndef event_name(evtype):\n '''return a displayable name for a pygame/SDL event type number'''\n try:\n result = _evnames[evtype]\n except:\n if evtype in range(USEREVENT,NUMEVENTS):\n result = 'USEREVENT+' + repr(evtype-USEREVENT)\n elif evtype >= NUMEVENTS:\n result = 'ILLEGAL_EVENT_' + repr(evtype)\n elif evtype == 14:\n result = 'EVENT_RESERVEDA'\n elif evtype == 15:\n result = 'EVENT_RESERVEDB'\n else:\n result = 'EVENT_RESERVED' + repr(evtype-16)\n return result\n\n\nfor i in range(0,33):\n print(repr(i) + ' = ' + event_name(i))\n\n\n# It's all gonna change in SDL 1.3:\n#\n# SDL_FIRSTEVENT = 0 # Unused\n#\n# SDL_QUIT = 0x100 # User-requested quit\n#\n# SDL_WINDOWEVENT = 0x200 # Window state change\n# SDL_SYSWMEVENT = 0x201 # System specific event\n#\n# # Keyboard events\n# SDL_KEYDOWN = 0x300 # Key pressed\n# SDL_KEYUP = 0x301 # Key released\n# SDL_TEXTEDITING = 0x302 # Keyboard text editing (composition)\n# SDL_TEXTINPUT = 0x303 # Keyboard text input\n#\n# # Mouse events\n# SDL_MOUSEMOTION = 0x400 # Mouse moved\n# SDL_MOUSEBUTTONDOWN = 0x401 # Mouse button pressed\n# SDL_MOUSEBUTTONUP = 0x402 # Mouse button released\n# SDL_MOUSEWHEEL = 0x403 # Mouse wheel motion\n#\n# # Tablet or multiple mice input device events\n# SDL_INPUTMOTION = 0x500 # Input moved\n# SDL_INPUTBUTTONDOWN = 0x501 # Input button pressed\n# SDL_INPUTBUTTONUP = 0x502 # Input button released\n# SDL_INPUTWHEEL = 0x503 # Input wheel motion\n# SDL_INPUTPROXIMITYIN = 0x504 # Input pen entered proximity\n# SDL_INPUTPROXIMITYOUT = 0x505 # Input pen left proximity\n#\n# # Joystick events\n# SDL_JOYAXISMOTION = 0x600 # Joystick axis motion\n# SDL_JOYBALLMOTION = 0x601 # Joystick trackball motion\n# SDL_JOYHATMOTION = 0x602 # Joystick hat position change\n# SDL_JOYBUTTONDOWN = 0x603 # Joystick button pressed\n# SDL_JOYBUTTONUP = 0x604 # Joystick button released\n#\n# # Touch events\n# SDL_FINGERDOWN = 0x700\n# SDL_FINGERUP = 0x701\n# SDL_FINGERMOTION = 0x702\n# SDL_TOUCHBUTTONDOWN = 0x703\n# SDL_TOUCHBUTTONUP = 0x704\n#\n# # Gesture events\n# SDL_DOLLARGESTURE = 0x800\n# SDL_DOLLARRECORD = 0x801\n# SDL_MULTIGESTURE = 0x802\n#\n# # Clipboard events\n# SDL_CLIPBOARDUPDATE = 0x900 # The clipboard changed\n#\n# # Obsolete events\n# SDL_EVENT_COMPAT1 =0x7000 # SDL 1.2 events for compatibility\n# SDL_EVENT_COMPAT2 =0x7001\n# SDL_EVENT_COMPAT3 =0x7002\n#\n# # SDL_USEREVENT thru SDL_LASTEVENT are for your use\n# SDL_USEREVENT =0x8000\n# SDL_LASTEVENT =0xFFFF", "user_title": "Dave Burton", "datetimeon": "2011-01-27T04:08:06", "link": "pygame.event.Event", "id": 3753}, {"content": "This function seems to me little bit buggy, so I wrote my own:\n\na and b are surfarrays of some surfaces that you want to compare\n\n def comparray(self,a,b):\n c = abs(a.__sub__(b))\n c = c.__ge__(self.tolerance)*255\n surface = pygame.surfarray.make_surface(c)\n return surface", "user_title": "Kaan Ak\u00c3\u009fit", "datetimeon": "2011-01-27T18:49:33", "link": "PixelArray.compare", "id": 3756}, {"content": "There's an error in this documentation w/r/t the final (width) argument:\n\n pygame.draw.rect(self.image, color, self.image.get_rect(), width=1)\nTypeError: rect() takes no keyword arguments\n\nLeave off the \"width=\" to make it work:\n\n pygame.draw.rect(self.image, color, self.image.get_rect(), 1)\n\nThis is with either pygame-1.9.1.win32-py2.6.msi or pygame-1.9.1.win32-py3.1.msi", "user_title": "Dave Burton", "datetimeon": "2011-01-28T03:12:31", "link": "pygame.draw.rect", "id": 3757}, {"content": "There's an error in this documentation w/r/t the final (width) argument:\n\n pygame.draw.line(self.image, (0,0,0), (x,y), (x,y+h), width=2)\nTypeError: line() takes no keyword arguments\n\nLeave off the \"width=\" to make it work:\n\n pygame.draw.line(self.image, (0,0,0), (x,y), (x,y+h), 2)\n\nThis is with either pygame-1.9.1.win32-py2.6.msi or pygame-1.9.1.win32-py3.1.msi", "user_title": "Dave Burton", "datetimeon": "2011-01-28T04:54:01", "link": "pygame.draw.line", "id": 3759}, {"content": "Rect.center rounds UP:\n\nr0x0 = pygame.Rect(0,0,0,0) # a 0x0 rect\nprint('center of 0x0 rect is ' + repr(r0x0.center)) # result is (0,0) = not in the rect!\nr1x1 = pygame.Rect(0,0,1,1) # a 1x1 rect\nprint('center of 1x1 rect is ' + repr(r1x1.center)) # result is (0,0) = correct\nr2x2 = pygame.Rect(0,0,2,2) # a 2x2 rect\nprint('center of 2x2 rect is ' + repr(r2x2.center)) # result is (1,1) = rounded up!\nr3x3 = pygame.Rect(0,0,3,3) # a 3x3 rect\nprint('center of 3x3 rect is ' + repr(r3x3.center)) # result is (1,1) = exact\nr4x4 = pygame.Rect(0,0,4,4) # a 4x4 rect\nprint('center of 4x4 rect is ' + repr(r4x4.center)) # result is (2,2) = rounded up!\nr5x5 = pygame.Rect(0,0,5,5) # a 5x5 rect\nprint('center of 5x5 rect is ' + repr(r5x5.center)) # result is (2,2) = exact\nr6x6 = pygame.Rect(0,0,6,6) # a 6x6 rect\nprint('center of 6x6 rect is ' + repr(r6x6.center)) # result is (3,3) = rounded up!\nr7x7 = pygame.Rect(0,0,7,7) # a 7x7 rect\nprint('center of 7x7 rect is ' + repr(r7x7.center)) # result is (3,3) = exact", "user_title": "Dave Burton", "datetimeon": "2011-01-29T20:46:36", "link": "Rect.collidepoint", "id": 3761}, {"content": "(Oops, I added that comment in the wrong place.)", "user_title": "Dave Burton", "datetimeon": "2011-01-29T20:47:58", "link": "Rect.collidepoint", "id": 3762}, {"content": "Rect.center rounds UP:\n\nr0x0 = pygame.Rect(0,0,0,0) # a 0x0 rect\nprint('center of 0x0 rect is ' + repr(r0x0.center)) # result is (0,0) = not in the rect!\nr1x1 = pygame.Rect(0,0,1,1) # a 1x1 rect\nprint('center of 1x1 rect is ' + repr(r1x1.center)) # result is (0,0) = correct\nr2x2 = pygame.Rect(0,0,2,2) # a 2x2 rect\nprint('center of 2x2 rect is ' + repr(r2x2.center)) # result is (1,1) = rounded up!\nr3x3 = pygame.Rect(0,0,3,3) # a 3x3 rect\nprint('center of 3x3 rect is ' + repr(r3x3.center)) # result is (1,1) = exact\nr4x4 = pygame.Rect(0,0,4,4) # a 4x4 rect\nprint('center of 4x4 rect is ' + repr(r4x4.center)) # result is (2,2) = rounded up!\nr5x5 = pygame.Rect(0,0,5,5) # a 5x5 rect\nprint('center of 5x5 rect is ' + repr(r5x5.center)) # result is (2,2) = exact\nr6x6 = pygame.Rect(0,0,6,6) # a 6x6 rect\nprint('center of 6x6 rect is ' + repr(r6x6.center)) # result is (3,3) = rounded up!\nr7x7 = pygame.Rect(0,0,7,7) # a 7x7 rect\nprint('center of 7x7 rect is ' + repr(r7x7.center)) # result is (3,3) = exact", "user_title": "Dave Burton", "datetimeon": "2011-01-29T20:48:50", "link": "pygame.Rect", "id": 3763}, {"content": "This documentation is incorrect. A point along the right or bottom edge IS\nwithin the Rect, and points at coordinates on the bottom or right edge DO\ncollide with the Rect.\n\nHere's proof:\n\nr = Rect(0,0, 4,4) # a 4x4 rectangle\nprint('0,0: ' + repr(r.collidepoint(0,0)))\nprint('1,1: ' + repr(r.collidepoint(1,1)))\nprint('2,2: ' + repr(r.collidepoint(2,2)))\nprint('3,3: ' + repr(r.collidepoint(3,3)))\nprint('4,4: ' + repr(r.collidepoint(4,4)))\n\nWith pygame 1.9.1 under both Python 3.1 and 2.6, it prints:\n\n0,0: 1\n1,1: 1\n2,2: 1\n3,3: 1\n4,4: 0\n\nNote that the bottom-right pixel within the 4x4 rect is at (3,3) and\ncollidepoint((3,3)) does return 1 (meaning true).\n\nA second (minor) documentation error is that it actually returns an integer\n1 or 0 instead of boolean True or False.", "user_title": "Dave Burton", "datetimeon": "2011-01-29T22:35:18", "link": "Rect.collidepoint", "id": 3764}, {"content": "This class is a bit odd. Event objects have no event.__dict__ attribute,\nand the dir(event) function doesn't work. However, repr(event) returns a\nnice, thorough description of an event object and its attributes, and the\nevent.dict attribute lists all the important attributes except .type and\n.dict itself.", "user_title": "Dave Burton", "datetimeon": "2011-02-07T04:48:40", "link": "pygame.event", "id": 3774}, {"content": "With a Microsoft IntelliMouse p/n X05-77975, under Windows Vista,\nwith either Python 2.6 or 3.1, the button numbers are:\n1 = left button\n2 = center button/wheel press\n3 = right button\n4 = wheel roll forward/up\n5 = wheel roll backward/down\n6 = left side extra button\n7 = right side extra button", "user_title": "Dave Burton", "datetimeon": "2011-02-23T21:04:46", "link": "pygame.event", "id": 3794}, {"content": "There's a cursor missing! sizer_xy_strings defines an upper-left-to-lower-right\nresizer cursor, suitable for dragging the upper-left or lower-right corner.\nBut there's no sizer_yx_strings to make the upper-right-to-lower-left cursor.\nHere's how I made one:\n\nsizer_yx_strings = [ x[12::-1]+x[13:] for x in pygame.cursors.sizer_xy_strings ]", "user_title": "Dave Burton", "datetimeon": "2011-02-24T02:08:27", "link": "pygame.cursors", "id": 3795}, {"content": "Or, equivalently:\n\nsizer_yx_strings = ( #sized 24x16\n \" XXXXXXXX \",\n \" X.....X \",\n \" X....X \",\n \" X...X \",\n \" X.X..X \",\n \" X.X X.X \",\n \"X X.X XX \",\n \"XX X.X X \",\n \"X.XX.X \",\n \"X...X \",\n \"X...X \",\n \"X....X \",\n \"X.....X \",\n \"XXXXXXXX \",\n \" \",\n \" \",\n)", "user_title": "Dave Burton", "datetimeon": "2011-02-24T02:13:59", "link": "pygame.cursors", "id": 3796}, {"content": "The Sound function now accepts 'buffer', 'file', and 'array' keyword arguments\nto remove any ambiguity in how to treat an argument. The 'array' keyword is new,\nand tells Sound to look check the argument for an array struct interface or\nthe new buffer protocol if supported. This allows Sound to function like\nsndarray.make_sound.\n\nSound also exposes an array struct interface and the new buffer protocol.", "user_title": "Lenard Lindstrom", "datetimeon": "2011-03-01T13:26:33", "link": "pygame.mixer.Sound", "id": 3799}, {"content": "I'm not sure which version of Pygame is being used here, 1.9? At the time it was\nreleased NumPy was unavailable for Python 3.1. Python 1.9.2 alpha from SVN\ncertainly does support NumPy for Python 3.1, and 3.2.", "user_title": "Lenard Lindstrom", "datetimeon": "2011-03-01T13:35:12", "link": "pygame.surfarray", "id": 3800}, {"content": "That should be \"which version of Pygame is being used here, 1.9.1?\"", "user_title": "Lenard Lindstrom", "datetimeon": "2011-03-01T13:37:12", "link": "pygame.surfarray", "id": 3801}, {"content": "New to Pygame 1.9.2 for NumPy: pixels_red, pixels_green, and pixels_blue.", "user_title": "Lenard Lindstrom", "datetimeon": "2011-03-01T13:38:40", "link": "pygame.surfarray.pixels_alpha", "id": 3802}, {"content": "For the KEYDOWN and KEYUP event \"scancode\" is also a member and can be used \nfor the unknown keys", "user_title": "Daniel Kaminsky", "datetimeon": "2011-03-23T05:51:58", "link": "pygame.event", "id": 3872}, {"content": "The wheel generates pygame.MOUSEBUTTONUP events too, not just pygame.MOUSEBUTTONDOWN event.", "user_title": "Dan Ross", "datetimeon": "2011-04-02T23:30:45", "link": "pygame.mouse", "id": 3884}, {"content": "Forget what the functions do, check out Mr. Brown's naming style. Its pure genius!\n1) angle_times_WOW_pi_divided_by_180\n2) HE_HE_strange_popper_z\n3) buffy_the_fat2\n4) they_did_touch\n5) while Grr < LIN_collide_max:\n6) Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol\n7) write_to_file_WEEE_STRANGE()\n8) freaky_rect_switcharoo_2D()", "user_title": "Mad Cloud Games", "datetimeon": "2011-04-03T18:28:23", "link": "pygame.draw.circle", "id": 3885}, {"content": "If you like to receive the inner rectangle, the blit is a much better setup. \nThe following comparing examples show how-to cut a (centered) 150x150 frame out of a 250x250 image:\norig_surf = pygame.Surface((250,250),flags=pygame.SRCALPHA)\npygame.draw.circle(orig_surf,(255,0,0),(50,50),25)\npygame.draw.circle(orig_surf,(0,255,0),(50,200),25)\npygame.draw.circle(orig_surf,(0,0,255),(200,50),25)\npygame.draw.circle(orig_surf,(0,255,255),(200,200),25)\n\ncrop_surf = pygame.transform.chop(pygame.transform.chop(orig_surf,(0,0,50,50)),(150,150,250,250))\npygame.image.save(crop_surf, 'test-crop.png')\n\n\ncrop_surf = pygame.Surface((150,150),flags=pygame.SRCALPHA)\ncrop_surf.blit(orig_surf, (0,0),(50,50,200,200))\npygame.image.save(crop_surf, 'test-blit.png')", "user_title": "Rick van der Zwet", "datetimeon": "2011-05-05T04:36:44", "link": "pygame.transform.chop", "id": 4045}, {"content": "It all seemed simple and working properly, then I noticed... \"The area covered by a Rect does not include the right- and bottom-most edge of pixels. If one Rect's bottom border is another Rect's top border (i.e., rect1.bottom=rect2.top), the two meet exactly on the screen but do not overlap, and rect1.colliderect(rect2) returns false.\"\n\n*mutter* good to know.", "user_title": "Anonymous", "datetimeon": "2011-01-10T19:28:58", "link": "Rect.colliderect", "id": 3725}, {"content": "Note that when the user resizes the game window, pygame does not automatically update its internal screen surface. You must call set_mode() every time VIDEORESIZE is sent. This really should be more clear in the documentation.", "user_title": "Anonymous", "datetimeon": "2011-01-11T15:55:57", "link": "pygame.display", "id": 3726}, {"content": "Is it possible to set this mode transparent?\nI mean without changing the transparency with set_alpha or ... but from the beginning.", "user_title": "Anonymous", "datetimeon": "2011-01-13T08:28:22", "link": "pygame.display.set_mode", "id": 3727}, {"content": "Draw a normal thick line, then draw two aa lines either side. Not exactly what you want but it will work.", "user_title": "Anonymous", "datetimeon": "2011-01-13T15:22:43", "link": "pygame.draw.aaline", "id": 3728}, {"content": "This code fixes the bad rect given by the line function.\n\ntemprect=(pygame.draw.line(screen,color,firstpos,newpos,thick))\ntemprect.inflate_ip(thick*2, thick*2)\ndirty.append(temprect)", "user_title": "Anonymous", "datetimeon": "2005-11-22T22:22:44", "link": "pygame.draw.line", "id": 8}, {"content": "if your rect contains a negative width or height you need to rect.normalize() your rect before passing it to this function", "user_title": "Anonymous", "datetimeon": "2005-11-27T22:45:10", "link": "pygame.draw.ellipse", "id": 13}, {"content": "Rotates image about its center.", "user_title": "Anonymous", "datetimeon": "2005-11-28T19:22:44", "link": "pygame.transform.rotate", "id": 14}, {"content": "Make sure you blit according to the center of the newly formed surface, and not what the center of the orginal image is.", "user_title": "Anonymous", "datetimeon": "2005-11-28T19:24:48", "link": "pygame.transform.rotate", "id": 15}, {"content": "This probably goes without saying, but always rotate the orginal image, not a rotated copy.", "user_title": "Anonymous", "datetimeon": "2005-11-28T19:26:45", "link": "pygame.transform.rotate", "id": 16}, {"content": "Before calling pygame.key.get_pressed(), one should call pygame.event.pump() to get the lates state of the keyboard.\n\nThis is so because the get_pressed() function wraps the SDL_GetKeyState() function and in the SDL_GetKeyState() documentation it is written that one should use SDL_PumpEvents() to update the state array and pygame.event.pump() just happens to be a wrapper for SDL_PumpEvents() :-)", "user_title": "Anonymous", "datetimeon": "2005-12-01T10:30:49", "link": "pygame.key.get_pressed", "id": 18}, {"content": "When I tryed to use this, he couldn't find the key K_t I wanted\n untill I used:\n\nfrom pygame.locals import *\n\nSo be sure to use it - Shefy", "user_title": "Anonymous", "datetimeon": "2005-12-07T04:09:33", "link": "pygame.key.get_pressed", "id": 19}, {"content": "if you pass in None as the background argument, you get the error\n\"TypeError: Invalid background RGBA argument\"", "user_title": "Anonymous", "datetimeon": "2005-12-10T19:21:13", "link": "Font.render", "id": 22}, {"content": "pygame.event.pump()\n m = pygame.key.get_mods()\n if m & KMOD_SHIFT:\n print 'shift pressed'", "user_title": "Anonymous", "datetimeon": "2005-12-25T19:36:47", "link": "pygame.key.get_pressed", "id": 32}, {"content": "Rotated objects tend to move around because bounding rectangle changes size.\nStore the center in a temporary variable, then rotate the original image, and finally reset the center before you blit or update\nThis code comes from a sprite class:\n\n def turn(self, amount):\n \"turn some amount\"\n oldCenter = self.rect.center\n self.dir += amount\n self.image = pygame.transform.rotate(self.baseImage, self.dir)\n self.rect = self.image.get_rect()\n self.rect.center = oldCenter", "user_title": "Anonymous", "datetimeon": "2006-01-03T09:48:09", "link": "pygame.transform.rotate", "id": 36}, {"content": "This effect (1 + 3 = 2) is caused by your X.org/XServer mouse configuration section, which allows to emulate the middle button by clicking both the left and right mouse button at the same time.", "user_title": "Anonymous", "datetimeon": "2006-01-04T09:16:25", "link": "pygame.mouse.get_pressed", "id": 37}, {"content": "This does not result in 'truly' transparent text, as the area between the letters is filled in with the background color. For truly transparent text with an invisible background behind the letters, use Numeric:\n\ndef RenderTransparent(font, text, antialias=1, color=(255, 0, 0, 0)):\n 'Render text with transparency underneath the letters'\n 'Requires Numeric'\n\n # Create a colored block big enough to hold the text\n w, h = font.size(text)\n surface = pygame.Surface((w, h), pygame.SRCALPHA)\n surface.fill(color)\n \n # Create an alpha channel that contains the shapes of the letters\n alpha = pygame.Surface((w, h), pygame.SRCALPHA)\n WHITE = (255, 255, 255, 0)\n BLACK = (0, 0, 0, 0)\n a = font.render(text, antialias, WHITE, BLACK)\n alpha.blit(a, (0, 0))\n \n # Combine the alpha channel with the colored block\n pic = surface.convert_alpha()\n mask = alpha.convert(32)\n mskarray = pygame.surfarray.pixels3d(mask)\n pygame.surfarray.pixels_alpha(pic)[:, :] = mskarray[:, :, 0]\n\n # Return the 'truly' transparent text.\n return pic", "user_title": "Anonymous", "datetimeon": "2006-01-17T14:45:02", "link": "Font.render", "id": 41}, {"content": "LOL", "user_title": "Anonymous", "datetimeon": "2011-01-03T19:03:40", "link": "PixelArray.replace", "id": 3700}, {"content": "FUCKING SPAMMER MOTHER FUCKERS WHO OWNS THIS SHIT THEY SHOULD BURN IN HELLL", "user_title": "Anonymous", "datetimeon": "2011-01-03T19:05:09", "link": "pygame.locals", "id": 3701}, {"content": "On my Windows Vista machine running Python 3.1.2 and pygame 1.9.1,\nthe 'black=' and 'white=' parameters are swapped.\n\nSo, to make the example work (with a black arrow outline\naround a white center), you have to do this:\n\nthickarrow_strings = ( #sized 24x24\n \"XX \",\n \"XXX \",\n \"XXXX \",\n \"XX.XX \",\n \"XX..XX \",\n \"XX...XX \",\n \"XX....XX \",\n \"XX.....XX \",\n \"XX......XX \",\n \"XX.......XX \",\n \"XX........XX \",\n \"XX........XXX \",\n \"XX......XXXXX \",\n \"XX.XXX..XX \",\n \"XXXX XX..XX \",\n \"XX XX..XX \",\n \" XX..XX \",\n \" XX..XX \",\n \" XX..XX \",\n \" XXXX \",\n \" XX \",\n \" \",\n \" \",\n \" \")\n\ndatatuple, masktuple = pygame.cursor.compile( thickarrow_strings,\n black='.', white='X', xor='o' )\npygame.mouse.set_cursor( (24,24), (0,0), datatuple, masktuple )", "user_title": "Anonymous", "datetimeon": "2011-01-04T09:45:11", "link": "pygame.cursors.compile", "id": 3702}, {"content": "I'm using this generator to get a channel id for each sprite:\n\ndef free_sound_channel():\n \"\"\"Get next available sound channel\n Usage:\n free_channels=free_sound_channel()\n id=free_channels.next()\n \"\"\"\n id=0\n while id<pygame.mixer.get_num_channels():\n yield id\n id+=1\n return # or: raise StopIteration()", "user_title": "Anonymous", "datetimeon": "2006-01-29T16:18:15", "link": "pygame.mixer.Channel", "id": 49}, {"content": "COLORKEY and ALPHA should have 'SRC' prefixed to them. Here is a more-complete list of flags revelvant to surface.get_flags():\n,\"SRCCOLORKEY\"\n,\"RLEACCEL\"\n,\"RLEACCELOK\"\n,\"PREALLOC\"\n,\"HWACCEL\"\n,\"SRCALPHA\"\n,\"UYVY_OVERLAY\"\n,\"YV12_OVERLAY\"\n,\"YVYU_OVERLAY\"\n,\"YUY2_OVERLAY\"\n,\"HWPALETTE\"\nSWSURFACE - not really usable as a surface flag, equates to 0 and is always default\nANYFORMAT - used to create surfaces, pygame defaults to this flag if you don't specifya bit depth\nHWACCEL - surface is hardware accelerated, readonly\nSRCCOLORKEY- surface has a colorkey for blits, readonly\nSRCALPHA - surface has alpha enabled, readonly\nRLEACCELOK - surface is rle accelerated, but hasn't been compiled yet, readonly\nPREALLOC - not even sure?\nHope this helps....", "user_title": "Anonymous", "datetimeon": "2006-02-07T21:37:24", "link": "Surface.get_flags", "id": 52}, {"content": "I wish all the possible flags were documented here...", "user_title": "Anonymous", "datetimeon": "2006-02-07T22:02:57", "link": "pygame.Surface", "id": 53}, {"content": "I don't know what is wrong with you two. I tested the following and it worked as expected. Perhaps it is because I tested it on windows, if you tested it somewhere else (of course that's not the likely cause but I really can't see what else is wrong).\n\nIt is true that passing None for the final argument causes \"Invalid RGBA argument\". This is a bug in the documentation, not the code. The proper way to get transparency is to simply omit the last argument.\n\n$python\n>>>import pygame\n>>>pygame.init()\n>>>screen = pygame.display.set_mode((300,300))\n>>>screen.fill((255,0,0))\n>>>pygame.display.flip()\n\n>>>font = pygame.font.SysFont(\"Times New Roman\",30)\n>>>s = font.render(\"Eggs are good for you, but not on the eiffel tower\",True,(0,255,255))\n>>>s.get_flags() #-> 65536 [SRCALPHA].. good, implies the image has per-pixel transparency\n>>>[s.get_at((i,j)) for i in range(20) for j in range(20)]\n[.... #here we see that indeed each\n(0,255,255,68) #pixel is a full RGBA pixel with 4\n....] #components.\n>>>screen.blit(s, (0,0))\n>>>pygame.display.flip()\n>>>pygame.event.pump() #in order to bring the window back to life...\n\nAnd the result is turquoise text with red in the background, clearly showing transparency. Phew, you had me worried there, thinking I couldn't do transparency with this... until I looked closer. These docs are shiny but can be very hard to read sometimes.", "user_title": "Anonymous", "datetimeon": "2006-02-08T02:29:13", "link": "Font.render", "id": 54}, {"content": "Here's another solution for creating surfaces with per-pixel-alpha:\n\nimage = pygame.Surface((width,height),pygame.SRCALPHA,32);\n\nAdding the depth argument '32' seems to make this work every time.", "user_title": "Anonymous", "datetimeon": "2006-02-21T15:49:19", "link": "pygame.Surface", "id": 58}, {"content": "In the documentery it says \"The antialias argument is a boolean, if true the \ncharacters will have smooth edges.\". If you pass a string as the antialias \nargument it raises an exception saying \"TypeError: an integer is required\". This\nis very confusing. It should raise \"TypeError: a boolean is required\". \nIf antialias is enabled it will greatly drop the framerate (from 100 to 33 on my\nmachine). Font.render should be called only for as many times as you need fonts.\nDo not call this function every gameloop for it will greatly drop the framerate.\n(this cost me about 2 houres of debugging to find out.)\nIf any admins read this: Please change the script so that long lines will be seperated to shorter lines. Those 500+ words lines are uncomfortable to read with all that scrolling. mfg nwp.", "user_title": "Anonymous", "datetimeon": "2006-03-05T13:28:14", "link": "Font.render", "id": 61}, {"content": "you may want to initalise the \ndifferent modules seperately\nto speed up your program. Of \ncourse, then you would need \nto know which modules you have\ninitalised and which ones you\nhave not.", "user_title": "Anonymous", "datetimeon": "2006-03-08T22:55:41", "link": "pygame.init", "id": 64}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-30T07:59:42", "link": "Hope to find some more useful information on your site! It is really great!", "id": 196}, {"content": "format of music files\non cds are (usualy) in\nCD Digital Audio, except\nsometimes a program will\nmake a cd useing a \ndifferent format, so \npygame.cdrom.CD(n).play()\nwill maby not play it.", "user_title": "Anonymous", "datetimeon": "2006-03-08T23:02:35", "link": "pygame.cdrom", "id": 67}, {"content": "import pygame\nfrom pygame.locals import *\n\npygame.init()\npygame.display.set_mode((300,200))\npygame.display.set_caption('Mouse Input Demonstration')\nrunning = True\nwhile running:\n for event in pygame.event.get():\n if event.type == QUIT:\n running = False\n if event.type == KEYDOWN and event.key == K_ESCAPE:\n running = False\n if event.type == MOUSEBUTTONDOWN:\n print event.button\n\npygame.display.quit()", "user_title": "Anonymous", "datetimeon": "2006-04-02T00:38:08", "link": "pygame.mouse.get_pressed", "id": 82}, {"content": "# An Example from perldude69@gmail.com www.wachadoo.com/forum/\n# CONSTANTS\nSCREEN_WIDTH = 800\nSCREEN_HEIGHT = 600\n#Initialise Game\npygame.init()\nscreen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT))\npygame.display.set_caption('Space Invaders')\nbackground = pygame.image.load('./pics/background1.jpg').convert()\nbackground = pygame.transform.scale(background,( SCREEN_WIDTH, SCREEN_HEIGHT))\nscreen.blit(background, (0,0)) \npygame.display.flip() \ndone = False\nwhile not done:\n\tfor e in pygame.event.get():\n\t\tif e.type == KEYDOWN:\n\t\t\tdone = True\nif __name__ == \"__main__\":\n main()", "user_title": "Anonymous", "datetimeon": "2006-04-07T18:04:11", "link": "pygame.transform.scale", "id": 84}, {"content": "Don't specify flags unless you absolutely *must* (that is, don't specify HWSURFACE, depth=32 just because you think it's a good idea). This will reduce the portability of your game.", "user_title": "Anonymous", "datetimeon": "2006-06-13T21:27:25", "link": "pygame.display.set_mode", "id": 98}, {"content": "Could someone please post the integer values corresponding to the various shift/ctl/alt keys? Or provide a link.\nthank you!", "user_title": "Anonymous", "datetimeon": "2006-12-18T16:29:25", "link": "pygame.key.get_mods", "id": 183}, {"content": "numpy is fine in Python 3.1.2. However, pygame.surfarray doesn't work\nat all in pygame-1.9.1.win32-py3.1.msi with python-3.1.2.msi and\nnumpy-1.5.1-win32-superpack-python3.1.exe under Windows Vista.\n\nTo see the problem, just run the test that comes with it; 4 of 14 tests fail:\n\nC:\\>cd \\python31\\lib\\site-packages\\pygame\\tests\n\nC:\\Python31\\Lib\\site-packages\\pygame\\tests>\\python31\\python surfarray_test.py\nEE.EE.........\n======================================================================\nERROR: test_array2d (__main__.SurfarrayModuleTest)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"surfarray_test.py\", line 147, in test_array2d\n arr = pygame.surfarray.array2d(surf)\n File \"C:\\python31\\lib\\site-packages\\pygame\\surfarray.py\", line 104, in array2d\n return numpysf.array2d (surface)\n File \"C:\\python31\\lib\\site-packages\\pygame\\_numpysurfarray.py\", line 77, in array2d\n data = ''.join (pattern.findall (data))\nTypeError: can't use a string pattern on a bytes-like object\n[...snip...]", "user_title": "Anonymous", "datetimeon": "2011-01-07T03:47:28", "link": "pygame.surfarray", "id": 3704}, {"content": "no fill?", "user_title": "Anonymous", "datetimeon": "2006-11-19T14:38:36", "link": "pygame.draw.arc", "id": 163}, {"content": "works perfectly fine for me... question: what's the name of the overloaded operator that does the pxarray[x,y] subscripting?", "user_title": "Anonymous", "datetimeon": "2010-12-23T18:28:10", "link": "pygame.PixelArray", "id": 3689}, {"content": "pygame.cursors.ball is also a cool one.", "user_title": "Anonymous", "datetimeon": "2010-12-28T16:21:48", "link": "pygame.cursors", "id": 3690}, {"content": "Its a success/failure scenario. It returns True (1) if it went well.", "user_title": "Anonymous", "datetimeon": "2010-12-30T05:47:55", "link": "pygame.display.toggle_fullscreen", "id": 3691}, {"content": "These appear to be in degrees rather than radians (different than how draw.arc()'s are specified) which is kind of inconsistent. Are these documented better elsewhere?", "user_title": "Anonymous", "datetimeon": "2011-01-01T15:04:01", "link": "pygame.gfxdraw.pie", "id": 3694}, {"content": "You need to put\nimport pygame\nat the top of your program, anonymous.", "user_title": "Anonymous", "datetimeon": "2011-01-02T07:03:47", "link": "pygame.display", "id": 3695}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-28T17:27:45", "link": "Looking for information and found it at this great site...", "id": 188}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-29T02:41:08", "link": "I love the whiiite suits! Great show!", "id": 189}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-29T10:09:22", "link": "Thank you for your site. I have found here much useful information...", "id": 190}, {"content": "I had this weird thing where blue/red was inversed, but not the other colours, when I was mapping some pixels from one image to a blank surface.\nIt was caused by copying the color integer directly to one pixel to the other, so the trick is to always surface.unmap_rgb(pixel) before setting the color to a new pixel", "user_title": "Anonymous", "datetimeon": "2010-12-13T21:22:42", "link": "pygame.PixelArray", "id": 3685}, {"content": ".", "user_title": "Anonymous", "datetimeon": "2010-12-17T14:01:47", "link": "Movie.play", "id": 3687}, {"content": "import pygame, sys\nfrom pygame.version import ver\nprint (\"pygame \", ver)\nstartstate = pygame.init()\nprint (\"{pygame.init()}\", startstate)\nscreen = pygame.display.set_mode([640, 480])\nprint (\"{pygame.display.set_mode([640, 480]}\", screen)\nwhile True:\n for event in pygame.event.get():\n if not event:\n print (\"Event processing error: cannot find event.\")\n elif event.type == pygame.QUIT or event.type == pygame.K_ESCAPE:\n print (\"{for event in pygame.event.get():} : \", event)\n sys.exit()\nsys.exit() command does not run when I press escape, all it does is the same as if not event.", "user_title": "Anonymous", "datetimeon": "2010-12-18T17:09:49", "link": "pygame.key", "id": 3688}, {"content": "It the range for H should only be [0, 360); at exactly 360 the expression throws an OverflowError. The other ranges are not affected as such.", "user_title": "Anonymous", "datetimeon": "2010-12-08T17:55:35", "link": "Color.hsva", "id": 3677}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-30T22:35:22", "link": "Very cool design! Useful information. Go on!", "id": 202}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-31T05:52:59", "link": "Very interesting! site. A must bookmark! I wait for continuation", "id": 203}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-31T13:03:40", "link": "Very interesting! site. A must bookmark! I wait for continuation", "id": 204}, {"content": "", "user_title": "Anonymous", "datetimeon": "2006-12-31T19:55:09", "link": "Just wanted to say you have some happyY looking walkers. All natural!", "id": 205}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-01T16:59:55", "link": "You have an outstanding good and well structured site. I enjoyed browsing through it.", "id": 208}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-02T00:19:19", "link": "I love the whiiite suits! Great show!", "id": 211}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-02T15:12:24", "link": "Hope to find some more useful information on your site! It is really great!", "id": 212}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-02T22:17:19", "link": "Just wanted to say you have some happyY looking walkers. All natural!", "id": 213}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-03T05:35:22", "link": "You have a great site. All in your web is very useful. Please keep on working.", "id": 214}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-03T12:53:55", "link": "Pretty nice site, wants to see much more on it! :)", "id": 215}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-03T20:02:38", "link": "You have an outstanding good and well structured site. I enjoyed browsing through it.", "id": 216}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-04T18:10:07", "link": "This site is asomeee, well done, thanks for all!", "id": 218}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-01-05T01:15:13", "link": "Very nice site. Keep up the great work.", "id": 219}, {"content": "Left:\nctrl 4160 \nshift 4097 \nalt 4352 \n\nRight:\nctrl 4224\nshift 4098\nalt 4608\n\nLeft:\nc+s 4161\nc+a 4416\na+s 4353\n\nRight:\nc+s 4226\nc+a 4736\na+s 4610\n\nDone by hand ;)\n\n-Jabapyth", "user_title": "Anonymous", "datetimeon": "2007-01-29T17:22:04", "link": "pygame.key.get_mods", "id": 316}, {"content": "Depending on your keyboard there may be limitations of how many simultaneous keypresses can be detected by this command. Some combinations will work on one keyboard and not on another.", "user_title": "Anonymous", "datetimeon": "2007-02-08T19:53:05", "link": "pygame.key.get_pressed", "id": 335}, {"content": "Just use the same line width as your radius.\nThis of course dosn't solve your problem if you want a border on your arc, but then you can just paint twice.", "user_title": "Anonymous", "datetimeon": "2007-02-26T18:27:17", "link": "pygame.draw.arc", "id": 374}, {"content": "It looks like width is not a keyword argument, but a required/positional/whatever one instead.\n\n>>> pygame.draw.line(surf, color, (x1, y1), (x2, y2), width=width)\nTraceback (most recent call last):\n File \"\", line 1, in ?\nTypeError: line() takes no keyword arguments\n>>> pygame.draw.line(surf, color, (x1, y1), (x2, y2))\n\n>>> pygame.draw.line(surf, color, (x1, y1), (x2, y2), 1)", "user_title": "Anonymous", "datetimeon": "2007-03-01T11:28:42", "link": "pygame.draw.line", "id": 386}, {"content": "first number = top left rectangle x coordinate\nsecond number = top left rectangle y coordinate\nthird number = width of rectangle\nfourth number = length of rectangle", "user_title": "Anonymous", "datetimeon": "2007-03-03T16:39:29", "link": "pygame.draw.rect", "id": 400}, {"content": "If you want to make a deep copy of a Rect object (without importing the copy module)\nthen you can do so by calling move with the arguments (0,0).", "user_title": "Anonymous", "datetimeon": "2008-01-04T00:22:04", "link": "Rect.move", "id": 1359}, {"content": "the forth numer is the height of the rect", "user_title": "Anonymous", "datetimeon": "2007-03-11T20:32:13", "link": "pygame.draw.rect", "id": 434}, {"content": "The documentation is incorrect. pygame.mixer.music(5) will indeed play the music five times, not six. Perhaps the function used to behave differently, but I can find nothing in the documentation for either pygame or SDL_mixer that suggests so.", "user_title": "Anonymous", "datetimeon": "2007-03-19T15:33:19", "link": "pygame.mixer.music.play", "id": 440}, {"content": "I meant to say pygame.mixer.music.play(5), of course. I left out the \"play\" part.", "user_title": "Anonymous", "datetimeon": "2007-03-19T15:34:19", "link": "pygame.mixer.music.play", "id": 441}, {"content": "Properties in the object returned by get_rect():\n\nbottom\nbottomleft\nbottomright\ncenter\ncenterx\ncentery\nclamp\nclamp_ip\nclip\ncollidedict\ncollidedictall\ncollidelist\ncollidelistall\ncollidepoint\ncolliderect\ncontains\nfit\nh\nheight\ninflate\ninflate_ip\nleft\nmidbottom\nmidleft\nmidright\nmidtop\nmove\nmove_ip\nnormalize\nright\nsize\ntop\ntopleft\ntopright\nunion\nunion_ip\nunionall\nunionall_ip\nw\nwidth\nx\ny", "user_title": "Anonymous", "datetimeon": "2007-03-23T00:10:38", "link": "Surface.get_rect", "id": 446}, {"content": "i've noticed the loop functionality to be iffy for certain wave files (an audible gap between each loop). from what i can tell, it looks like this happens with stereo wave files, but i'm not completely sure. the mono waves i try to loop play as expected", "user_title": "Anonymous", "datetimeon": "2008-01-04T23:39:18", "link": "pygame.mixer.music.fadeout", "id": 1365}, {"content": "i've noticed the loop functionality to be iffy for certain wave files (an audible gap between each loop). from what i can tell, it looks like this happens with stereo wave files, but i'm not completely sure. the mono waves i try to loop play as expected (i accidentally added this comment to fadeout(), sorry)", "user_title": "Anonymous", "datetimeon": "2008-01-04T23:39:59", "link": "pygame.mixer.music.play", "id": 1366}, {"content": "what about osx? is macosx working?", "user_title": "Anonymous", "datetimeon": "2007-03-25T15:39:01", "link": "pygame.display.init", "id": 448}, {"content": "omg, you should really use the KMOD_ constants here", "user_title": "Anonymous", "datetimeon": "2007-03-28T15:20:15", "link": "pygame.key.get_mods", "id": 451}, {"content": "In 1.7.1, the behaviour when None is passed in is NOT reversed. pygame.event.set_allowed(None) will BLOCK all events.", "user_title": "Anonymous", "datetimeon": "2007-04-01T20:22:29", "link": "pygame.event.set_allowed", "id": 457}, {"content": "just do:\nimg = pygame.image.load(\"<>\").convert()\n\n-harry666t", "user_title": "Anonymous", "datetimeon": "2007-04-03T12:22:16", "link": "pygame.draw", "id": 463}, {"content": "BTW, Those values gotten below are if num-lock is on\nKMOD_NUM == 4096\nKMOD_LSHIFT == 1\nKMOD_RSHIFT == 2\nKMOD_NUM | KMOD_LSHIFT == 4097\nThe simpler way is to use the bitwise AND (&)\n\nkeymods & KMOD_LSHIFT \n\nreturns true (actually 1 in this case) if left shift is pressed, no matter what else is pressed or if num lock is on, or if the planets are aligned correctly.", "user_title": "Anonymous", "datetimeon": "2007-04-03T19:15:04", "link": "pygame.key.get_mods", "id": 466}, {"content": "What is the definition of the key and mod members of KEYDOWN?", "user_title": "Anonymous", "datetimeon": "2007-04-07T05:35:39", "link": "pygame.event", "id": 475}, {"content": "who do i get a reference to a reserved channel? its not channel 0 nor num_channels-1", "user_title": "Anonymous", "datetimeon": "2007-04-07T11:19:42", "link": "pygame.mixer.set_reserved", "id": 476}, {"content": "\"does not work with current release\". Which release is that? Is the information valid?", "user_title": "Anonymous", "datetimeon": "2007-04-18T13:12:55", "link": "pygame.movie", "id": 497}, {"content": "FLAC support would be cool", "user_title": "Anonymous", "datetimeon": "2007-11-19T06:00:08", "link": "pygame.mixer.Sound", "id": 1142}, {"content": "fadeout does not block in linux either", "user_title": "Anonymous", "datetimeon": "2008-01-01T21:41:36", "link": "pygame.mixer.music.fadeout", "id": 1351}, {"content": "Watch out for this one, it has a major twist:\n(x,y) are coordinates in the referential of the rectangle.\nFor instance:\n>>> import pygame\n>>> r = pygame.Rect(32,32,132,132)\n>>> r.collidepoint(140,140)\n1", "user_title": "Anonymous", "datetimeon": "2007-04-20T17:57:54", "link": "Rect.collidepoint", "id": 502}, {"content": "Music will be resampled in some cases, not in others. When playing a 44.1kHz MP3, the default 22050 frequency works, but a 48kHz mp3 plays in less than half speed - 48000 or 24000 works then.\nTo handle this behaviour, you have to know the sample rate of your music files before playing them, and can't switch smoothly. Big bummer.", "user_title": "Anonymous", "datetimeon": "2008-01-13T07:45:26", "link": "pygame.mixer.music.play", "id": 1406}, {"content": "# This should draw a square with a hight of 20 pixels on a Surface:\nheight = 20\npygame.draw.rect(Surface, (255, 255, 255), (0, 0, height, height))", "user_title": "Anonymous", "datetimeon": "2007-12-06T15:43:11", "link": "pygame.draw.rect", "id": 1220}, {"content": "Anonymous[0], that's nonsense. The x,y coords are absolute coordinates. To illustrate:\n\n>>> r = pygame.rect.Rect(32, 32, 132, 132)\n>>> r.collidepoint(1,1)\n0\n>>> r.collidepoint(32,32)\n1", "user_title": "Anonymous", "datetimeon": "2007-11-23T19:43:53", "link": "Rect.collidepoint", "id": 1153}, {"content": "A little black cross. Mouse cursor is 8*8 Pixel, hotspot is at (4, 4). \nthe cross is (Read Binary):\n00011000 => 24 \n00011000\n00011000\n11100111 => 231\n11100111\n00011000\n00011000\nand has no AND-Mask. \n\npygame.mouse.set_cursor((8, 8), (4, 4), (24, 24, 24, 231, 231, 24, 24, 24), (0, 0, 0, 0, 0, 0, 0, 0))", "user_title": "Anonymous", "datetimeon": "2007-11-26T11:20:04", "link": "pygame.mouse.set_cursor", "id": 1157}, {"content": "excellent comments!\njorgen", "user_title": "Anonymous", "datetimeon": "2007-11-26T19:56:49", "link": "Surface.fill", "id": 1158}, {"content": "True. set_allowed(None) blocks all event types.\n\n- Another (initially skeptical) pygame user.", "user_title": "Anonymous", "datetimeon": "2007-11-26T22:35:30", "link": "pygame.event.set_blocked", "id": 1159}, {"content": "The first channels are reserved.\nFor example: pygame.mixer.Channel(0)", "user_title": "Anonymous", "datetimeon": "2007-11-27T12:11:21", "link": "pygame.mixer.set_reserved", "id": 1160}, {"content": "I have found that just watching for joystick events may not provide enough \ngranularity for fast-paced arcade games that require 100 millisecond changes.\nInstead of events, consider polling the status of the axes in the main game loop\n(or whatever your local equivalent is)", "user_title": "Anonymous", "datetimeon": "2007-12-08T11:08:32", "link": "pygame.joystick", "id": 1230}, {"content": "Actually, on my system [Ubunty Gutsy] it returned a list of None:\n>>> import pygame\n>>> pygame.font.get_fonts()\n[None]", "user_title": "Anonymous", "datetimeon": "2007-12-12T08:11:00", "link": "pygame.font.get_fonts", "id": 1242}, {"content": "How do you draw squares in pygame??", "user_title": "Anonymous", "datetimeon": "2007-11-29T13:19:20", "link": "pygame.draw.rect", "id": 1171}, {"content": "\"... will only effect the smaller area\" is probably meant to read \"... will only affect the smaller area\"", "user_title": "Anonymous", "datetimeon": "2007-12-20T07:32:23", "link": "pygame.Surface", "id": 1290}, {"content": "# Matthew N. Brown copyright 2007\n\n# Here is an example program in wich\n# balls hit walls and other balls:\n#\n# This program draws circles using: pygame.draw.circle\n#\n# You can copy this program on to\n# your own computer and run it.\n#\n\nimport os, sys\n\n ## INIT STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef HE_HE_init():\n global screen, big_black_rect, APPLICATION_w_size, APPLICATION_z_size\n global WOW_pi_divided_by_180, WOW_180_divided_by_pi\n pygame.init()\n random.seed()\n APPLICATION_w_size = 700\n APPLICATION_z_size = 500\n ##### To close window while in fullscreen, press Esc while holding shift. #######\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size))\n #screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), FULLSCREEN)\n pygame.display.set_caption(\"They bwounce off bwalls? Matthew N. Brown copyright 2007\")\n pygame.mouse.set_visible(1)\n big_black_rect = pygame.Surface(screen.get_size())\n big_black_rect = big_black_rect.convert()\n big_black_rect.fill((0, 0, 0))\n screen.blit(big_black_rect, (0, 0))\n #fonty = pygame.font.Font(None, 36)\n fonty = pygame.font.SysFont(\"Times New Roman\", 25)\n fonty.set_bold(0)\n IMAGEE = fonty.render('Loading . . .', 1, (0, 250, 10))\n screen.blit(IMAGEE, (100, 200)); del IMAGEE\n pygame.display.flip()\n pygame.mixer.init(22050, -16, True, 1024)\n WOW_pi_divided_by_180 = math.pi / 180.0\n WOW_180_divided_by_pi = 180.0 / math.pi\n set_up_key_variables()\n Lets_ROLL()\n ## INIT STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n\n ## SAVE LEVEL?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef write_to_file_WEEE_STRANGE(file_namey, data):\n noq = '\\n'\n filey = open(file_namey, 'w')\n for d in data:\n filey.write( str(d) + noq)\n ## SAVE LEVEL?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n\n ## SMALL FUNCTIONS STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n ### some functions: ###\ndef distance_2D (w1, z1, w2, z2):\n return math.sqrt(math.pow(float(w1) - float(w2), 2) + math.pow(float(z1) - float(z2), 2))\ndef rect_touching_rect(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 > ww1 and w2 < wol1 and z2 > zz1 and z2 < zol1)\ndef rect_touching_rect2(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 >= ww1 and w2 <= wol1 and z2 >= zz1 and z2 <= zol1)\ndef positive(n):\n if n < 0: n = -n; return n\ndef int_randy(range, add):\n return int((random.random() * range) + add)\ndef randy(range, add):\n return (random.random() * range) + add\ndef freaky_rect_switcharoo_2D(pw, pz, pwol, pzol, buffy_the_fat):\n buffy_the_fat2 = buffy_the_fat * 2\n if pwol > 0:\n gw = pw; gwol = pwol\n else:\n gw = pwol + pw; gwol = pw - gw\n if pzol > 0:\n gz = pz; gzol = pzol\n else:\n gz = pzol + pz; gzol = pz - gz\n return [gw - buffy_the_fat, gz - buffy_the_fat, gwol + buffy_the_fat2, gzol + buffy_the_fat2]\ndef points_rotated_by_angle_2D(points_wz, axis_w, axis_z, angle):\n rotated_points_wz = []\n angle = -angle -90\n angle_times_WOW_pi_divided_by_180 = angle * WOW_pi_divided_by_180\n c1 = math.cos(angle_times_WOW_pi_divided_by_180)\n s1 = math.sin(angle_times_WOW_pi_divided_by_180)\n for pointy in points_wz:\n xt = pointy[0] - axis_w\n yt = pointy[1] - axis_z\n rotated_points_wz += [(-xt * s1) + (yt * c1) + axis_w, (-xt * c1) - (yt * s1) + axis_z]\n return rotated_points_wz\ndef point_rotated_by_angle_2D(point_w, point_z, axis_w, axis_z, angle):\n angle = -angle -90\n angle_times_WOW_pi_divided_by_180 = angle * WOW_pi_divided_by_180\n c1 = math.cos(angle_times_WOW_pi_divided_by_180)\n s1 = math.sin(angle_times_WOW_pi_divided_by_180)\n xt = point_w - axis_w\n yt = point_z - axis_z\n return (-xt * s1) + (yt * c1) + axis_w, (-xt * c1) - (yt * s1) + axis_z\ndef arc_tangent_2D(point_w, point_z):\n return math.atan2(point_w, point_z) * WOW_180_divided_by_pi + 180\ndef arc_tangent_2D_2(point_w, point_z):\n return -math.atan2(point_w, point_z) * WOW_180_divided_by_pi + 180\ndef ball_to_ball_wzkol_bounce(V1, m1, V2, m2, ball1_is_to_the_left):\n if (ball1_is_to_the_left and V1 >= V2) or (not ball1_is_to_the_left and V1 <= V2):\n Rv1 = V1 - V2\n Rv2 = 0 #V2 - V2\n NewV1 = ((m1 - m2) / float(m1 + m2)) * float(Rv1) + V2\n NewV2 = (( 2 * m1) / float(m1 + m2)) * float(Rv1) + V2\n return NewV1, NewV2\n else:\n return V1, V2\ndef Find_where_ball_stops_on_line_w(ball_w, ball_z, ball_wol, ball_zol, ball_rad, line_w, line_rad):\n did_collide = False\n totally = ball_rad + line_rad\n b1 = line_w + totally\n b2 = line_w - totally\n New_ball_w = ball_w + ball_wol\n New_ball_z = ball_z + ball_zol\n if ball_w >= b1 and ball_wol < 0 and New_ball_w < b1: New_ball_w = b1; did_collide = True\n elif ball_w <= b2 and ball_wol > 0 and New_ball_w > b2: New_ball_w = b2; did_collide = True\n else:\n if ball_w > b2 and ball_w < b1:\n if ball_w > line_w and ball_wol < 0:\n New_ball_w = ball_w; New_ball_z = ball_z\n did_collide = True\n elif ball_w < line_w and ball_wol > 0:\n New_ball_w = ball_w; New_ball_z = ball_z\n did_collide = True\n return New_ball_w, New_ball_z, did_collide\n New_ball_z = (float(ball_zol) / float(ball_wol) * float(New_ball_w - ball_w)) + float(ball_z)\n return New_ball_w, New_ball_z, did_collide\ndef find_where_ball_collides_on_a_wall(\n ball_w, ball_z,\n ball_wol, ball_zol,\n ball_rad,\n wall_type,\n wall_w1, wall_z1,\n wall_w2, wall_z2,\n wall_rad):\n toetoadly = ball_rad + wall_rad\n did_collide = False\n New_ball_w = ball_w + ball_wol\n New_ball_z = ball_z + ball_zol\n angle_hit_at = None\n Relate_ball_w = ball_w - wall_w1\n Relate_ball_z = ball_z - wall_z1\n Relate_wall_w2 = wall_w2 - wall_w1\n Relate_wall_z2 = wall_z2 - wall_z1\n arc_tangeriney = arc_tangent_2D(Relate_wall_w2, Relate_wall_z2)\n Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_Relate_wall_w2, Rotate_Relate_wall_z2 = points_rotated_by_angle_2D(((Relate_ball_w, Relate_ball_z), (Relate_wall_w2, Relate_wall_z2)), 0, 0, arc_tangeriney)\n Rotate_ball_wol, Rotate_ball_zol = point_rotated_by_angle_2D(ball_wol, ball_zol, 0, 0, arc_tangeriney)\n Rotate_Relate_ball_collide_w, Rotate_Relate_ball_collide_z, did_hit_weird_line = Find_where_ball_stops_on_line_w(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, ball_rad, 0, wall_rad)\n if Rotate_Relate_ball_w > -toetoadly and Rotate_Relate_ball_w < toetoadly:\n HE_HE_strange_popper_z = Rotate_Relate_ball_z\n else:\n HE_HE_strange_popper_z = Rotate_Relate_ball_collide_z\n Rotate_angle_hit_at = None\n if HE_HE_strange_popper_z < Rotate_Relate_wall_z2:\n if ball_is_going_towards_point(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, 0, Rotate_Relate_wall_z2):\n p1_touched, p1_collide_w, p1_collide_z, p1_angle_hit_at = find_where_ball_collides_on_another_ball(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, ball_rad, 0, Rotate_Relate_wall_z2, wall_rad)\n if p1_touched:\n Rotate_Relate_ball_collide_w = p1_collide_w\n Rotate_Relate_ball_collide_z = p1_collide_z\n Rotate_angle_hit_at = p1_angle_hit_at\n did_collide = True\n elif HE_HE_strange_popper_z > 0:\n if ball_is_going_towards_point(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, 0, 0):\n p2_touched, p2_collide_w, p2_collide_z, p2_angle_hit_at = find_where_ball_collides_on_another_ball(Rotate_Relate_ball_w, Rotate_Relate_ball_z, Rotate_ball_wol, Rotate_ball_zol, ball_rad, 0, 0, wall_rad)\n if p2_touched:\n Rotate_Relate_ball_collide_w = p2_collide_w\n Rotate_Relate_ball_collide_z = p2_collide_z\n Rotate_angle_hit_at = p2_angle_hit_at\n did_collide = True\n else:\n if did_hit_weird_line:\n did_collide = True\n if Rotate_Relate_ball_collide_w < 0: Rotate_angle_hit_at = 90\n else: Rotate_angle_hit_at = 270\n if did_collide:\n arc_tangeriney_2 = -arc_tangeriney\n angle_hit_at = Rotate_angle_hit_at + arc_tangeriney\n New_ball_w, New_ball_z = point_rotated_by_angle_2D(Rotate_Relate_ball_collide_w, Rotate_Relate_ball_collide_z, 0, 0, arc_tangeriney_2)\n New_ball_w += wall_w1\n New_ball_z += wall_z1\n return did_collide, New_ball_w, New_ball_z, angle_hit_at #, is_moving_towards\ndef zol_at_angle(wol, zol, angle):\n rotated_wol, rotated_zol = point_rotated_by_angle_2D(wol, zol, 0, 0, angle)\n return rotated_zol\ndef wzol_bounce_at_angle(wol, zol, angle, multi):\n rotated_wol, rotated_zol = point_rotated_by_angle_2D(wol, zol, 0, 0, angle)\n if rotated_zol > 0: rotated_zol = -rotated_zol * multi\n return point_rotated_by_angle_2D(rotated_wol, rotated_zol, 0, 0, -angle)\ndef ball_is_going_towards_point(ball_w, ball_z, ball_wol, ball_zol, point_w, point_z):\n angley = arc_tangent_2D(ball_w - point_w, ball_z - point_z)\n rotated_wol, rotated_zol = point_rotated_by_angle_2D(ball_wol, ball_zol, 0, 0, angley)\n return rotated_zol > 0\ndef find_where_ball_collides_on_another_ball (\n ball1_w, ball1_z,\n ball1_wol, ball1_zol,\n ball1_rad,\n ball2_w, ball2_z,\n ball2_rad\n ):\n totally = ball1_rad + ball2_rad\n dis_from_each_other = math.sqrt(math.pow(float(ball1_w) - float(ball2_w), 2) + math.pow(float(ball1_z) - float(ball2_z), 2))\n if dis_from_each_other < totally:\n angley = arc_tangent_2D(ball1_w - ball2_w, ball1_z - ball2_z)\n return True, ball1_w, ball1_z, angley\n else:\n they_did_touch = False\n New_ball1_w = ball1_w + ball1_wol\n New_ball1_z = ball1_z + ball1_zol\n angle_hit_at = None\n Relate_ball1_w = ball1_w - ball2_w\n Relate_ball1_z = ball1_z - ball2_z\n Relate_ball2_w = 0\n Relate_ball2_z = 0\n arcy_tangeriney = arc_tangent_2D(ball1_wol, ball1_zol)\n Rotated_Relate_ball1_w, Rotated_Relate_ball1_z, Rotated_ball1_wol, Rotated_ball1_zol = points_rotated_by_angle_2D(((Relate_ball1_w, Relate_ball1_z), (ball1_wol, ball1_zol)), 0, 0, arcy_tangeriney)\n did_collidey = False\n if Rotated_Relate_ball1_z > 0 and (Rotated_Relate_ball1_w > -totally and Rotated_Relate_ball1_w < totally):\n Rotated_Relate_ball1_collide_w = Rotated_Relate_ball1_w # + Rotated_ball1_wol\n HE_HE = math.pow(Rotated_Relate_ball1_w, 2) - math.pow(totally, 2)\n if HE_HE < 0: HE_HE = -HE_HE\n Rotated_Relate_ball1_collide_z = math.sqrt(HE_HE)\n Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol = Rotated_Relate_ball1_z + Rotated_ball1_zol\n if Rotated_Relate_ball1_collide_z < Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol:\n collision_wol = Rotated_ball1_wol\n collision_zol = Rotated_ball1_zol\n Rotated_Relate_ball1_collide_z = Rotated_Relate_ball1_z__PLUS__Rotated_ball1_zol\n angley_to_hit = None\n else:\n did_collidey = True\n they_did_touch = True\n angley_to_hit = arc_tangent_2D(Rotated_Relate_ball1_collide_w, Rotated_Relate_ball1_collide_z)\n else:\n angley_to_hit = None\n collision_wol = Rotated_ball1_wol\n collision_zol = Rotated_ball1_zol\n Rotated_Relate_ball1_collide_w = Rotated_Relate_ball1_w + Rotated_ball1_wol\n Rotated_Relate_ball1_collide_z = Rotated_Relate_ball1_z + Rotated_ball1_zol\n if did_collidey:\n arcy_tangeriney_2 = -arcy_tangeriney\n angle_hit_at = angley_to_hit + arcy_tangeriney\n New_ball1_w, New_ball1_z = point_rotated_by_angle_2D(Rotated_Relate_ball1_collide_w, Rotated_Relate_ball1_collide_z, 0, 0, arcy_tangeriney_2)\n New_ball1_w += ball2_w\n New_ball1_z += ball2_z\n return they_did_touch, New_ball1_w, New_ball1_z, angle_hit_at #, New_ball1_wol, New_ball1_zol\n ### some functions: ###\n\n ## GRAPHICS STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef chilly_font(size):\n fonti = pygame.font.SysFont(\"Times New Roman\", size)\n return fonti\ndef chilly_font_Italicy(size):\n fonti = pygame.font.SysFont(\"Times New Roman\", size)\n fonti.set_italic(1)\n return fonti\ndef draw_loading_messagey(stringy): # Draw loading message\n pygame.mouse.set_visible(1)\n fonty = chilly_font(26)\n IMAGEE = fonty.render(stringy, 0, (0, 255, 0), (0, 0, 0))\n screen.blit(IMAGEE, (200, 250))\n del IMAGEE\n pygame.display.flip()\n ## GRAPHICS STUFF: ##\n#########################################################################################\n\n ## KEYS AND MOUSE STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef set_up_key_variables():\n global ky_held, ky_first_held, ky_time_last_pressed\n global mowse_w, mowse_z, mowse_inn\n global mowse_left_pressed, mowse_right_pressed, mowse_left_held, mowse_right_held\n mowse_left_held = False\n mowse_right_held = False\n mowse_left_pressed = False\n mowse_right_pressed = False\n mowse_w = 0\n mowse_z = 0\n mowse_inn = 0\n ky_held = []\n ky_first_held = []\n ky_time_last_pressed = []\n m = -1\n while m < 500:\n m += 1\n ky_held += [0]\n ky_first_held += [0]\n ky_time_last_pressed += [0]\ndef clear_all_kys():\n global mowse_left_pressed, mowse_right_pressed, mowse_left_held, mowse_right_held\n mowse_left_held = False\n mowse_right_held = False\n mowse_left_pressed = False\n mowse_right_pressed = False\n m = -1\n while (m < 500):\n m += 1; ky_held[m] = 0; ky_first_held[m] = 0; ky_time_last_pressed[m] = 0\ndef clear_these_ky_first_held(list_keys_numbers):\n for k in list_keys_numbers:\n ky_first_held[k] = 0\ndef clear_first_held_kys():\n m = -1\n while (m < 500):\n m += 1; ky_first_held[m] = 0\ndef old_style_ky(n):\n return (ky_first_held_CEV(n) or (ky_held[n] and ky_time_last_pressed[n] < time.time() - .3))\ndef ky_first_held_CEV(n):\n if (ky_first_held[n]):\n ky_first_held[n] = 0; return 1\n else:\n return 0\ndef mowse_in_rect (w, z, wol, zol):\n return (mowse_w >= w and mowse_z >= z and mowse_w <= w + wol and mowse_z <= z + zol)\ndef mowse_in_circle (w, z, rad):\n dia = rad * 2\n if mowse_in_rect(w - rad, z - rad, w + dia, z + dia):\n return (distance_2D(mowse_w, mowse_z, w, z) < rad)\n else:\n return 0\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\ndef check_for_keys():\n global mowse_w, mowse_z, mowse_inn, mowse_left_pressed, mowse_right_pressed, mowse_left_held, mowse_right_held, APPLICATION_w_size, APPLICATION_z_size\n global loopy\n global unicodey\n mowse_left_pressed = False\n mowse_right_pressed = False\n unicodey = ''\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n elif e.type == ACTIVEEVENT:\n mowse_inn = (e.gain and (e.state == 1 or e.state == 6))\n elif e.type == KEYDOWN:\n ky_held[e.key] = 1\n ky_first_held[e.key] = 1\n ky_time_last_pressed[e.key] = time.time()\n unicodey = e.unicode\n elif e.type == KEYUP:\n ky_held[e.key] = 0\n elif e.type == MOUSEMOTION:\n mowse_w = e.pos[0]\n mowse_z = e.pos[1]\n if mowse_w >= 0 and mowse_w <= APPLICATION_w_size and mowse_z >= 0 and mowse_z <= APPLICATION_z_size:\n mowse_inn = 1\n else:\n mowse_inn = 0\n elif e.type == MOUSEBUTTONUP:\n if e.button == 1: mowse_left_held = 0\n if e.button == 3: mowse_right_held = 0\n elif e.type == MOUSEBUTTONDOWN:\n mowse_left_pressed = (e.button == 1)\n mowse_right_pressed = (e.button == 3)\n mowse_left_held = mowse_left_held or e.button == 1\n mowse_right_held = mowse_right_held or e.button == 3\n elif e.type == JOYAXISMOTION:\n pass\n elif e.type == JOYBALLMOTION:\n pass\n elif e.type == JOYHATMOTION:\n pass\n elif e.type == JOYBUTTONUP:\n pass\n elif e.type == JOYBUTTONDOWN:\n pass\n elif e.type == VIDEORESIZE:\n print e\n print \"What happened!?\"\n #global big_black_rect, screen\n #APPLICATION_w_size = e.size[0]\n #APPLICATION_z_size = e.size[1]\n #screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size))#, RESIZABLE)\n #big_black_rect = pygame.Surface(screen.get_size())\n #big_black_rect = big_black_rect.convert()\n #big_black_rect.fill((0, 100, 200))\n elif e.type == VIDEOEXPOSE:\n pass\n elif e.type == USEREVENT:\n pass\n if ky_held[27] and (ky_held[303] or ky_held[304]): loopy = 0\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\n ## KEYS AND MOUSE STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\n\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n\n\n ## MAIN LOOPY STUFF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n#########################################################################################\ndef ball_is_going_towards_ball(Bn1, Bn2):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_mass, ball_RECT\n arc_tangerine = arc_tangent_2D(ball_w[Bn1] - ball_w[Bn2], ball_z[Bn1] - ball_z[Bn2])\n woly1, zoly1 = point_rotated_by_angle_2D(ball_wol[Bn1], ball_zol[Bn1], 0, 0, arc_tangerine)\n return zoly1 > 0\ndef ball_is_relatively_going_towards_ball(Bn1, Bn2):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_mass, ball_RECT\n arc_tangerine = arc_tangent_2D(ball_w[Bn1] - ball_w[Bn2], ball_z[Bn1] - ball_z[Bn2])\n woly1, zoly1, woly2, zoly2 = points_rotated_by_angle_2D(((ball_wol[Bn1], ball_zol[Bn1]), (ball_wol[Bn2], ball_zol[Bn2])), 0, 0, arc_tangerine)\n return zoly1 > 0 and zoly1 > zoly2 # zoly2 < zoly1 or zoly2 > zoly1 # zoly1 + zoly2 > 0\n #return zoly1 > 0 or zoly1 > zoly2\ndef Make_two_balls_hit_at_angle(Bn1, Bn2, angle):\n global bounce_friction\n #print angle\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_mass, ball_RECT\n woly1, zoly1, woly2, zoly2 = points_rotated_by_angle_2D(((ball_wol[Bn1], ball_zol[Bn1]), (ball_wol[Bn2], ball_zol[Bn2])), 0, 0, angle)\n V1 = zoly1 * bounce_friction\n V2 = zoly2 * bounce_friction\n zoly1, zoly2 = ball_to_ball_wzkol_bounce(V1, ball_mass[Bn1], V2, ball_mass[Bn2], True)\n ball_wol[Bn1], ball_zol[Bn1], ball_wol[Bn2], ball_zol[Bn2] = points_rotated_by_angle_2D(((woly1, zoly1), (woly2, zoly2)), 0, 0, -angle)\n updatey_ball_quick_rect(Bn1)\n updatey_ball_quick_rect(Bn2)\ndef updatey_ball_quick_rect(B):\n dia = ball_rad[B] * 2 + 4\n ball_squar[B] = [ball_w[B] - ball_rad[B] - 2, ball_z[B] - ball_rad[B] - 2, dia, dia]\n ball_RECT[B] = freaky_rect_switcharoo_2D(ball_w[B], ball_z[B], ball_wol[B], ball_zol[B], ball_rad[B] + 4)\ndef minus_ball_thing(n):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n if ball_max >= 0:\n del ball_w [n]\n del ball_z [n]\n del ball_wol [n]\n del ball_zol [n]\n del ball_rad [n]\n del ball_color [n]\n del ball_squar [n]\n del ball_angle [n]\n del ball_angleol[n]\n del ball_mass [n]\n del ball_RECT [n]\n ball_max -= 1\ndef add_ball_thing(w, z, wol, zol, rad, color, angle, angleol, mass_thing, rect_thing):\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_squar, ball_angle, ball_angleol, ball_mass, ball_RECT\n ball_max += 1\n ball_w += [w]\n ball_z += [z]\n ball_wol += [wol]\n ball_zol += [zol]\n ball_rad += [rad]\n ball_color += [color]\n ball_angle += [angle]\n ball_angleol += [angleol]\n dia = rad * 2\n ball_squar += [[w - rad, z - rad, dia, dia]]\n if mass_thing == True:\n ball_mass += [4 / 3 * math.pi * rad * rad * rad]\n else:\n ball_mass += [mass_thing]\n if rect_thing == True:\n ball_RECT += [None]\n updatey_ball_quick_rect(ball_max)\n #ball_RECT += [freaky_rect_switcharoo_2D(w, z, wol, zol, rad)]\n else:\n ball_RECT += [rect_thing]\ndef minus_wall_thing(WAL):\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n if wall_max >= 0:\n del wall_type [WAL]\n del wall_w1 [WAL]\n del wall_z1 [WAL]\n del wall_w2 [WAL]\n del wall_z2 [WAL]\n del wall_rad [WAL]\n del wall_color [WAL]\n del wall_RECT [WAL]\n wall_max -= 1\ndef add_wall_thing(type, w1, z1, w2, z2, rad, color_thing, rect_thing):\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n wall_max += 1\n wall_type += [type]\n wall_w1 += [w1]\n wall_z1 += [z1]\n wall_w2 += [w2]\n wall_z2 += [z2]\n wall_rad += [rad]\n if color_thing == True:\n if type == 1: color_thing = (220, 220, 220)\n elif type == 2: color_thing = (240, 140, 130)\n elif type == 3: color_thing = (100, 255, 100)\n elif type == 4: color_thing = (255, 100, 100)\n elif type == 5: color_thing = (100, 100, 255)\n wall_color += [color_thing]\n if rect_thing == True:\n wall_RECT += [freaky_rect_switcharoo_2D(w1 - 2, z1 - 2, w2 - w1 + 4, z2 - z1 + 4, rad)]\n else:\n wall_RECT += [rect_thing]\ndef reset_stuff():\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global levely\n if levely == 1:\n ball_max = -1\n ball_w = []\n ball_z = []\n ball_wol = []\n ball_zol = []\n ball_rad = []\n ball_color = []\n ball_angle = []\n ball_angleol = []\n ball_squar = []\n ball_mass = []\n ball_RECT = []\n #add_ball_thing(350, 300, 0, 0, 18, (230, 230, 250), 0, 0, True, True)\n #add_ball_thing(150, 400, 0, 0, 40, (220, 210, 255), 0, 0, True, True)\n #add_ball_thing(300, 150, 0, 0, 62, (110, 106, 255), 0, 0, True, True)\n add_ball_thing(220, 200, 0, 0, 50, (180, 226, 255), 180, 0, True, True)\n wall_max = -1\n wall_type = []\n wall_w1 = []\n wall_z1 = []\n wall_w2 = []\n wall_z2 = []\n wall_rad = []\n wall_color = []\n wall_RECT = []\n add_wall_thing(1, 160, 250, 300, 270, 1, True, True)\n add_wall_thing(1, 500, 270, 600, 310, 1, True, True)\n add_wall_thing(1, 200, 450, 600, 450, 10, True, True)\n add_wall_thing(1, 300, 350, 400, 370, 5, True, True)\n add_wall_thing(1, 300, 100, 400, 100, 20, True, True)\n add_wall_thing(1, 650, 140, 700, 200, 6, True, True)\n add_wall_thing(1, 650, 140, 600, 40, 6, True, True)\n add_wall_thing(1, 150, 340, 150, 340, 30, True, True)\n add_wall_thing(1, 40, 200, 40, 200, 30, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 30, 30, 30, 30, 10, True, True)\n add_wall_thing(1, 0, 0, APPLICATION_w_size, 0, 5, True, True)\n add_wall_thing(1, 0, 0, 0, APPLICATION_z_size, 5, True, True)\n add_wall_thing(1, 0, APPLICATION_z_size, APPLICATION_w_size, APPLICATION_z_size, 5, True, True)\n add_wall_thing(1, APPLICATION_w_size, 0, APPLICATION_w_size, APPLICATION_z_size, 5, True, True)\n elif levely == 2:\n ball_max = 1\n ball_w = [323.62638473709342, 384.72135876760257]\n ball_z = [298.67896746658624, 109.24043981044279]\n ball_wol = [-0.27396932987421913, 7.133321987715842]\n ball_zol = [-0.38420912894762504, 1.6564147490246901]\n ball_rad = [15, 28]\n ball_color = [(137, 244, 234), (138, 221, 217)]\n ball_angle = [51.908780125668613, 294.77431504891717]\n ball_angleol = [-1.2400074168431123, 17.698615258690229]\n ball_squar = [[306.62638473709342, 281.67896746658624, 34, 34], [354.72135876760257, 79.240439810442794, 60, 60]]\n ball_mass = [10602.875205865552, 68964.24193160313]\n ball_RECT = [[304.35241540721921, 279.2947583376386, 38.273969329874205, 38.384209128947646], [352.72135876760257, 77.240439810442794, 71.133321987715846, 65.656414749024691]]\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [189, 290, 166, 14, 697, 562, 643, 3, 0, 223, 117, 695, 497, 497, 0, 0, 0, 700]\n wall_z1 = [284, 316, 436, 499, 446, 0, 128, 225, 106, 310, 155, 210, 159, 159, 0, 0, 500, 0]\n wall_w2 = [222, 446, 697, 157, 377, 681, 679, 49, 383, 287, 5, 448, 376, 546, 700, 0, 700, 700]\n wall_z2 = [301, 314, 478, 432, 487, 99, 98, 416, 171, 324, 225, 323, 147, 179, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[186, 281, 39, 23], [287, 313, 162, 4], [154, 424, 555, 66], [7, 429, 157, 73], [359, 424, 356, 85], [554, -8, 135, 115], [635, 94, 52, 38], [-29, 193, 110, 255], [-32, 74, 447, 129], [211, 298, 88, 38], [-3, 143, 128, 94], [440, 198, 263, 137], [368, 139, 137, 28], [485, 147, 73, 44], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\n elif levely == 3:\n ball_max = 2\n ball_w = [425.0, 492.31837629165733, 98.512856261065167]\n ball_z = [126.0, 422.24553778829392, 430.4902396760661]\n ball_wol = [-12.0, 2.6816237083426699, 6.487143738934833]\n ball_zol = [-3.0, -1.245537788293916, -21.490239676066096]\n ball_rad = [15, 28, 21]\n ball_color = [(137, 244, 234), (138, 221, 217), (136, 235, 236)]\n ball_angle = [93.833857527468922, 75.681742520058592, 323.2915629772819]\n ball_angleol = [-0.87655530207419896, 0.30220691772972269, 1.1825329351046094]\n ball_squar = [[408.0, 109.0, 34, 34], [462.31837629165733, 392.24553778829392, 60, 60], [75.512856261065167, 407.4902396760661, 46, 46]]\n ball_mass = [10602.875205865552, 68964.24193160313, 29094.28956489508]\n ball_RECT = [[394.0, 104.0, 50.0, 41.0], [460.31837629165733, 389.0, 66.68162370834267, 65.245537788293916], [73.512856261065167, 384.0, 56.487143738934833, 71.490239676066096]]\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [189, 290, 166, 14, 697, 562, 643, 3, 0, 223, 117, 695, 497, 497, 0, 0, 0, 700]\n wall_z1 = [284, 316, 436, 499, 446, 0, 128, 225, 106, 310, 155, 210, 159, 159, 0, 0, 500, 0]\n wall_w2 = [222, 446, 697, 157, 377, 681, 679, 49, 383, 287, 5, 480, 376, 546, 700, 0, 700, 700]\n wall_z2 = [301, 314, 478, 432, 487, 99, 98, 416, 171, 324, 225, 325, 147, 179, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[186, 281, 39, 23], [287, 313, 162, 4], [154, 424, 555, 66], [7, 429, 157, 73], [359, 424, 356, 85], [554, -8, 135, 115], [635, 94, 52, 38], [-29, 193, 110, 255], [-32, 74, 447, 129], [211, 298, 88, 38], [-3, 143, 128, 94], [472, 198, 231, 139], [368, 139, 137, 28], [485, 147, 73, 44], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\n elif levely == 4:\n ball_max = 15\n ball_w = [60.722554805471077, 452.1573538490178, 80.244575784959252, 38.90004863123329, 526.62934623960155, 561.76077439217966, 51.00641675327735, 476.21179724447387, 74.019911348330012, 104.13986580489509, 77.672785567417591, 97.908669417930454, 492.31309851379422, 107.55531577343871, 25.677250467589708, 408.28461679522843]\n ball_z = [123.53309256655999, 426.85562864865636, 446.98025958602022, 145.55077237791539, 432.36880616921724, 419.52605372165829, 185.76812996010321, 398.60172712183214, 227.90675893521163, 330.14246403509031, 280.7917430301959, 382.77488932204739, 431.7008452670733, 426.72875393133694, 108.86075181750218, 420.07030113046562]\n ball_wol = [0.58974898201312453, 0.29357826379544644, -0.7453458908661944, -0.26977452024547638, -0.13077525550683244, 0.35703289164546842, 0.25581836770201244, -0.16968524576896582, -0.96858759109981474, 0.020541831638986374, 0.21623640500730243, 0.16869582232640204, -0.32778500262837312, -1.0423733543425631, 0.078384075232750969, 0.070169924397188832]\n ball_zol = [2.5202528491916918, -0.067935899483811957, 1.0209651395893582, 1.5519551597452736, 0.37674466231734333, 0.7179102343171756, 1.2098558443319702, -0.21937811619009639, 1.6292902773669935, 0.95366629391114355, 0.99836183708718151, 0.65985328138026611, 0.72997687518744558, -0.33325230167901332, 1.8584237502130836, 1.1180771215980612]\n ball_rad = [12, 20, 14, 19, 14, 23, 23, 13, 25, 28, 28, 25, 20, 20, 20, 24]\n ball_color = [(132, 202, 208), (130, 220, 228), (133, 230, 241), (133, 200, 224), (138, 244, 248), (134, 176, 212), (132, 246, 206), (136, 191, 201), (130, 247, 204), (135, 190, 248), (136, 196, 244), (137, 246, 211), (132, 176, 232), (139, 200, 204), (135, 204, 206), (137, 234, 248)]\n ball_angle = [250.64218161257492, 228.50285566079282, 169.93029421257162, 93.92451866434908, 160.53385135173758, 101.81391124171368, 58.682544988047297, 42.833392250734839, 278.96920717602609, 157.52451729820555, 104.82808146227505, 319.29094377305643, 8.3988066326588289, 61.303383965779759, 262.01723832271352, 187.75853100116501]\n ball_angleol = [-11.145052526574146, 0.73910476098485844, -1.916370769365741, 7.8109934129380036, 1.2564621818214414, -0.21633250902344123, 0.96094866236460608, 18.696614939999161, -2.7765510174821686, -0.46915418861267033, 1.3615127061730832, 0.55215997018655683, 0.83188571652892485, -2.1096665563746759, 4.3536534603644128, 0.77565328887569629]\n ball_squar = [[46.722554805471077, 109.53309256655999, 28, 28], [430.1573538490178, 404.85562864865636, 44, 44], [64.244575784959252, 430.98025958602022, 32, 32], [17.90004863123329, 124.55077237791539, 42, 42], [510.62934623960155, 416.36880616921724, 32, 32], [536.76077439217966, 394.52605372165829, 50, 50], [26.00641675327735, 160.76812996010321, 50, 50], [461.21179724447387, 383.60172712183214, 30, 30], [47.019911348330012, 200.90675893521163, 54, 54], [74.139865804895095, 300.14246403509031, 60, 60], [47.672785567417591, 250.7917430301959, 60, 60], [70.908669417930454, 355.77488932204739, 54, 54], [470.31309851379422, 409.7008452670733, 44, 44], [85.555315773438707, 404.72875393133694, 44, 44], [3.6772504675897082, 86.860751817502177, 44, 44], [382.28461679522843, 394.07030113046562, 52, 52]]\n ball_mass = [5428.6721054031623, 25132.741228718347, 8620.5302414503913, 21548.184010972389, 8620.5302414503913, 38223.757816227015, 38223.757816227015, 6902.0790599367756, 49087.385212340516, 68964.24193160313, 68964.24193160313, 49087.385212340516, 25132.741228718347, 25132.741228718347, 25132.741228718347, 43429.376843225298]\n tempy = [[24.00641675327735, 158.76812996010321, 54.255818367702012, 55.209855844331969], [459.04211199870491, 381.38234900564203, 34.16968524576896, 34.219378116190114], [44.051323757230193, 198.90675893521163, 58.968587591099819, 59.629290277366991], [72.139865804895095, 298.14246403509031, 64.02054183163898, 64.953666293911141], [45.672785567417591, 248.7917430301959, 64.216236405007308, 64.998361837087188], [68.908669417930454, 353.77488932204739, 58.168695822326399, 58.659853281380265], [467.98531351116583, 407.7008452670733, 48.327785002628389, 48.729976875187447], [82.512942419096149, 402.39550162965793, 49.042373354342558, 48.333252301679011], [1.6772504675897082, 84.860751817502177, 48.078384075232748, 49.858423750213085], [380.28461679522843, 392.07030113046562, 56.070169924397192, 57.118077121598063]]\n ball_RECT = [[44.722554805471077, 107.53309256655999, 32.589748982013127, 34.520252849191692], [428.1573538490178, 402.78769274917255, 48.293578263795446, 48.067935899483814], [61.499229894093062, 428.98025958602022, 36.74534589086619, 37.020965139589357], [15.630274110987813, 122.55077237791539, 46.269774520245477, 47.551955159745276], [508.49857098409473, 414.36880616921724, 36.130775255506819, 36.376744662317343], [534.76077439217966, 392.52605372165829, 54.357032891645467, 54.717910234317173]] + tempy\n del tempy\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [189, 196, 166, 14, 697, 562, 643, 0, 326, 51, 18, 695, 497, 497, 0, 0, 0, 700]\n wall_z1 = [284, 221, 436, 499, 446, 0, 128, 201, 62, 9, 182, 210, 159, 159, 0, 0, 500, 0]\n wall_w2 = [220, 297, 697, 157, 377, 681, 679, 49, 304, 139, 0, 480, 376, 524, 700, 0, 700, 700]\n wall_z2 = [244, 218, 478, 432, 487, 99, 98, 416, 161, 315, 126, 325, 147, 176, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[186, 245, 37, 38], [193, 218, 107, 3], [154, 424, 555, 66], [7, 429, 157, 73], [359, 424, 356, 85], [554, -8, 135, 115], [635, 94, 52, 38], [-32, 169, 113, 279], [276, 30, 78, 163], [39, -3, 112, 330], [-8, 118, 34, 72], [472, 198, 231, 139], [368, 139, 137, 28], [485, 147, 51, 41], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\n elif levely == 5:\n ball_max = 15\n ball_w = [563.2380017184845, 135.5091931534665, 435.09697027584525, 132.51126304855137, 158.80356877160969, 486.49890666361813, 28.0454597909272, 469.94449157610796, 253.77058846375945, 33.311743878553251, 651.08671805489632, 467.4560139814393, 420.90145867058521, 248.83956419449743, 98.267666685148598, 670.85536291962285]\n ball_z = [340.3499477728684, 192.53572614832325, 274.00276170743837, 474.72360924550071, 248.04392629767023, 199.66234253741388, 291.77486188629132, 98.828156873677884, 261.79870802935454, 452.90721309179793, 434.31611085503482, 422.84067516142846, 143.71750465032488, 474.55563009909457, 63.407930077910926, 97.5392796541895]\n ball_wol = [-0.12736934788998625, -0.34670289908297647, -0.62730956112551528, -0.01316352118701539, -0.36875760413492498, 0.3253705975573648, -0.43186646985168864, 0.029829055857965088, -0.051399766840351885, 0.31143213467472303, 0.91261705660387604, -0.39289683694945782, 0.6973192899270082, -0.026739395385515136, 0.47773812365404217, -0.14449244329674141]\n ball_zol = [0.2651067487506561, 0.33747092449158278, -0.20330004911815291, 0.11263669365628809, 0.62183969591811039, 0.220324713577495, 0.12382039798193512, -0.062689280803922554, 0.13756798955280808, 0.8702172500111478, -0.031277763984301599, 0.28378328194527458, 0.1666190295210413, 0.056074468995401638, 0.75422143538357722, 0.14790083350095956]\n ball_rad = [12, 20, 14, 19, 14, 23, 23, 13, 25, 28, 28, 25, 20, 20, 20, 24]\n ball_color = [(132, 202, 208), (130, 220, 228), (133, 230, 241), (133, 200, 224), (138, 244, 248), (134, 176, 212), (132, 246, 206), (136, 191, 201), (130, 247, 204), (135, 190, 248), (136, 196, 244), (137, 246, 211), (132, 176, 232), (139, 200, 204), (135, 204, 206), (137, 234, 248)]\n ball_angle = [103.32400188884675, 316.71158855283181, 66.797426175129175, 35.509394217326573, 15.886531654813545, 0.61656478963343941, 195.33151301725019, 152.08747184390086, 199.80989069184068, 131.62120808048311, 339.38767654500623, 158.21789358507957, 322.31233400906359, 97.437869538449633, 179.6312883714439, 134.41162557033078]\n ball_angleol = [0.54118695268280415, -1.0009948706990461, -0.42583251039327935, -0.049119552546591096, -1.7234897593393199, 0.1278122582140804, -0.33925087348758332, 0.98916269599321738, 0.054177225060088277, 0.93648329222661952, 2.0855948904138386, -1.2792816321392795, 1.9343475351789952, -0.094694117658838645, 1.3328174529019678, 1.0390947956294083]\n ball_squar = [[549.2380017184845, 326.3499477728684, 28, 28], [113.5091931534665, 170.53572614832325, 44, 44], [419.09697027584525, 258.00276170743837, 32, 32], [111.51126304855137, 453.72360924550071, 42, 42], [142.80356877160969, 232.04392629767023, 32, 32], [461.49890666361813, 174.66234253741388, 50, 50], [3.0454597909272003, 266.77486188629132, 50, 50], [454.94449157610796, 83.828156873677884, 30, 30], [226.77058846375945, 234.79870802935454, 54, 54], [3.3117438785532514, 422.90721309179793, 60, 60], [621.08671805489632, 404.31611085503482, 60, 60], [440.4560139814393, 395.84067516142846, 54, 54], [398.90145867058521, 121.71750465032488, 44, 44], [226.83956419449743, 452.55563009909457, 44, 44], [76.267666685148598, 41.407930077910926, 44, 44], [644.85536291962285, 71.5392796541895, 52, 52]]\n ball_mass = [5428.6721054031623, 25132.741228718347, 8620.5302414503913, 21548.184010972389, 8620.5302414503913, 38223.757816227015, 38223.757816227015, 6902.0790599367756, 49087.385212340516, 68964.24193160313, 68964.24193160313, 49087.385212340516, 25132.741228718347, 25132.741228718347, 25132.741228718347, 43429.376843225298]\n tempy = [[140.43481116747478, 230.04392629767023, 36.368757604134913, 36.621839695918112], [459.49890666361813, 172.66234253741388, 54.325370597557367, 54.220324713577497], [0.61359332107551268, 264.77486188629132, 54.431866469851684, 54.123820397981937], [452.94449157610796, 81.765467592873961, 34.029829055857967, 34.062689280803923], [224.7191886969191, 232.79870802935454, 58.051399766840348, 58.137567989552807], [1.3117438785532514, 420.90721309179793, 64.311432134674718, 64.870217250011152], [619.08671805489632, 402.28483309105053, 64.912617056603878, 64.031277763984292], [438.06311714448987, 393.84067516142846, 58.392896836949433, 58.283783281945276], [396.90145867058521, 119.71750465032488, 48.697319289927009, 48.166619029521044], [224.81282479911192, 450.55563009909457, 48.026739395385505, 48.056074468995405], [74.267666685148598, 39.407930077910926, 48.477738123654042, 48.754221435383577], [642.71087047632614, 69.5392796541895, 56.144492443296713, 56.147900833500962]]\n ball_RECT = [[547.11063237059454, 324.3499477728684, 32.127369347889953, 32.265106748750654], [111.16249025438353, 168.53572614832325, 48.34670289908297, 48.337470924491583], [416.46966071471974, 255.79946165832024, 36.627309561125514, 36.203300049118127], [109.49809952736436, 451.72360924550071, 46.01316352118701, 46.112636693656285]] + tempy\n del tempy\n wall_max = 17\n wall_type = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n wall_w1 = [135, 120, 230, 14, 531, 562, 441, 128, 403, 51, 504, 518, 377, 447, 0, 0, 0, 700]\n wall_z1 = [265, 216, 439, 499, 339, 0, 217, 104, 306, 9, 441, 210, 168, 127, 0, 0, 500, 0]\n wall_w2 = [227, 288, 697, 157, 456, 665, 476, 432, 61, 139, 633, 547, 435, 537, 700, 0, 700, 700]\n wall_z2 = [262, 200, 478, 432, 302, 141, 228, 77, 334, 315, 295, 193, 178, 114, 0, 500, 500, 500]\n wall_rad = [1, 1, 10, 5, 20, 6, 6, 30, 30, 10, 10, 10, 10, 10, 5, 5, 5, 5]\n wall_color = [(220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220), (220, 220, 220)]\n wall_RECT = [[132, 262, 98, 3], [117, 201, 174, 14], [218, 427, 491, 63], [7, 429, 157, 73], [438, 284, 111, 73], [554, -8, 119, 157], [433, 209, 51, 27], [96, 49, 368, 83], [33, 274, 398, 92], [39, -3, 112, 330], [492, 287, 153, 162], [506, 185, 53, 33], [365, 156, 82, 34], [435, 106, 114, 29], [-7, -7, 714, 14], [-7, -7, 14, 514], [-7, 493, 714, 14], [693, -7, 14, 514]]\ndef draw_walls_on_big_black_rect():\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global big_black_rect\n global LIN_selected, CLICKER, CLICKER2\n if CLICKER:\n if LIN_selected != -1:\n nnn = LIN_selected[0]\n if LIN_selected[1] == 1:\n wall_w1[nnn] = mowse_w\n wall_z1[nnn] = mowse_z\n else:\n wall_w2[nnn] = mowse_w\n wall_z2[nnn] = mowse_z\n w1 = wall_w1[nnn]\n z1 = wall_z1[nnn]\n w2 = wall_w2[nnn]\n z2 = wall_z2[nnn]\n rad = wall_rad[nnn]\n wall_RECT[nnn] = freaky_rect_switcharoo_2D(w1 - 2, z1 - 2, w2 - w1 + 4, z2 - z1 + 4, rad)\n wl = -1\n while wl < wall_max:\n wl += 1\n w1 = wall_w1[wl]\n z1 = wall_z1[wl]\n w2 = wall_w2[wl]\n z2 = wall_z2[wl]\n rad = wall_rad[wl]\n collyu = wall_color[wl]\n pygame.draw.line(big_black_rect, collyu, (w1, z1), (w2, z2), rad * 2)\n pygame.draw.circle(big_black_rect, collyu, (w1, z1), rad)\n pygame.draw.circle(big_black_rect, collyu, (w2, z2), rad)\n #pygame.draw.rect(big_black_rect, (200, 200, 200), wall_RECT[wl], 1)\n if CLICKER2:\n if mowse_in_rect(wall_RECT[wl][0], wall_RECT[wl][1], wall_RECT[wl][2], wall_RECT[wl][3]):\n if mowse_in_circle(w1, z1, rad+3): selected = -1; LIN_selected = [wl, 1]\n elif mowse_in_circle(w2, z2, rad+3): selected = -1; LIN_selected = [wl, 2]\ndef Lets_ROLL():\n global loopy\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global bounce_friction, air_friction, gravity, rock_and_ROLLY\n global LIN_selected, CLICKER, CLICKER2\n global levely\n levely = 3\n bounce_friction = 0.8\n #bounce_friction = 1.0\n air_friction = 0.999\n #air_friction = 1.0\n gravity = 0.5\n rock_and_ROLLY = math.pi / 8 * 180 #24\n reset_stuff()\n fontyyy = chilly_font_Italicy(24)\n PRESS_SPACE_BAR_TO_MOVE_immy = fontyyy.render('Press SPACE BAR to start motion.', 0, (100, 200, 100))\n PRESS_SPACE_BAR_TO_STOP_immy = fontyyy.render('Press SPACE BAR to stop motion.', 0, (200, 100, 100))\n PRESS_ENTER_TO_RESET_immy = fontyyy.render('Press ENTER to reset.', 0, (150, 150, 150))\n PRESS_MINUS_TO_MINUS_immy = fontyyy.render('Press - to delete a ball.', 0, (150, 150, 150))\n PRESS_ADD_TO_ADD_immy = fontyyy.render('Press + to add a ball.', 0, (150, 150, 150))\n LEFT_CLICK_TO_immy = fontyyy.render('Left click on a \"ghost ball\" to change its speed.', 0, (150, 150, 150))\n RIGHT_CLICK_TO_immy = fontyyy.render('Right click on a ball to stop its motion.', 0, (150, 150, 150))\n PRESS_S_TO_immy = fontyyy.render('Press S to stop all balls.', 0, (150, 150, 150))\n PRESS_PAGE_UP_TO_immy = fontyyy.render('Press Page Up to change the level.', 0, (150, 150, 150))\n #message_1_immy\n del fontyyy\n #calculate_for_sure = True\n selected = -1\n LIN_selected = -1\n move_stuff = True\n t = time.time() + .01\n CLICKER = False\n CLICKER2 = False\n loopy = 1\n while loopy:\n big_black_rect.fill((0, 0, 0))\n draw_walls_on_big_black_rect()\n screen.blit(big_black_rect, (0, 0))\n check_for_keys()\n CLICKER = mowse_left_held\n CLICKER2 = mowse_left_pressed\n CLICKER_2 = mowse_right_held\n CLICKER2_2 = mowse_right_pressed\n if ky_first_held_CEV(32): move_stuff = not move_stuff\n if ky_first_held_CEV(13): reset_stuff()\n if ky_first_held_CEV(280):\n levely += 1\n if levely > 5: levely = 1\n reset_stuff()\n if ky_first_held_CEV(115): # S\n M = -1\n while M < ball_max:\n M += 1\n ball_wol[M] = 0\n ball_zol[M] = 0\n updatey_ball_quick_rect(M)\n if ky_first_held_CEV(45) or ky_first_held_CEV(269): # -\n minus_ball_thing(0)\n if ky_first_held_CEV(61) or ky_first_held_CEV(270): # +\n add_ball_thing(350 + randy(40, -20), 400 + randy(40, -20), randy(40, -20), randy(40, -20), int_randy(20, 10), (int_randy(10, 130), int_randy(80, 170), int_randy(50, 200)), 0, 0, True, True)\n if ky_first_held_CEV(49):\n listy = ['Level_save']\n listy += ['ball_max = ' + str(ball_max)]\n listy += ['ball_w = ' + str(ball_w)]\n listy += ['ball_z = ' + str(ball_z)]\n listy += ['ball_wol = ' + str(ball_wol)]\n listy += ['ball_zol = ' + str(ball_zol)]\n listy += ['ball_rad = ' + str(ball_rad)]\n listy += ['ball_color = ' + str(ball_color)]\n listy += ['ball_angle = ' + str(ball_angle)]\n listy += ['ball_angleol = ' + str(ball_angleol)]\n listy += ['ball_squar = ' + str(ball_squar)]\n listy += ['ball_mass = ' + str(ball_mass)]\n listy += ['ball_RECT = ' + str(ball_RECT)]\n listy += ['wall_max = ' + str(wall_max)]\n listy += ['wall_type = ' + str(wall_type)]\n listy += ['wall_w1 = ' + str(wall_w1)]\n listy += ['wall_z1 = ' + str(wall_z1)]\n listy += ['wall_w2 = ' + str(wall_w2)]\n listy += ['wall_z2 = ' + str(wall_z2)]\n listy += ['wall_rad = ' + str(wall_rad)]\n listy += ['wall_color = ' + str(wall_color)]\n listy += ['wall_RECT = ' + str(wall_RECT)]\n ##write_to_file_WEEE_STRANGE(\"Level_Save.dat\", listy)\n del listy\n if CLICKER2:\n allow_selectey_thing = True\n else:\n allow_selectey_thing = False\n if not CLICKER:\n selected = -1\n LIN_selected = -1\n to_be_selected = selected\n M = -1\n while M < ball_max:\n M += 1\n if move_stuff:\n move_ball(M)\n wwol = int(ball_w[M] + ball_wol[M])\n zzol = int(ball_z[M] + ball_zol[M])\n pygame.draw.circle(screen, ball_color[M], (int(ball_w[M]), int(ball_z[M])), ball_rad[M])\n blpw, blpz = point_rotated_by_angle_2D(0, -ball_rad[M], 0, 0, ball_angle[M])\n pygame.draw.line(screen, (100, 100, 100), (int(ball_w[M] + blpw), int(ball_z[M] + blpz)), (int(ball_w[M]), int(ball_z[M])))\n if not move_stuff:\n pygame.draw.circle(screen, (100, 100, 250), (wwol, zzol), ball_rad[M], 1)\n pygame.draw.circle(screen, (100, 100, 150), (wwol, zzol), int(ball_rad[M] * 1.0), 1)\n pygame.draw.circle(screen, (150, 150, 200), (wwol, zzol), int(ball_rad[M] * 0.8), 1)\n pygame.draw.circle(screen, (200, 200, 250), (wwol, zzol), int(ball_rad[M] * 0.5), 1)\n pygame.draw.line(screen, (100, 160, 250), (int(ball_w[M]), int(ball_z[M])), (wwol, zzol))\n pygame.draw.rect(screen, (130, 130, 130), ball_RECT[M], 1)\n pygame.draw.rect(screen, (140, 140, 140), ball_squar[M], 1)\n if allow_selectey_thing:\n if mowse_in_rect(ball_RECT[M][0], ball_RECT[M][1], ball_RECT[M][2], ball_RECT[M][3]):\n if mowse_in_circle(wwol, zzol, ball_rad[M]):\n to_be_selected = M\n LIN_selected = -1\n if CLICKER_2:\n if mowse_in_rect(ball_squar[M][0], ball_squar[M][1], ball_squar[M][2], ball_squar[M][3]):\n if mowse_in_circle(ball_w[M], ball_z[M], ball_rad[M]):\n ball_wol[M] = 0\n ball_zol[M] = 0\n ball_angleol[M] = 0\n updatey_ball_quick_rect(M)\n if CLICKER:\n if selected == M:\n if move_stuff:\n mowseyy_w = mowse_w\n mowseyy_z = mowse_z\n bw1 = ball_rad[M]\n bz1 = ball_rad[M]\n bw2 = APPLICATION_w_size - ball_rad[M]\n bz2 = APPLICATION_z_size - ball_rad[M]\n if mowseyy_w < bw1: mowseyy_w = bw1\n if mowseyy_w > bw2: mowseyy_w = bw2\n if mowseyy_z < bz1: mowseyy_z = bz1\n if mowseyy_z > bz2: mowseyy_z = bz2\n ww = mowseyy_w - ball_w[M]\n zz = mowseyy_z - ball_z[M]\n #dissy = distance_2D(0, 0, ww, zz)\n ball_wol[M] = ww # / 2.0 # / dissy\n ball_zol[M] = zz # / 2.0 # / dissy\n else:\n ball_wol[M] = mowse_w - ball_w[M]\n ball_zol[M] = mowse_z - ball_z[M]\n updatey_ball_quick_rect(M)\n selected = to_be_selected\n if not move_stuff:\n screen.blit(PRESS_SPACE_BAR_TO_MOVE_immy, (10, 10))\n else:\n screen.blit(PRESS_SPACE_BAR_TO_STOP_immy, (10, 10))\n screen.blit(PRESS_MINUS_TO_MINUS_immy, (10, 30))\n screen.blit(PRESS_ADD_TO_ADD_immy, (10, 50))\n screen.blit(PRESS_ENTER_TO_RESET_immy, (10, 70))\n screen.blit(LEFT_CLICK_TO_immy, (10, 90))\n screen.blit(RIGHT_CLICK_TO_immy, (10, 110))\n screen.blit(PRESS_S_TO_immy, (10, 130))\n screen.blit(PRESS_PAGE_UP_TO_immy, (10, 150))\n pygame.display.flip()\n while t > time.time(): pass\n t = time.time() + .01\n # Try_Again_HE_HE Is weird!! maybe It should be deleted!!\ndef move_ball(M):\n ball_angle[M] += ball_angleol[M]\n if ball_angle[M] > 359: ball_angle[M] -= 360\n elif ball_angle[M] < 0: ball_angle[M] += 361\n #movey_bally_speciality(M, ball_wol[M], ball_zol[M], 10)\n movey_bally_speciality(M, ball_wol[M], ball_zol[M], 10)\n ball_zol[M] += gravity\n updatey_ball_quick_rect(M)\ndef movey_bally_speciality(M, wol_special, zol_special, Try_Again_HE_HE):\n global loopy\n global ball_max, ball_w, ball_z, ball_wol, ball_zol, ball_rad, ball_color, ball_angle, ball_angleol, ball_squar, ball_mass, ball_RECT\n global wall_max, wall_type, wall_w1, wall_z1, wall_w2, wall_z2, wall_rad, wall_color, wall_RECT\n global bounce_friction, air_friction, gravity, rock_and_ROLLY\n distance_is_supposed_to_be_at = distance_2D(0, 0, wol_special, zol_special)\n wa = ball_w[M]\n za = ball_z[M]\n #will_be_w = wa + ball_wol[M]\n #will_be_z = za + ball_zol[M]\n will_be_w = wa + wol_special\n will_be_z = za + zol_special\n LIN_collide_max = -1\n LIN_collide_w = []\n LIN_collide_z = []\n LIN_collide_ang = []\n LIN_collide_dis = []\n LL = -1\n while LL < wall_max:\n LL += 1\n if rect_touching_rect2(ball_RECT[M][0], ball_RECT[M][1], ball_RECT[M][2], ball_RECT[M][3], wall_RECT[LL][0], wall_RECT[LL][1], wall_RECT[LL][2], wall_RECT[LL][3]):\n #print 'weee'\n did_collide, New_ball_w, New_ball_z, angle_hit_at = find_where_ball_collides_on_a_wall(wa, za, wol_special, zol_special, ball_rad[M], wall_type[LL], wall_w1[LL], wall_z1[LL], wall_w2[LL], wall_z2[LL], wall_rad[LL])\n if did_collide:\n #print 'collide'\n #print str(New_ball_w), str(New_ball_z)\n LIN_collide_max += 1\n LIN_collide_w += [New_ball_w]\n LIN_collide_z += [New_ball_z]\n LIN_collide_ang += [angle_hit_at]\n LIN_collide_dis += [distance_2D(wa, za, New_ball_w, New_ball_z)]\n HEH_collide_max = -1\n HEH_collide_w = []\n HEH_collide_z = []\n HEH_collide_ang = []\n HEH_collide_dis = []\n HEH_collide_ball_hit = []\n M2 = -1\n while M2 < ball_max:\n M2 += 1\n if M2 != M:\n if rect_touching_rect2(ball_RECT[M][0], ball_RECT[M][1], ball_RECT[M][2], ball_RECT[M][3], ball_squar[M2][0], ball_squar[M2][1], ball_squar[M2][2], ball_squar[M2][3]):\n #they_did_touch, New_ball1_w, New_ball1_z, angle_hit_at = find_where_ball_collides_on_another_ball(wa, za, ball_wol[M], ball_zol[M], ball_rad[M], ball_w[M2], ball_z[M2], ball_rad[M2])\n they_did_touch, New_ball1_w, New_ball1_z, angle_hit_at = find_where_ball_collides_on_another_ball(wa, za, wol_special, zol_special, ball_rad[M], ball_w[M2], ball_z[M2], ball_rad[M2])\n if they_did_touch:\n HEH_collide_max += 1\n HEH_collide_w += [New_ball1_w]\n HEH_collide_z += [New_ball1_z]\n HEH_collide_ang += [angle_hit_at]\n HEH_collide_dis += [distance_2D(wa, za, New_ball1_w, New_ball1_z)]\n HEH_collide_ball_hit += [M2]\n current_dis = distance_is_supposed_to_be_at\n Wall_to_hit_at_angley = None\n Grr = -1\n while Grr < LIN_collide_max:\n Grr += 1\n #print LIN_collide_dis[Grr], current_dis\n if LIN_collide_dis[Grr] < current_dis:\n #print 'weee!'\n Wall_to_hit_at_angley = LIN_collide_ang[Grr]\n current_dis = LIN_collide_dis[Grr]\n will_be_w = LIN_collide_w[Grr]\n will_be_z = LIN_collide_z[Grr]\n Ball_to_hit = None\n Ball_to_hit_at_angley = None\n Heh = -1\n while Heh < HEH_collide_max:\n Heh += 1\n if HEH_collide_dis[Heh] < current_dis:\n if ball_is_going_towards_ball(M, HEH_collide_ball_hit[Heh]):\n if ball_is_relatively_going_towards_ball(M, HEH_collide_ball_hit[Heh]):\n Ball_to_hit = HEH_collide_ball_hit[Heh]\n Ball_to_hit_at_angley = HEH_collide_ang[Heh]\n else:\n Ball_to_hit = None\n Ball_to_hit_at_angley = None\n current_dis = HEH_collide_dis[Heh]\n will_be_w = HEH_collide_w[Heh]\n will_be_z = HEH_collide_z[Heh]\n if Ball_to_hit != None:\n Make_two_balls_hit_at_angle(M, Ball_to_hit, Ball_to_hit_at_angley)\n else:\n #if bouncey == 1: ball_wol[M] = -ball_wol[M] * bounce_friction\n #elif bouncey == 2: ball_zol[M] = -ball_zol[M] * bounce_friction\n if Wall_to_hit_at_angley != None:\n ball_wol[M], ball_zol[M] = wzol_bounce_at_angle(ball_wol[M], ball_zol[M], Wall_to_hit_at_angley, bounce_friction)\n ball_angleol[M] = zol_at_angle(ball_wol[M], ball_zol[M], Wall_to_hit_at_angley + 90) / ball_rad[M] * rock_and_ROLLY\n ball_w[M] = will_be_w\n ball_z[M] = will_be_z\n if ball_w[M] < 0 or ball_w[M] > APPLICATION_w_size or ball_z[M] < 0 or ball_z[M] > APPLICATION_z_size:\n #print str(M) + \" \", str(wa), str(za)\n print str(M) + \" \", str(ball_w[M]), str(ball_z[M]), str(ball_rad[M])\n ball_wol[M] *= air_friction\n ball_zol[M] *= air_friction\n updatey_ball_quick_rect(M)\n if current_dis < distance_is_supposed_to_be_at:\n if Try_Again_HE_HE > 0:\n distance_to_travel_next = distance_is_supposed_to_be_at - current_dis\n disy_HE_HE = distance_2D(0, 0, ball_wol[M], ball_zol[M])\n next_wol = ball_wol[M]\n next_zol = ball_zol[M]\n movey_bally_speciality(M, next_wol, next_zol, Try_Again_HE_HE - 1)\n\n ## Woah... Finally! Were near the end of the program! ##\nif __name__ == '__main__':\n import math\n import pygame\n import random\n import time\n import gc\n import copy\n from pygame.locals import *\n if not pygame.font: print 'Warning, fonts disabled?'\n if not pygame.mixer: print 'Warning, sound disabled?'\n HE_HE_init()\n ## THE END! ##", "user_title": "Anonymous", "datetimeon": "2007-12-23T15:46:20", "link": "pygame.draw.circle", "id": 1320}, {"content": "Guillame was confused on the nature of the last two arguments to Rect().\nhe thought that they were absolute coordinates, not width-height.\nThere isn't a bug with collidepoint.\nsee the pygame mailing list archives for the discussion with Guillame where this topic arose.", "user_title": "Anonymous", "datetimeon": "2007-04-25T17:52:17", "link": "Rect.collidepoint", "id": 524}, {"content": "I'm guessing they're key pressed (eg, the A or J keys) and the modifiers (Shift, Ctrl, Alt, Meta, Super, etc). Experiment to find details.", "user_title": "Anonymous", "datetimeon": "2007-04-26T17:03:55", "link": "pygame.event", "id": 528}, {"content": "I, too, get a list with a single item, None. This on Mac OS X 10.4 (Tiger) and Python 2.4.4; \npygame.ver returns '1.8.0pre'", "user_title": "Anonymous", "datetimeon": "2008-01-20T04:04:36", "link": "pygame.font.get_fonts", "id": 1428}, {"content": "The params units are in pixels.\nThe smallest unit for Pygame I think.\nIt doesn't make sense to change this to float.", "user_title": "Anonymous", "datetimeon": "2008-01-21T10:48:00", "link": "Rect.move_ip", "id": 1430}, {"content": "this resource is perfect for games with controls options\nbecause through it you can show the current input without creating a whole database of inputs\ntxt = font.render(pygame.key.name(current_key),True,(0,0,0))\nscreen.blit(txt,(0,0))", "user_title": "Anonymous", "datetimeon": "2008-01-21T20:04:03", "link": "pygame.key.name", "id": 1431}, {"content": "#!/usr/bin/python\nimport pygame\nfrom pygame.locals import *\n\ndef main():\n pygame.init()\n pygame.display.set_mode((300,200))\n pygame.display.set_caption('Testing')\n running = True\n while running:\n for event in pygame.event.get():\n if event.type == QUIT:\n running = False\n if event.type == KEYDOWN and event.key == K_ESCAPE:\n running = False\n if event.type == MOUSEBUTTONDOWN:\n #print event.button\n print pygame.mouse.get_pos()\n pygame.display.quit()\n\nif __name__ == '__main__':\n main()", "user_title": "Anonymous", "datetimeon": "2008-01-23T15:43:49", "link": "pygame.mouse.get_pos", "id": 1436}, {"content": "Once you draw something, how do you delete it??", "user_title": "Anonymous", "datetimeon": "2007-05-04T00:39:15", "link": "pygame.draw.rect", "id": 548}, {"content": "Hi,\n\nI was having a problem when trying to jump from one movie to another. Even with the fist one stopped the second movie played at half speed with no volume. A solution was to keep a reference to the old movie, which I assume stops the garbage collector trying to delete it before its finished doing whatever it was doing. \n\nThis would cause problems:\n\ncurrentMovie.stop()\ncurrentMovie = pygame.movie.Movie(fullname)\ncurrentMovie.play()\n\nAnd this fixes it:\n\ncurrentMovie.stop()\noldMovie = currentMovie\ncurrentMovie = pygame.movie.Movie(fullname)\ncurrentMovie.play()\n\nMaybe need a method for unloading the memory and stopping all its threads.", "user_title": "Anonymous", "datetimeon": "2007-05-09T10:27:03", "link": "pygame.movie", "id": 554}, {"content": "Does anyone know how to make multiple screens? I've tried making a different variable with different dimensions, but it only changes original\n\nsize=400,400\nscreen=display.set_mode(size)\n\nsize1=200,200\nnew_screen=display.set_mode(size1)", "user_title": "Anonymous", "datetimeon": "2008-01-29T10:33:27", "link": "pygame.display.set_mode", "id": 1456}, {"content": "The first two arguments to Surface.blit() seem to be reversed. To draw Surface \"source\" onto Surface \"dest\" the correct call is:\n\n pygame.Surface.blit(dest, source, position)\n\nor\n\n dest.blit(source, position)\n\n\nExample:\n\n screen = pygame.display.set_mode((100,100))\n screen.fill((255,255,255)) # white background\n red_block = pygame.Surface((50,50))\n red_block.fill((255,0,0))\n\n # draw red block onto white background\n screen.blit(red_block, (25,25))\n\n pygame.display.update()", "user_title": "Anonymous", "datetimeon": "2007-05-16T08:51:09", "link": "Surface.blit", "id": 572}, {"content": "Actually, on re-reading the method description, I realize that the \"dest\" argument means the position to where the source Surface should be copied too. So the call synopsis is equivalent to the second case in my comment.\n\nThe naming of the arguments is a bit confusing, IMHO, and also that it is not (visually) clear that the documentation describes the methods of a Surface object instance and makes no mention of the class methods.", "user_title": "Anonymous", "datetimeon": "2007-05-16T09:15:43", "link": "Surface.blit", "id": 573}, {"content": "Only the \"systems with multiple choices\" are listed there.", "user_title": "Anonymous", "datetimeon": "2007-05-20T19:04:19", "link": "pygame.display.init", "id": 578}, {"content": "Yeah the doc could be improved. The wrong comment below should be removed, it's only confusing. I did find another problem, though: It's important to note that when you cut out an area from the source, then the dest argument does _not_ specify where the origin of the source would be on the surface that is blitted on, but instead dest specifies the top left corner of just the area that is actually blitted. This is not quite clear from this doc, I think.", "user_title": "Anonymous", "datetimeon": "2007-05-24T07:20:10", "link": "Surface.blit", "id": 588}, {"content": "Pygame (and SDL) doesn't support multiple windows.", "user_title": "Anonymous", "datetimeon": "2008-02-09T15:29:56", "link": "pygame.display.set_mode", "id": 1525}, {"content": "you can't \"delete\" something you have drawn, you have to draw something over it instead.\nyou can make classes that wrap the different draw function and have both a \"show\" and a \"hide\" function and the hide function has to draw the background over the shape you created in \"hide\", but this can have weird results if shapes overlap and are not correctly redrawn.\ncheers", "user_title": "Anonymous", "datetimeon": "2007-06-02T10:44:48", "link": "pygame.draw.rect", "id": 607}, {"content": "destination.blit(source (distination location),(source location x,y and size x,y)", "user_title": "Anonymous", "datetimeon": "2007-06-05T09:13:28", "link": "pygame.Surface", "id": 609}, {"content": "destination.blit(source (distination location),(source location x,y and size x,y)", "user_title": "Anonymous", "datetimeon": "2007-06-05T09:13:56", "link": "Surface.blit", "id": 610}, {"content": "use events to save and release keystates to use it only for some:\n\nif event.type == pygame.KEYDOWN: if event.key == MYKEY: i = True\nelif event.type == pygame.KEYUP: if event.key == MYKEY: i = False\nif i: do stuff.", "user_title": "Anonymous", "datetimeon": "2008-02-11T16:09:33", "link": "pygame.key.set_repeat", "id": 1541}, {"content": "Is there a way to make the collision box of the line accurate to the line itself?", "user_title": "Anonymous", "datetimeon": "2008-02-12T18:09:15", "link": "pygame.draw.line", "id": 1549}, {"content": "Is there a way to produce small line segments that have accurate collision boxes\nso that one could have two lines that would be parallel to each other with out them colliding?", "user_title": "Anonymous", "datetimeon": "2008-02-12T18:13:18", "link": "pygame.draw", "id": 1550}, {"content": "Yes, actually it would be quite useful for it to be float, if it could store the\ndecimals and increment them as such but not display it until it would take effect...", "user_title": "Anonymous", "datetimeon": "2008-02-12T20:21:41", "link": "Rect.move_ip", "id": 1552}, {"content": "Clearly, more documentation about the event properties should be written.", "user_title": "Anonymous", "datetimeon": "2007-06-10T10:28:36", "link": "pygame.event", "id": 628}, {"content": "MOUSEBUTTONDOWN/UP:\n pos: tuple of x and y coordinates of the click\n button: 1 - left 2 - middle 3 - right button", "user_title": "Anonymous", "datetimeon": "2007-06-10T13:16:50", "link": "pygame.event", "id": 630}, {"content": "MOUSEMOTION\n pos: tuple of x and y coordinates\n rel: tuple of relative x and relative y change from previous position\n buttons: tuple of three values (left,middle,right). 0-not pressed 1-pressed", "user_title": "Anonymous", "datetimeon": "2007-06-10T13:18:03", "link": "pygame.event", "id": 631}, {"content": "While thick arcs do get filled, they also get moire holes - at least on Debian's 1.7.1release-4.1. For now, I've been using a rather ugly workaround where one draws the arc several times with the start angle offset by 0.01 to cut the moires back.", "user_title": "Anonymous", "datetimeon": "2008-02-16T15:39:17", "link": "pygame.draw.arc", "id": 1580}, {"content": "I have noticed that with my analog joystick square shaped information is returned\nfrom this function. For example, pressing fully down and right would return 1.0\nfor both axis directions, instead of 0.7071... as one might expect (since analog\njoysticks have a circle shaped socket for the stick to move in.)\n\nTo correct this one might want to use a function similar to the following\njoystick_transform function:\n\ndef length(v):\n\treturn math.sqrt(v[0]*v[0] + v[1]*v[1])\n\n# Transforms the square info of an analog joystick into circular info\ndef joystick_transform(j):\n\t# If joystick is not centered:\n\tif (j[0],j[1]) != (0,0):\n\t\t# Check if x axis is larger than y axis\n\t\tif abs(j[0]) > abs(j[1]):\n\t\t\t# Since x>y we will check for line intersection with wall\n\t\t\t# Get slope (m = y/x) for y = m * x (line equation)\n\t\t\tm = abs(j[1] / j[0])\n\t\t\t# At x=1.0 (intersecting right wall), y would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((1.0, m))\n\t\telse:\n\t\t\t# Since y>=x we will check for line intersection with ceiling\n\t\t\t# Get slope (m = x/y) for x = m * y (line equation)\n\t\t\tm = abs(j[0] / j[1])\n\t\t\t# At y=1.0 (intersecting ceiling), x would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((m,1.0))\n\telse:\n\t\t# Since the joystick is centered, the scaler will be 0\n\t\ts = 0\n\t\t\n\t# Simply scale the joystick axis data by the scaler\n\treturn (j[0] * s, j[1] * s)\n\n-----\n\nHere is a full example illustrating the difference between raw joystick input\nand transformed joystick information:\n\n#!/usr/bin/python\n\n# In this example the function joystick_transform will transform the\n# square shaped joystick axis information into a circular shape.\n# This will make the new joystick axis information easier to use while\n# moving around a character or cursor.\n\n# The RED dot represents the actual joystick information.\n\n# The BLUE dot represents the transformed joystick information.\n\n# The GREEN dot is a cursor that moves by the transformed joystick\n# information in a motion relative to it's previous location.\n\n\nimport pygame\nimport math\n\ndef norm(v):\n\tl = length(v)\n\tif l != 0:\n\t\treturn (v[0] / l, v[1] / l)\n\treturn (0,0)\n\t\ndef length(v):\n\treturn math.sqrt(v[0]*v[0] + v[1]*v[1])\n\n# Transforms the square info of an analog joystick into circular info\ndef joystick_transform(j):\n\t# If joystick is not centered:\n\tif (j[0],j[1]) != (0,0):\n\t\t# Check if x axis is larger than y axis\n\t\tif abs(j[0]) > abs(j[1]):\n\t\t\t# Since x>y we will check for line intersection with wall\n\t\t\t# Get slope (m = y/x) for y = m * x (line equation)\n\t\t\tm = abs(j[1] / j[0])\n\t\t\t# At x=1.0 (intersecting right wall), y would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((1.0, m))\n\t\telse:\n\t\t\t# Since y>=x we will check for line intersection with ceiling\n\t\t\t# Get slope (m = x/y) for x = m * y (line equation)\n\t\t\tm = abs(j[0] / j[1])\n\t\t\t# At y=1.0 (intersecting ceiling), x would equal m\n\t\t\t# scaler = length of normalized vector / length of line intersecting box\n\t\t\ts = 1.0 / length((m,1.0))\n\telse:\n\t\t# Since the joystick is centered, the scaler will be 0\n\t\ts = 0\n\t\t\n\t# Simply scale the joystick axis data by the scaler\n\treturn (j[0] * s, j[1] * s)\n\npygame.init()\npygame.joystick.init()\n\nscreen = pygame.display.set_mode((640,480))\n\njs = pygame.joystick.Joystick(0)\njs.init()\n\npx = 320.0\npy = 240.0\n\nmove_speed = 2.0\n\ndone = False\nwhile not done:\n\tkey = pygame.key.get_pressed()\n\tscreen.fill((255,255,255))\n\t\n\t# Outer box boundry\n\tpygame.draw.rect(screen, (200,200,200), ((10,10),(180,180)), 1)\n\t\n\t# Circle boundry\n\tpygame.draw.circle(screen, (0,0,0), (100,100), 90, 1)\n\t\n\t# Center point\n\tpygame.draw.circle(screen, (200,200,200), (100,100), 2, 1)\n\t\n\tjx = js.get_axis(0)\n\tjy = js.get_axis(1)\n\tn = norm((jx,jy))\n\t\n\t# Line representing normalized joystick information\n\tpygame.draw.line(screen, (200,200,200), (100,100), (100 + int(n[0] * 90.0), 100 + int(n[1] * 90.0)))\n\t\n\t# Raw joystick information\n\tx = 100 + int(jx * 90.0)\n\ty = 100 + int(jy * 90.0)\n\tpygame.draw.circle(screen, (255,0,0), (x, y), 5)\n\t\n\t# Transformed joystick information\n\ttj = joystick_transform((jx,jy))\n\tx = 100 + int(tj[0] * 90.0)\n\ty = 100 + int(tj[1] * 90.0)\n\tpygame.draw.circle(screen, (0,0,255), (x, y), 5)\n\t\n\t# Cursor moved by transformed joystick information\n\tpx = px + tj[0] * move_speed\n\tpy = py + tj[1] * move_speed\n\tpygame.draw.circle(screen, (0, 255, 0), (int(px), int(py)), 5)\n\t\n\tpygame.display.flip()\n\t\n\tfor event in pygame.event.get():\n\t\tif event.type == pygame.QUIT: \n\t\t\tdone = True\n\t\t\t\n\t\tif key[pygame.K_ESCAPE]:\n\t\t\tdone = True", "user_title": "Anonymous", "datetimeon": "2008-02-16T23:25:56", "link": "Joystick.get_axis", "id": 1584}, {"content": "the playback doesnt work, the music stops for a second and then starts again\nHELP ME", "user_title": "Anonymous", "datetimeon": "2010-11-27T14:29:59", "link": "pygame.mixer.music.play", "id": 3415}, {"content": "An example of the Color object is an rgb tuple like (100,0,200).", "user_title": "Anonymous", "datetimeon": "2008-02-27T10:13:22", "link": "Surface.set_at", "id": 1657}, {"content": "It might be convenient to have this particular documentation within the\nhelp(pygame.display.init) documentation, as of this writing it is not.\n\nAdditionally, information pertaining to Mac-OS X is not present; it may also be\nnoteworthy to document the methods by which Pygame renders its surfaces, as OSX,\nlike Windows, has its own various subsystems to draw views(surfaces).", "user_title": "Anonymous", "datetimeon": "2008-03-01T23:13:53", "link": "pygame.display.init", "id": 1680}, {"content": "All the keyboard event.key constants:\n\nLetters:\n K_a ... K_z\n\nNumbers:\n K_0 ... K_9\n\nControl:\n K_TAB\n K_RETURN\n K_ESCAPE\n K_SCROLLOCK\n K_SYSREQ\n K_BREAK\n K_DELETE\n K_BACKSPACE\n K_CAPSLOCK\n K_CLEAR\n K_NUMLOCK\n\nPunctuation:\n K_SPACE\n K_PERIOD\n K_COMMA\n K_QUESTION\n K_AMPERSAND\n K_ASTERISK\n K_AT\n K_CARET\n K_BACKQUOTE\n K_DOLLAR\n K_EQUALS\n K_EURO\n K_EXCLAIM\n K_SLASH, K_BACKSLASH\n K_COLON, K_SEMICOLON\n K_QUOTE, K_QUOTEDBL\n K_MINUS, K_PLUS\n K_GREATER, K_LESS\n\nBrackets:\n K_RIGHTBRACKET, K_LEFTBRACKET\n K_RIGHTPAREN, K_LEFTPAREN\n\nF-Keys:\n K_F1 ... K_F15\n\nEdit keys:\n K_HELP\n K_HOME\n K_END\n K_INSERT\n K_PRINT\n K_PAGEUP, K_PAGEDOWN\n K_FIRST, K_LAST\n\nKeypad:\n K_KP0 ... K_KP9\n K_KP_DIVIDE\n K_KP_ENTER\n K_KP_EQUALS\n K_KP_MINUS\n K_KP_MULTIPLY\n K_KP_PERIOD\n K_KP_PLUS\n\nSHF,CTL,ALT etc:\n K_LALT, K_RALT\n K_LCTRL, K_RCTRL\n K_LSUPER, K_RSUPER\n K_LSHIFT, K_RSHIFT\n K_RMETA, K_LMETA\n\nArrows:\n K_LEFT\n K_UP\n K_RIGHT\n K_DOWN\n\nOther:\n K_MENU\n K_MODE\n K_PAUSE\n K_POWER\n K_UNDERSCORE\n K_HASH\n\n K_UNKNOWN", "user_title": "Anonymous", "datetimeon": "2008-03-11T11:07:08", "link": "pygame.event.Event", "id": 1682}, {"content": "key is one of the K_* constants in the pygame package level -- it indicates the key pressed. For example, K_UP or K_ESCAPE.\nmod is the modifier. I'm assuming it's either a bitfield or a list. Shouldn't be hard to figure it out.", "user_title": "Anonymous", "datetimeon": "2008-03-16T02:36:10", "link": "pygame.event", "id": 1685}, {"content": "can anyone give me just a small script about how can i play a movie, please?\n\ni used this script but nothing happened. just a window but blank :(\n\nimport pygame\n\n\npygame.init()\n\n\ncine = pygame.movie.Movie('film.mpg')\nsz=cine.get_size()\npygame.display.set_mode(sz)\nwhile 1:\n cine.play(1)\n\nthe movie loads because i tried to find the length and worked and the movie is at the same location. please HELP!!", "user_title": "Anonymous", "datetimeon": "2007-07-12T09:58:26", "link": "pygame.movie", "id": 716}, {"content": "JOYBUTTONDOWN/JOYBUTTONUP\nbutton -- the ID of the button which fired the event.", "user_title": "Anonymous", "datetimeon": "2007-07-14T15:07:03", "link": "pygame.event", "id": 719}, {"content": "Is there a way to draw an anti aliased line with a thickness?", "user_title": "Anonymous", "datetimeon": "2008-03-28T14:12:55", "link": "pygame.draw.aaline", "id": 1713}, {"content": "I've notice that passing any negative number will cause the music to loop forever, not just -1.", "user_title": "Anonymous", "datetimeon": "2008-03-30T20:55:44", "link": "pygame.mixer.music.play", "id": 1722}, {"content": "It seams that you can't read the axes or button positions if you don't start the event loop.\nThis is a little different from what the docs are saying but actually expected since the joystick \nbroadcasts the position (is this right for all drivers?)", "user_title": "Anonymous", "datetimeon": "2008-04-02T23:40:29", "link": "pygame.joystick.Joystick", "id": 1727}, {"content": "Can I draw just one pixel with this? \nApparantly the smallest rect one can draw is 2 pixels big, I guess?", "user_title": "Anonymous", "datetimeon": "2007-07-16T21:15:36", "link": "pygame.draw.rect", "id": 726}, {"content": "the fade_in parameter seems to be missing. Using 1.7: \nTypeError: function takes at most 2 arguments (3 given)", "user_title": "Anonymous", "datetimeon": "2008-04-04T23:21:49", "link": "Sound.play", "id": 1732}, {"content": "The algorithm used will probably ruin the edges in your images. Makes them kinda blurry.", "user_title": "Anonymous", "datetimeon": "2008-04-05T17:52:43", "link": "pygame.transform.smoothscale", "id": 1734}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-07-18T19:04:17", "link": "Very cool design! Useful information. Go on!", "id": 733}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-07-18T19:04:17", "link": "Pretty nice site, wants to see much more on it! :)", "id": 734}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-07-18T19:04:17", "link": "Thanks for the enjoy to have you on my site ! Good luck.", "id": 735}, {"content": "How about creating a sound from a string of raw samples? I shouldn't have to construct a fake WAV header just to get Pygame to accept sound data.", "user_title": "Anonymous", "datetimeon": "2008-04-09T23:10:47", "link": "pygame.mixer.Sound", "id": 1742}, {"content": "Here are attributes of the different events (as best I can tell):\n\nACTIVEEVENT:\n\tgain\n\tstate\nKEYDOWN:\n\tunicode\nKEYUP:\n\tkey\n\tmod\nMOUSEMOTION:\n\tpos\n\trel\n\tbuttons\nMOUSEBUTTONDOWN and MOUSEBUTTONUP:\n\tpos\n\tbutton\nJOYAXISMOTION:\n\tjoy\n\taxis\n\tvalue\nJOYBALLMOTION:\n\tjoy\n\tball\n\trel\nJOYHATMOTION:\n\tjoy\n\that\n\tvalue\nJOYBUTTONUP and case JOYBUTTONDOWN:\n\tjoy\n\tbutton\nVIDEORESIZE:\n\tsize\n\tw\n\th\nSYSWMEVENT (WIN32 only):\n\thwnd\n\tmsg\n\twparam\n\tlparam", "user_title": "Anonymous", "datetimeon": "2007-07-19T13:22:47", "link": "pygame.event", "id": 740}, {"content": "> It is true that passing None for the final argument causes \"Invalid RGBA argument\". This is a bug in the documentation, not the code.\nI'd suggest replacing 'None' with 'NULL' in the documentation, then - it also indicates no value, but is not one you can enter in Python (hence there's no confusion).", "user_title": "Anonymous", "datetimeon": "2007-07-19T16:32:03", "link": "Font.render", "id": 741}, {"content": "There seems to be a typo in the sentence \"The blit routines will attempt to use hardware acceleration when possible, otherwise will use highly optimized software blitting methods.\"", "user_title": "Anonymous", "datetimeon": "2007-07-22T04:31:03", "link": "pygame.Surface", "id": 748}, {"content": "hey thanks for the tip ive been searchin in vain for days", "user_title": "Anonymous", "datetimeon": "2007-07-22T10:05:15", "link": "Surface.get_rect", "id": 749}, {"content": "Yes, it's particularly an issue for when a sprite wants to move diagonally in a low resolution.\n\nWhen you're only moving sideways 1 pixel at a time, it's impossible to move diagonally without breaking conservation of momentum.", "user_title": "Anonymous", "datetimeon": "2008-04-23T19:32:47", "link": "Rect.move_ip", "id": 1782}, {"content": "# Mattew N. Brown copyright 2007\n# This is an example program for key input:\n\n ## IMPORT THEN EXECUTE IMPORTED MODULE ('*.py'): ##\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\n ## UH!? WHAT IF IT ISN'T EXISTANT!?: ##\nif not pygame.font: print 'Warning, fonts disabled'\nif not pygame.mixer: print 'Warning, sound disabled'\n\n ## LOAD IMAGE AND SOUND: ##\ndef image_file_data(file_name, colorkey=None):\n try:\n image = pygame.image.load(file_name)\n except pygame.error, message:\n print 'ERROR: Image did not load:', file_name\n raise SystemExit, message\n image = image.convert()\n if colorkey is not None:\n if colorkey is -1:\n colorkey = image.get_at((0,0))\n image.set_colorkey(colorkey, RLEACCEL)\n return image, image.get_rect()\ndef sound_file_data(file_name):\n class NoneSound:\n def play(self): pass\n if not pygame.mixer:\n return NoneSound()\n try:\n sound = pygame.mixer.Sound(file_name)\n except pygame.error, message:\n print 'ERROR: Sound did not load:', file_name\n raise SystemExit, message\n return sound\ndef HEHEHE_font(size):\n fonti = pygame.font.Font(None, size)\n fonti.set_bold(0)\n return fonti\n ## IMAGE STRETCH AND ROTATE: ##\ndef HEHEHE_stretch_image (IMAGEY, wol, zol):\n #return pygame.transform.scale(IMAGEY, (wol, zol))\n return pygame.transform.scale(IMAGEY, (wol + IMAGEY.get_width(), zol + IMAGEY.get_height()))\ndef HEHEHE_rotate_image (IMAGEY, angle):\n center = (0, 0)\n rotate = pygame.transform.rotate\n IMAGEY = rotate(IMAGEY, angle)\n recty = IMAGEY.get_rect(center=center)\n return IMAGEY, recty\n ## DRAW IMAGE: ##\ndef draw_HEHEHE_image (IMAGEE, w, z):\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_image_stretch (IMAGEE, w, z, wol, zol):\n IMAGEE = HEHEHE_stretch_image(IMAGEE, wol, zol)\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_image_stretch_rotate (IMAGEE, w, z, wol, zol, angle):\n IMAGEE = HEHEHE_stretch_image(IMAGEE, wol, zol)\n IMAGEE, recty = HEHEHE_rotate_image(IMAGEE, angle)\n screen.blit(IMAGEE, (w + recty.x, z + recty.y))\n ## DRAW TEXT IMAGE: ##\ndef draw_HEHEHE_text (t, special, size, w, z, colory):\n fonty = HEHEHE_font(size)\n IMAGEE = fonty.render(t, special, colory)\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_text_stretch (t, special, size, w, z, colory, wol, zol):\n fonty = HEHEHE_font(size)\n IMAGEE = fonty.render(t, special, colory)\n IMAGEE = HEHEHE_stretch_image(IMAGEE, wol, zol)\n screen.blit(IMAGEE, (w, z))\ndef draw_HEHEHE_text_stretch_rotate (t, special, size, w, z, colory, wol, zol, angle):\n fonty = HEHEHE_font(size)\n IMAGE = fonty.render(t, special, colory)\n IMAGE = HEHEHE_stretch_image(IMAGE, wol, zol)\n IMAGE, recty = HEHEHE_rotate_image(IMAGE, angle)\n screen.blit(IMAGE, (w + recty.x, z + recty.y))\n ### AAAH! FREAKY!! ###\nclock = pygame.time.Clock()\nImage_directory = \"PNG/\"\nSound_directory = \"SOUND/\"\n ### WHAT IN THE WORLD IS THIS!!??: ###\npygame.init()\nAPPLICATION_w_size = 700\nAPPLICATION_z_size = 500\nscreen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n#pygame.display.set_icon(image_file_data(Image_directory + \"ICON.PNG\", 0)[0])\npygame.display.set_caption('Mattew N. Brown copyright 2007')\npygame.mouse.set_visible(1)\n ### WHAT IN THE WORLD IS THIS!!!!!!!!!!!!??: ###\nbackground = pygame.Surface(screen.get_size())\nbackground = background.convert()\nbackground.fill((0, 0, 0))\n ### THIS IS DRIVING MY CAR CRAZYs!!!\nscreen.blit(background, (0, 0))\npygame.display.flip()\nrandom.seed()\n\n ## LOAD ALL IMAGES AND SOUNDS: ##\nimage_MAX = 0\nimage_file_name = [];\nimage = []\nimage_rect = []\nwhile (image_MAX <= (-1) ):\n I = image_MAX\n Itemp1, Itemp2 = image_file_data(Image_directory + image_file_name[I], 0)\n image += [Itemp1]\n image_rect += [Itemp2]\n image_MAX += 1\nsound_MAX = 0\nsound_file_name = [];\nsound = []\nsound_rect = []\nwhile (sound_MAX <= (-1) ):\n I = sound_MAX\n Itemp1, Itemp2 = sound_file_data(Sound_directory + sound_file_name[I])\n sound += [Itemp1]\n sound_rect += [Itemp2]\n sound_MAX += 1\n ## LOAD ONE AND ONLY MUSIC FILE: ##\n#pygame.mixer.music.load(d + 'PCDV0043.WAV')\n\n # QUIT\t none\n # ACTIVEEVENT gain, state\n # KEYDOWN\t unicode, key, mod\n # KEYUP\t key, mod\n # MOUSEMOTION pos, rel, buttons\n # MOUSEBUTTONUP pos, button\n # MOUSEBUTTONDOWN pos, button\n # JOYAXISMOTION joy, axis, value\n # JOYBALLMOTION joy, ball, rel\n # JOYHATMOTION joy, hat, value\n # JOYBUTTONUP joy, button\n # JOYBUTTONDOWN joy, button\n # VIDEORESIZE size, w, h\n # VIDEOEXPOSE none\n # USEREVENT code\n\n\n\n ## MAIN: ##\nif __name__ == '__main__':\n EE = ['', '', '', '', '', '', '', '', '', '',\n '', '', '', '', '']\n b = (190, 130, 110)\n COLORY = [b, b, b, b, b, b, b, b, b, b,\n b, b, b, b, b]\n angy = 0\n loopy = 1\n while (loopy == 1):\n angy += 1\n if angy > 360:\n angy = 1\n clock.tick(70)\n screen.blit(background, (0, 0))\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n #elif e.type == KEYDOWN and e.key == K_ESCAPE:\n # loopy = 0\n else:\n nnnnnn = -1\n if e.type == QUIT: nnnnnn = 0\n if e.type == ACTIVEEVENT: nnnnnn = 1\n if e.type == KEYDOWN: nnnnnn = 2\n if e.type == KEYUP: nnnnnn = 3\n if e.type == MOUSEMOTION: nnnnnn = 4\n if e.type == MOUSEBUTTONUP: nnnnnn = 5\n if e.type == MOUSEBUTTONDOWN: nnnnnn = 6\n if e.type == JOYAXISMOTION: nnnnnn = 7\n if e.type == JOYBALLMOTION: nnnnnn = 8\n if e.type == JOYHATMOTION: nnnnnn = 9\n if e.type == JOYBUTTONUP: nnnnnn = 10\n if e.type == JOYBUTTONDOWN: nnnnnn = 11\n if e.type == VIDEORESIZE:\n nnnnnn = 12\n APPLICATION_w_size = e.size[0]\n APPLICATION_z_size = e.size[1]\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n if e.type == VIDEOEXPOSE: nnnnnn = 13\n if e.type == USEREVENT: nnnnnn = 14\n if nnnnnn != -1:\n EE[nnnnnn] = str(e); COLORY[nnnnnn] = (190, 200, 255)\n WOW = 0\n while (WOW < 14):\n draw_HEHEHE_text(\"E\" + str(WOW) + \" = \" + EE[WOW], 1, 24, 30, 20 + (WOW * 22), COLORY[WOW])\n if COLORY[WOW] == b:\n COLORY[WOW] = COLORY[WOW]\n else:\n COLORY[WOW] = (200, 240, 200)\n WOW += 1\n # QUIT\t none\n # ACTIVEEVENT gain, state\n # KEYDOWN\t unicode, key, mod\n # KEYUP\t key, mod\n # MOUSEMOTION pos, rel, buttons\n # MOUSEBUTTONUP pos, button\n # MOUSEBUTTONDOWN pos, button\n # JOYAXISMOTION joy, axis, value\n # JOYBALLMOTION joy, ball, rel\n # JOYHATMOTION joy, hat, value\n # JOYBUTTONUP joy, button\n # JOYBUTTONDOWN joy, button\n # VIDEORESIZE size, w, h\n # VIDEOEXPOSE none\n # USEREVENT code\n #I = 0\n #www = 0\n #while (www < 2):\n # www += 1\n # zzz = 0\n # while (zzz < 10):\n # zzz += 1\n # #if (I < image_MAX): draw_HEHEHE_image(I, www * 40, zzz * 40)\n # if (I < image_MAX): draw_HEHEHE_image_stretch_rotate(I, www * 40, zzz * 40, 40, 40, angy)\n # I += 1\n #draw_HEHEHE_text_stretch(\"BOOM!\", 1, 40, 330, 400, (255, 255, 255), 20, 20)\n #draw_HEHEHE_text_stretch_rotate(\"BOOM!\", 1, 40, 140, 400, (255, 255, 255), 0, 0, angy)\n #draw_HEHEHE_text_stretch_rotate(\"WEEEE!\", 1, 30, 450, 470, (255, 255, 255), 0, 0, angy)\n pygame.display.flip()", "user_title": "Anonymous", "datetimeon": "2007-07-28T12:28:50", "link": "pygame.event", "id": 779}, {"content": "# Matthew N. Brown copyright 2007\n# Here is an example program that\n# draws: polygons, circles, and rectangles:\n#\n# You can copy this program on to\n# your own computer and run it.\n#\n\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\nif not pygame.font: print 'Warning, fonts disabled'\nif not pygame.mixer: print 'Warning, sound disabled'\nimport time\nimport gc\nimport math\n\npygame.init()\nAPPLICATION_w_size = 700\nAPPLICATION_z_size = 500\nscreen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n#screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), FULLSCREEN)\npygame.display.set_caption(\"HEHE test draw thingie program Matthew N. Brown copyright 2007\")\n#pygame.mouse.set_visible(0)\nglobal background\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0, 0, 0))\nscreen.blit(background, (0, 0))\npygame.display.flip()\nrandom.seed()\n\nplayer_w = 3\nplayer_z = 2\n\nx = -1\nmap_w_size = 10\nmap_z_size = 10\nmap = [[x, x, x, x, x, x, x, x, x, x, x],\n [x, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1],\n [x, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],\n [x, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1],\n [x, 1, 1, 0, 0, 1, 0, 0, 4, 0, 0],\n [x, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0],\n [x, 1, 1, 0, 0, 2, 0, 1, 1, 0, 1],\n [x, 1, 1, 1, 0, 2, 0, 1, 0, 0, 1],\n [x, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1],\n [x, 1, 0, 4, 0, 1, 0, 0, 0, 0, 1],\n [x, 1, 0, 2, 2, 2, 2, 2, 2, 2, 1]]\n\nmap[player_z][player_w] = 3\n\n ## IMAGE STRETCH AND ROTATE: ##\ndef chilly_font(size):\n fonti = pygame.font.Font(None, size)\n fonti.set_bold(0)\n return fonti\n ## DRAW TEXT IMAGE: ##\ndef draw_chilly_text (t, special, size, w, z, colory):\n fonty = chilly_font(size)\n IMAGEE = fonty.render(t, special, colory)\n screen.blit(IMAGEE, (w, z))\n\n\n ### some functions: ###\ndef in_repeating_boundy (n, b1, b2):\n if n < b1: n = b2\n if n > b2: n = b1\n return n\ndef in_boundy (n, b1, b2):\n if n < b1: n = b1\n if n > b2: n = b2\n return n\ndef in_boundy2D ((w, z), (w1, z1, w2, z2)):\n if w < w1: w = w1\n if w > w2: w = w2\n if z < z1: z = z1\n if z > z2: z = z2\n return w, z\ndef chilly_distance (w1, z1, w2, z2):\n return math.sqrt(math.pow(w1 - w2, 2) + math.pow(z1 - z2, 2))\ndef chilly_rect_touching_rect(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 >= ww1 and w2 <= wol1 and z2 >= zz1 and z2 <= zol1)\n\n ## keys and mouse stuff: ##\nglobal ky_held, ky_first_held, ky_time_last_pressed\nglobal mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\nnot_mouse_left_or_right_held = 1\nmouse_left_held = 0\nmouse_right_held = 0\nmouse_w = 0\nmouse_z = 0\nmouse_inn = 0\nky_held = [0]\nky_first_held = [0]\nky_time_last_pressed = [0]\nm = -1\nwhile (m < 500):\n m += 1\n ky_held += [0]\n ky_first_held += [0]\n ky_time_last_pressed += [0]\n\n ## MOUSE AND KEY FUNCTIONS: ##\ndef clear_kys():\n m = -1\n while (m < 500):\n m += 1\n ky_held[m] = 0\n ky_first_held[m] = 0\n ky_time_last_pressed[m] = 0\ndef mouse_left_pressed_CEV():\n global mouse_left_pressed\n if mouse_left_pressed: mouse_left_pressed = 0; return 1\ndef mouse_right_pressed_CEV():\n global mouse_right_pressed\n if mouse_right_pressed: mouse_right_pressed = 0; return 1\ndef old_style_ky(n):\n return (ky_first_held_CEV(n) or (ky_held[n] and ky_time_last_pressed[n] < time.time() - .3))\ndef ky_first_held_CEV(n):\n if (ky_first_held[n]):\n ky_first_held[n] = 0\n return 1\n else:\n return 0\ndef mouse_in_rect (w, z, wol, zol):\n return (mouse_w >= w and mouse_z >= z and mouse_w <= w + wol and mouse_z <= z + zol)\ndef mouse_in_circle (w, z, rad):\n dia = rad * 2\n if mouse_in_rect(w - rad, z - rad, w + dia, z + dia):\n return (chilly_distance(mouse_w, mouse_z, w, z) < rad)\n else:\n return 0\n\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\ndef check_for_keys():\n global mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\n global loopy, letter_hitty\n global not_mouse_left_or_right_held\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n if e.type == ACTIVEEVENT:\n mouse_inn = (e.gain and (e.state == 1 or e.state == 6))\n if not mouse_inn:\n mouse_w = 0\n mouse_z = 0\n if e.type == KEYDOWN:\n ky_held[e.key] = 1\n ky_first_held[e.key] = 1\n ky_time_last_pressed[e.key] = time.time()\n if (e.key >= 97 and e.key <= 122):\n letter_hitty = e.unicode.lower()\n if e.type == KEYUP:\n ky_held[e.key] = 0\n #ky_first_held[e.key] = 0\n if e.type == MOUSEMOTION:\n mouse_w = e.pos[0]\n mouse_z = e.pos[1]\n if e.type == MOUSEBUTTONUP:\n if e.button == 1: mouse_left_held = 0\n if e.button == 3: mouse_right_held = 0\n if not mouse_left_held and not mouse_right_held: not_mouse_left_or_right_held = 1\n if e.type == MOUSEBUTTONDOWN:\n mouse_left_pressed = e.button == 1\n mouse_right_pressed = e.button == 3\n mouse_left_held = mouse_left_held or e.button == 1\n mouse_right_held = mouse_right_held or e.button == 3\n if mouse_left_held or mouse_right_held: not_mouse_left_or_right_held = 0\n if e.type == JOYAXISMOTION: nnnnnn = 7\n if e.type == JOYBALLMOTION: nnnnnn = 8\n if e.type == JOYHATMOTION: nnnnnn = 9\n if e.type == JOYBUTTONUP: nnnnnn = 10\n if e.type == JOYBUTTONDOWN: nnnnnn = 11\n if e.type == VIDEORESIZE:\n global background, Dimage_editing_screen, screen, APPLICATION_w_size, APPLICATION_z_size\n APPLICATION_w_size = e.size[0]\n APPLICATION_z_size = e.size[1]\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n background = pygame.Surface((APPLICATION_w_size, APPLICATION_z_size))\n if e.type == VIDEOEXPOSE: nnnnnn = 13\n if e.type == USEREVENT: nnnnnn = 14\n\n ### MORE STUFF: ###\nHE_HE_surfacey = pygame.Surface((40, 40))\ncolor1 = (200, 200, 200)\ncolor2 = (200, 0, 0)\ncolor3 = (0, 200, 0)\ncolor4 = (130, 180, 180)\nblack_colory = (0, 0, 0)\nHE_HE_surfacey.fill(black_colory)\n\ndef try_to_push_block(w, z, wo, zo):\n if map[z][w] == 1:\n w_pushed = w + wo\n z_pushed = z + zo\n w_pushed, z_pushed = in_boundy2D((w_pushed, z_pushed), (0, 0, map_w_size, map_z_size))\n if map[z_pushed][w_pushed] == 0:\n map[z][w] = 0\n map[z_pushed][w_pushed] = 1\n\ndef draw_map():\n ww = 0\n while ww < map_w_size:\n ww += 1\n zz = 0\n while zz < map_z_size:\n zz += 1\n n = map[zz][ww]\n screen.blit(HE_HE_surfacey, (ww * 40, zz * 40))\n if n == 1:\n pygame.draw.rect(screen, color1, (ww * 40, zz * 40, 40, 40), 2)\n elif n == 2:\n #pygame.draw.rect(screen, color2, (ww * 40, zz * 40, 40, 40), 2)\n pygame.draw.circle(screen, color2, (ww * 40 + 20, zz * 40 + 20), 17, 2)\n elif n == 3:\n #pygame.draw.rect(screen, color3, (ww * 40, zz * 40, 40, 40), 2)\n locy_w = ww * 40\n locy_z = zz * 40\n point1 = (20 + locy_w, 10 + locy_z)\n point2 = (40 + locy_w, 12 + locy_z)\n point3 = (30 + locy_w, 19 + locy_z)\n point4 = (30 + locy_w, 30 + locy_z)\n point5 = (20 + locy_w, 20 + locy_z)\n points = (point1, point2, point3, point4, point5)\n pygame.draw.polygon(screen, color3, points, 2)\n elif n == 4:\n pygame.draw.rect(screen, color4, (ww * 40, zz * 40, 40, 40), 4)\n\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n\n# NOTE: w = x\n# NOTE: z = y\n# -- HE, HE, Bad habit of mine . . .\n\n ## MAIN: ##\nif __name__ == '__main__':\n\n # THE MAIN, MAIN, MAIN LOOP:\n loopy = 1\n while (loopy == 1):\n\n\n mouse_left_pressed = 0\n mouse_right_pressed = 0\n check_for_keys()\n\n draw_map()\n draw_chilly_text('Press the arrow keys to move . . .', 0, 20, 0, 0, (255, 255, 255))\n\n wa = player_w\n za = player_z\n map[player_z][player_w] = 0\n if old_style_ky(276): player_w -= 1\n if old_style_ky(273): player_z -= 1\n if old_style_ky(275): player_w += 1\n if old_style_ky(274): player_z += 1\n player_w, player_z = in_boundy2D((player_w, player_z), (0, 0, map_w_size, map_z_size))\n try_to_push_block(player_w, player_z, player_w - wa, player_z - za)\n if map[player_z][player_w] != 0:\n player_w = wa\n player_z = za\n map[player_z][player_w] = 3\n\n #if ky_first_held[27]: loopy = 0\n pygame.display.flip()", "user_title": "Anonymous", "datetimeon": "2007-07-29T17:05:04", "link": "pygame.draw", "id": 781}, {"content": "# Matthew N. Brown copyright 2007\n# Here is an example program that\n# draws a bouncing ball using: pygame.draw.circle\n#\n# You can copy this program on to\n# your own computer and run it.\n#\n\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\nif not pygame.font: print 'Warning, fonts disabled'\nif not pygame.mixer: print 'Warning, sound disabled'\nimport time\nimport gc\nimport math\n\npygame.init()\nAPPLICATION_w_size = 700\nAPPLICATION_z_size = 500\nscreen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n#screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), FULLSCREEN)\npygame.display.set_caption(\"HEHE test circle thingie program Matthew N. Brown copyright 2007\")\n#pygame.mouse.set_visible(0)\nglobal background\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0, 0, 0))\nscreen.blit(background, (0, 0))\npygame.display.flip()\nrandom.seed()\n\n\n ## IMAGE STRETCH AND ROTATE: ##\ndef HEHEHE_font(size):\n fonti = pygame.font.Font(None, size)\n fonti.set_bold(0)\n return fonti\n ## DRAW TEXT IMAGE: ##\ndef draw_HEHEHE_text (t, special, size, w, z, colory):\n fonty = HEHEHE_font(size)\n IMAGEE = fonty.render(t, special, colory)\n screen.blit(IMAGEE, (w, z))\n\n\n ### some functions: ###\ndef in_repeating_boundy (n, b1, b2):\n if n < b1: n = b2\n if n > b2: n = b1\n return n\ndef in_boundy (n, b1, b2):\n if n < b1: n = b1\n if n > b2: n = b2\n return n\ndef in_boundy2D ((w, z), (w1, z1, w2, z2)):\n if w < w1: w = w1\n if w > w2: w = w2\n if z < z1: z = z1\n if z > z2: z = z2\n return w, z\ndef HEHEHE_distance (w1, z1, w2, z2):\n return math.sqrt(math.pow(w1 - w2, 2) + math.pow(z1 - z2, 2))\ndef HEHEHE_rect_touching_rect(w1, z1, wol1, zol1, w2, z2, wol2, zol2):\n w2 -= w1\n z2 -= z1\n ww1 = -wol2\n zz1 = -zol2\n return (w2 >= ww1 and w2 <= wol1 and z2 >= zz1 and z2 <= zol1)\n\n ## keys and mouse stuff: ##\nglobal ky_held, ky_first_held, ky_time_last_pressed\nglobal mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\nnot_mouse_left_or_right_held = 1\nmouse_left_held = 0\nmouse_right_held = 0\nmouse_w = 0\nmouse_z = 0\nmouse_inn = 0\nky_held = [0]\nky_first_held = [0]\nky_time_last_pressed = [0]\nm = -1\nwhile (m < 500):\n m += 1\n ky_held += [0]\n ky_first_held += [0]\n ky_time_last_pressed += [0]\n\n ## MOUSE AND KEY FUNCTIONS: ##\ndef clear_kys():\n m = -1\n while (m < 500):\n m += 1\n ky_held[m] = 0\n ky_first_held[m] = 0\n ky_time_last_pressed[m] = 0\ndef mouse_left_pressed_CEV():\n global mouse_left_pressed\n if mouse_left_pressed: mouse_left_pressed = 0; return 1\ndef mouse_right_pressed_CEV():\n global mouse_right_pressed\n if mouse_right_pressed: mouse_right_pressed = 0; return 1\ndef old_style_ky(n):\n return (ky_first_held_CEV(n) or (ky_held[n] and ky_time_last_pressed[n] < time.time() - .3))\ndef ky_first_held_CEV(n):\n if (ky_first_held[n]):\n ky_first_held[n] = 0\n return 1\n else:\n return 0\ndef mouse_in_rect (w, z, wol, zol):\n return (mouse_w >= w and mouse_z >= z and mouse_w <= w + wol and mouse_z <= z + zol)\ndef mouse_in_circle (w, z, rad):\n dia = rad * 2\n if mouse_in_rect(w - rad, z - rad, w + dia, z + dia):\n return (HEHEHE_distance(mouse_w, mouse_z, w, z) < rad)\n else:\n return 0\n\n ## CHECK FOR: KEYBOARD, MOUSE, JOYSTICK, AND OTHERY INPUTY: ##\ndef check_for_keys():\n global mouse_w, mouse_z, mouse_inn, mouse_left_pressed, mouse_right_pressed, mouse_left_held, mouse_right_held\n global loopy, letter_hitty\n global not_mouse_left_or_right_held\n for e in pygame.event.get():\n if e.type == QUIT:\n loopy = 0\n if e.type == ACTIVEEVENT:\n mouse_inn = (e.gain and (e.state == 1 or e.state == 6))\n if not mouse_inn:\n mouse_w = 0\n mouse_z = 0\n if e.type == KEYDOWN:\n ky_held[e.key] = 1\n ky_first_held[e.key] = 1\n ky_time_last_pressed[e.key] = time.time()\n if (e.key >= 97 and e.key <= 122):\n letter_hitty = e.unicode.lower()\n if e.type == KEYUP:\n ky_held[e.key] = 0\n #ky_first_held[e.key] = 0\n if e.type == MOUSEMOTION:\n mouse_w = e.pos[0]\n mouse_z = e.pos[1]\n if e.type == MOUSEBUTTONUP:\n if e.button == 1: mouse_left_held = 0\n if e.button == 3: mouse_right_held = 0\n if not mouse_left_held and not mouse_right_held: not_mouse_left_or_right_held = 1\n if e.type == MOUSEBUTTONDOWN:\n mouse_left_pressed = e.button == 1\n mouse_right_pressed = e.button == 3\n mouse_left_held = mouse_left_held or e.button == 1\n mouse_right_held = mouse_right_held or e.button == 3\n if mouse_left_held or mouse_right_held: not_mouse_left_or_right_held = 0\n if e.type == JOYAXISMOTION: nnnnnn = 7\n if e.type == JOYBALLMOTION: nnnnnn = 8\n if e.type == JOYHATMOTION: nnnnnn = 9\n if e.type == JOYBUTTONUP: nnnnnn = 10\n if e.type == JOYBUTTONDOWN: nnnnnn = 11\n if e.type == VIDEORESIZE:\n global background, Dimage_editing_screen, screen, APPLICATION_w_size, APPLICATION_z_size\n APPLICATION_w_size = e.size[0]\n APPLICATION_z_size = e.size[1]\n screen = pygame.display.set_mode((APPLICATION_w_size, APPLICATION_z_size), RESIZABLE)\n background = pygame.Surface((APPLICATION_w_size, APPLICATION_z_size))\n if e.type == VIDEOEXPOSE: nnnnnn = 13\n if e.type == USEREVENT: nnnnnn = 14\n\n ### MORE STUFF: ###\nball_w = 30.0\nball_z = 20.0\n\nball_wol = 4.0\nball_zol = -1.0\n\ngravity_w = 0.0\ngravity_z = 1.0\n\nradius = 11.0\n\nmakes_ball_slower_per_bounce = 1.2\n\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n#######################################################################################\n\n# NOTE: w = x\n# NOTE: z = y\n# -- HE, HE, Bad habit of mine . . .\n\n ## MAIN: ##\nif __name__ == '__main__':\n\n # THE MAIN, MAIN, MAIN LOOP:\n loopy = 1\n while (loopy == 1):\n\n t = time.time()\n while t > time.time() - .03:\n pass\n mouse_left_pressed = 0\n mouse_right_pressed = 0\n check_for_keys()\n\n ball_wol += gravity_w\n ball_zol += gravity_z\n\n if old_style_ky(276): ball_wol -= 12\n if old_style_ky(273): ball_zol -= 22\n if old_style_ky(275): ball_wol += 12\n if old_style_ky(274): ball_zol += 22\n if ky_held[115]: ball_wol = 0; ball_zol = 0\n if ky_held[99]: ball_wol = (random.random() * 400) - 200; ball_zol = (random.random() * 400) - 200\n\n ball_w += ball_wol\n ball_z += ball_zol\n\n if ball_w < radius: ball_w = radius; ball_wol = -(ball_wol / makes_ball_slower_per_bounce)\n if ball_z < radius: ball_z = radius; ball_zol = -(ball_zol / makes_ball_slower_per_bounce)\n if ball_w > APPLICATION_w_size - radius: ball_w = APPLICATION_w_size - radius; ball_wol = -(ball_wol / makes_ball_slower_per_bounce)\n if ball_z > APPLICATION_z_size - radius: ball_z = APPLICATION_z_size - radius; ball_zol = -(ball_zol / makes_ball_slower_per_bounce)\n\n screen.fill((0, 0, 0))\n draw_HEHEHE_text('Press the arrow keys to move ball.', 0, 25, 0, 0, (255, 255, 255))\n draw_HEHEHE_text('Hold S to stop ball.', 0, 25, 0, 30, (255, 255, 255))\n draw_HEHEHE_text('press C to make ball go crazy.', 0, 25, 0, 70, (255, 255, 255))\n pygame.draw.circle(screen, (200, 200, 200), (int(ball_w), int(ball_z)), int(radius))\n\n #if ky_first_held[27]: loopy = 0\n pygame.display.flip()", "user_title": "Anonymous", "datetimeon": "2007-07-29T19:02:50", "link": "pygame.draw.circle", "id": 782}, {"content": "", "user_title": "Anonymous", "datetimeon": "2007-08-08T10:35:27", "link": "You have a great site. All in your web is very useful. Please keep on working.", "id": 796}, {"content": "Is this fast ? What is better for software systems ?", "user_title": "Anonymous", "datetimeon": "2007-08-12T18:03:51", "link": "Surface.blit", "id": 800}, {"content": "I have not tried photoshop or something to create alpha channels in bitmaps directly. Instead, I use a mask color in a 'normal' bitmap, and make that look transparant in the icon.\ncreate a bitmap in mspaint, black areas will be transparant, size 32x32 pixels, save it as 'icon.bmp'.\n \nthen create a file named icon.py and put this in it:\n###\nimport pygame\n\ndef seticon(iconname):\n \"\"\"\n give an iconname, a bitmap sized 32x32 pixels, black (0,0,0) will be alpha channel\n \n the windowicon will be set to the bitmap, but the black pixels will be full alpha channel\n \n can only be called once after pygame.init() and before somewindow = pygame.display.set_mode()\n \"\"\"\n icon=pygame.Surface((32,32))\n icon.set_colorkey((0,0,0))#and call that color transparant\n rawicon=pygame.image.load(iconname)#must be 32x32, black is transparant\n for i in range(0,32):\n for j in range(0,32):\n icon.set_at((i,j), rawicon.get_at((i,j)))\n pygame.display.set_icon(icon)#set wind\n\npygame.init()\nseticon('icon.bmp')\nwindow=pygame.display.set_mode((250,250))\nbackground=pygame.Surface(window.get_size())\nbackground.fill((50,50,50))\n \nwhile 1:\n for event in pygame.event.get():\n if not event.type == pygame.MOUSEMOTION:#print all events, but not the mousemoves :) for feedback info\n print str(event)\n if event.type == pygame.QUIT: # close window cross (upper right corner) pressed: exit\n raise SystemExit\n elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: #escape pressed: exit\n raise SystemExit\n window.blit(background, (0, 0))#fresh background \n #insert other blitty things here onto the window \n pygame.display.flip()#show completed window\n###\nto see the effect: a transparant icon, based on a normal bitmap with a mask color. \nYou can easily modify the code to use an different mask color or filename if needed.", "user_title": "Anonymous", "datetimeon": "2007-08-12T20:35:22", "link": "pygame.display.set_icon", "id": 803}, {"content": "This function's resume is wrong, it says:\nGroup.has(*sprites): return None\nshould say\nGroup.has(*sprites): return Boolean", "user_title": "Anonymous", "datetimeon": "2007-08-13T03:15:52", "link": "Group.has", "id": 805}, {"content": "In other words, this returns the area in which 2 Rects overlap.\nThis implies that rectA.clip(rectB) == rectB.clip(rectA).", "user_title": "Anonymous", "datetimeon": "2007-08-17T03:35:39", "link": "Rect.clip", "id": 809}, {"content": "There should be more types of sounds loadable. Also a way to save them.", "user_title": "Anonymous", "datetimeon": "2007-08-25T14:44:46", "link": "pygame.mixer.Sound", "id": 815}, {"content": "if you want to use the same function but from module PIL\n\n from PIL import Image\n im = pygame.image.load (\"image.png\")\n s = pygame.image.tostring (im, \"RGBX\")\n temp = Image.fromstring (\"RGBX\", im.get_size (), s)\n tu = (0,0, im.get_size () [0]-1, im.get_size () [1] - 1)\n temp = temp.transform (size2, Image.EXTENT, tu, Image.BICUBIC)\n mode = temp.mode\n size = temp.size\n data = temp.tostring()\n res = pygame.image.fromstring (data, size, mode)", "user_title": "Anonymous", "datetimeon": "2007-08-26T09:36:23", "link": "pygame.transform.rotate", "id": 817}, {"content": "if you want to use the same function but from module PIL\n\n from PIL import Image\n im = pygame.image.load (\"image.png\")\n s = pygame.image.tostring (im, \"RGBX\")\n temp = Image.fromstring (\"RGBX\", im.get_size (), s)\n tu = (0,0, im.get_size () [0]-1, im.get_size () [1] - 1)\n temp = temp.transform (size2, Image.EXTENT, tu, Image.BICUBIC)\n mode = temp.mode\n size = temp.size\n data = temp.tostring()\n res = pygame.image.fromstring (data, size, mode)", "user_title": "Anonymous", "datetimeon": "2007-08-26T09:37:15", "link": "pygame.transform.scale", "id": 818}, {"content": "Numbers greater than 1.0 seem to be interpreted as 1.0.\nNegative numbers are made positive (absolute value)\n\n-4.0 = 4.0 = 1.0", "user_title": "Anonymous", "datetimeon": "2007-08-26T21:15:03", "link": "Sound.set_volume", "id": 820}, {"content": "If you try to use the alpha in [Color] its not applied, \nbut Draw.lines applies alpha in [Color]", "user_title": "Anonymous", "datetimeon": "2007-08-27T12:19:45", "link": "pygame.draw.aalines", "id": 821}, {"content": "Is it possible to use this on only certain keys,\nor to use different values for different groups of keys?\n\nFor example, say you wanted to have a certain value assigned to the player movement keys,\nbut a different value assigned to the attack keys, and no value set for the menu keys.", "user_title": "Anonymous", "datetimeon": "2007-08-31T04:29:56", "link": "pygame.key.set_repeat", "id": 823}, {"content": "That depends on a number of things. For digital D-pads (like on a SNES controller)\nthe values reported will always be \"full blast\" because that's how the gamepad\nhardware reports the direction of the D-pad. For analog sticks, like the ones in\nthe middle of a PS2 controller, they will usually report a value in the range of\na 32-bit integer (or maybe a 16-bit integer, or even a float--I don't really know).\nIt all depends on the hardware, not to mention the drivers of your OS.", "user_title": "Anonymous", "datetimeon": "2007-09-04T04:16:59", "link": "pygame.joystick.Joystick", "id": 825}, {"content": "This will make one single pixel a Color at coordanates x, y on a Surface:\npygame.draw.rect(Surface, Color, (x, y, 1, 1))", "user_title": "Anonymous", "datetimeon": "2007-09-05T21:50:41", "link": "pygame.draw.rect", "id": 826}, {"content": "Hi Tim,\n\nMaybe you realised your error by now, but here is a little clarification for the\npeople reading your comment.\n\nYou created a square with a side of 4 pixel at the position (0, 0). \n\n 0 1 2 3 4\n0 x x x x .\n1 x x x x .\n2 x x x x .\n3 x x x x .\n4 . . . . .\n\nSo of course any position with x or y >= 4 will be outside the square.", "user_title": "Anonymous", "datetimeon": "2008-05-04T00:13:13", "link": "Rect.collidepoint", "id": 1821}, {"content": "How to draw a single pixel: draw a circle with radius zero! Took me a while to find this.\n\ncircle( ..., 0 ) will give you a single pixel", "user_title": "Anonymous", "datetimeon": "2007-09-08T02:17:09", "link": "pygame.draw.circle", "id": 837}, {"content": "It seems to be true that None will cause set_allowed to BLOCK all events.\n...even though it was April 1st.\n\nset_blocked(None) doesn't seem to have any effect like described above.", "user_title": "Anonymous", "datetimeon": "2007-09-09T17:05:37", "link": "pygame.event.set_allowed", "id": 847}, {"content": "set_blocked(None) doesn't seem to have this effect at all, see the comment in set_allowed after April 1st.", "user_title": "Anonymous", "datetimeon": "2007-09-09T17:06:19", "link": "pygame.event.set_blocked", "id": 848}, {"content": "Antialised text *does* work on black backgrounds, you just have to be careful only to\nblit it once, because the parts with less than full alpha will build up \n(very quickly if you are blitting over and over.)", "user_title": "Anonymous", "datetimeon": "2007-09-12T21:29:31", "link": "Font.render", "id": 851}, {"content": "There is an alternative to setting pixels one-at-a-time that is much, much faster. Pygame's Surfarray module will allow you to access the pixels like an array.\n\nIf you need to manipulate pixels on an individual level, it is strongly recommended that you use Surfarrays instead of set_at.", "user_title": "Anonymous", "datetimeon": "2008-05-07T10:56:34", "link": "Surface.set_at", "id": 1842}, {"content": "fade out does NOT block till it is finished in windows!!!!", "user_title": "Anonymous", "datetimeon": "2007-11-12T00:14:47", "link": "pygame.mixer.music.fadeout", "id": 1084}, {"content": "There is no explanation of 'color' argument...?", "user_title": "Anonymous", "datetimeon": "2007-10-09T13:57:03", "link": "pygame.draw.rect", "id": 913}, {"content": "colors are usually done as a tuple\n(red light out of 255,green light out of 255, blue light out of 255).", "user_title": "Anonymous", "datetimeon": "2007-10-09T20:01:34", "link": "pygame.draw.rect", "id": 915}, {"content": "OGG is a container format... They probably meant only OGG/Vorbis.", "user_title": "Anonymous", "datetimeon": "2007-10-17T11:20:52", "link": "pygame.mixer.Sound", "id": 932}, {"content": "You can use Surface.set_at((x,y), colour) to set a pixel.", "user_title": "Anonymous", "datetimeon": "2007-10-17T16:49:42", "link": "pygame.draw.rect", "id": 933}, {"content": "It is a dissapointment to discover that \\n does not\nwork with the default font and merely shows a box.", "user_title": "Anonymous", "datetimeon": "2007-10-18T18:45:51", "link": "Font.render", "id": 937}, {"content": "# This is an example that uses pygame.draw.rect:\nimport os, sys\nimport random\nimport pygame\nfrom pygame.locals import *\npygame.init()\nAPPLICATION_x_size = 400\nAPPLICATION_y_size = 300\nscreen = pygame.display.set_mode((APPLICATION_x_size, APPLICATION_y_size))\npygame.display.set_caption('Fun Boring Example comes with Source Code too!!')\npygame.mouse.set_visible(True)\n#pygame.mouse.set_visible(False)\nblack_square_that_is_the_size_of_the_screen = pygame.Surface(screen.get_size())\nblack_square_that_is_the_size_of_the_screen.fill((0, 0, 0))\nscreen.blit(black_square_that_is_the_size_of_the_screen, (0, 0))\npygame.display.flip()\nWeeee = True\nwhile Weeee:\n # a color can be: (0 to 255, 0 to 255, 0 to 255)\n My_red_color = (255, 0, 0)\n My_blue_color = (0, 0, 255)\n My_green_color = (0, 255, 0)\n My_yellow_color = (255, 255, 0)\n WHITE_WHITE_HOORAY = (255, 255, 255)\n My_light_red_color = (255, 180, 180)\n My_light_blue_color = (190, 190, 255)\n # \"screen.set_at((x, y), Color)\" and \"pygame.draw.rect(screen, Color, (x, y, x_size, y_size))\" draw colors on to an \"in computer memory image\" called: \"screen\"\n screen.set_at(( 1, 1), My_yellow_color)\n screen.set_at(( 2, 2), My_yellow_color)\n screen.set_at(( 3, 3), My_yellow_color)\n screen.set_at(( 4, 4), My_yellow_color)\n screen.set_at(( 5, 5), My_yellow_color)\n screen.set_at(( 6, 6), My_yellow_color)\n screen.set_at(( 7, 7), My_yellow_color)\n screen.set_at(( 8, 8), My_yellow_color)\n screen.set_at(( 9, 9), My_yellow_color)\n screen.set_at((10, 10), My_yellow_color)\n screen.set_at((11, 11), My_yellow_color)\n screen.set_at((12, 12), My_yellow_color)\n screen.set_at((13, 13), My_yellow_color)\n screen.set_at((14, 14), My_yellow_color)\n screen.set_at((15, 15), My_yellow_color)\n screen.set_at((16, 16), My_yellow_color)\n screen.set_at((17, 17), My_yellow_color)\n screen.set_at((18, 18), My_yellow_color)\n screen.set_at((19, 19), My_yellow_color)\n screen.set_at((20, 20), My_yellow_color)\n pygame.draw.rect(screen, My_red_color, (50, 50, 10, 10))\n pygame.draw.rect(screen, My_red_color, (50, 120, 20, 20))\n pygame.draw.rect(screen, My_blue_color, (50, 150, 30, 30))\n pygame.draw.rect(screen, My_blue_color, (50, 1000, 1000, 10))\n pygame.draw.rect(screen, My_green_color, (200, 10, 40, 40))\n pygame.draw.rect(screen, My_light_red_color, (10, 200, 50, 50))\n pygame.draw.rect(screen, My_light_blue_color, (200, 200, 60, 60))\n pygame.draw.rect(screen, My_light_blue_color, (100, 200, 10, 2))\n pygame.draw.rect(screen, WHITE_WHITE_HOORAY, (0, 100, 50, 52))\n # If you delete the below line you should no longer see the vibrant colors.\n pygame.display.flip()\n # if the 'X' button is pressed the window should close:\n Geesh = pygame.event.get()\n if len(Geesh) > 0:\n if Geesh[0].type == QUIT: Weeee = False\n## Once this line is reached the window should close", "user_title": "Anonymous", "datetimeon": "2007-10-18T19:23:51", "link": "pygame.draw.rect", "id": 938}, {"content": "If your program has sources of events that are not managed by pygame, such as\nnetwork socket data, or large files, you must either add a thread that selects\non the source and injects pygame events, or poll the source briefly and rapidly.", "user_title": "Anonymous", "datetimeon": "2007-10-23T23:20:11", "link": "pygame.event", "id": 955}, {"content": "It never seems to be able to load this (error reported, cannot read). But when I run a script that directly runs it(without the loop), it works fine\n\n(songs is a list of filenames loaded form a .txt file)\n\ncurrent_song = 0\nwhile 1:\n if pygame.mixer.music.get_busy() == False:\n print songs[current_song]\n pygame.mixer.music.load(songs[current_song])\n pygame.mixer.music.play() \n current_song += 1", "user_title": "Anonymous", "datetimeon": "2007-10-25T21:19:18", "link": "pygame.mixer.music.load", "id": 965}, {"content": "When you make an icon make a 16x16 icon and then scale it to 32x32 pixels.\nIf you make it 16x16 pixels it looks distorted.\n\nI usally have a transparent 32x32 .gif icon for my games.", "user_title": "Anonymous", "datetimeon": "2007-11-01T18:47:48", "link": "pygame.display.set_icon", "id": 1002}, {"content": "When you make an icon make a 16x16 icon and then scale it to 32x32 pixels.\nIf you make it 16x16 pixels it looks distorted.\n\nI usally have a transparent 32x32 .gif icon for my games.", "user_title": "Anonymous", "datetimeon": "2007-11-01T18:49:22", "link": "pygame.display.set_icon", "id": 1003}, {"content": "Always set the icon before you call pygame.display.set_mode", "user_title": "Anonymous", "datetimeon": "2007-11-01T18:50:24", "link": "pygame.display.set_icon", "id": 1004}, {"content": "Here's a quick script for loading images:\n\ndef load_image(file, colorkey=False):\n file = os.path.join('data', file)\n try:\n image = pygame.image.load(file)\n colorkey = image.get_at((0, 0))\n if colorkey is True:\n image.set_colorkey(colorkey, pygame.RLEACCEL)\n except:\n print 'Unable to load: ' + file\n return image.convert_alpha() #Convert any transparency in the image", "user_title": "Anonymous", "datetimeon": "2007-11-01T18:56:17", "link": "pygame.image.load", "id": 1005}, {"content": "I agree that it is a dissapointment about \\n, but anti-aliasing works fine for me!", "user_title": "Anonymous", "datetimeon": "2007-11-01T19:00:33", "link": "Font.render", "id": 1006}, {"content": "this gives me 6 modules initialised OK, 0 failed.\nbut i only know of 5 modules that have to be inited:\ncdrom, display, font, joystick, mixer. which one did i miss?", "user_title": "Anonymous", "datetimeon": "2007-11-03T04:27:28", "link": "pygame.init", "id": 1013}, {"content": "Yeah, it is fast, but what do you want to compare it to when you\nask \"what is better\"? Within PyGame, there's no alternative to\nusing Surface.blit. I'd suggest you either use that, or if you find\nit too slow (but really make sure it's too slow for you, i.e. test\nif the real problem might be using flip instead of update), use\nOpenGL.", "user_title": "Anonymous", "datetimeon": "2007-11-05T05:06:25", "link": "Surface.blit", "id": 1032}, {"content": "Rects do not move to floating point numbers. Only integers.\n\n\nSo if you do:\n\nself.rect.move_ip(4.5, 0)\n\nit will actually execute:\n\nself.rect.move_ip(4, 0)\n\n\nThis limitation is really bad if you're making a small screen platformer.\nI hope that Rects will move to floating point numbers in pygame 1.8.", "user_title": "Anonymous", "datetimeon": "2007-11-05T09:47:30", "link": "Rect.move_ip", "id": 1034}, {"content": "Copy this to your computer and save it as a .py file to run a little trig demo.\n\n\n\n#! usr/bin/env python\n\nimport pygame, math\nfrom pygame.locals import *\n\nclass Ship:\n def __init__(self):\n self.image=pygame.Surface((40, 40))\n self.rect=self.image.get_rect(center=(320,240))\n self.x=200\n self.y=150\n self.x_vel=0\n self.y_vel=0\n self.angle=0\n self.point_list = [(0, -20), (2.25, -20), (3.0, -6), (4.05, -20)]\n def update(self):\n self.rect.centerx=self.x\n self.rect.centery=self.y\n self.x+=self.x_vel\n self.y+=self.y_vel\n key = pygame.key.get_pressed()\n if key[K_RIGHT]:\n self.angle -= 4\n if key[K_LEFT]:\n self.angle += 4\n if key[K_UP]:\n self.accel(0.1)\n if key[K_DOWN]:\n self.accel(-0.1)\n def draw(self, surface):\n surface.blit(self.image, self.rect)\n self.image.fill((0, 0, 0))\n\tpoint_list = []\n\tself.angle2 = math.radians(self.angle)\n\tfor p in self.point_list:\n radian, radius = p\n x = int(math.sin(radian+self.angle2)*radius)\n y = int(math.cos(radian+self.angle2)*radius)\n\t point_list.append((x+self.image.get_width()/2,y+self.image.get_height()/2))\n\tpygame.draw.polygon(self.image, (255,255,255), point_list, 1)\n def accel(self, accel_speed):\n self.x_vel += math.sin(self.angle*2*math.pi/360)*-accel_speed\n self.y_vel += math.cos(self.angle*2*math.pi/360)*-accel_speed\n def wrap(self, surface):\n if self.x >= surface.get_width() + self.image.get_width()/2:\n self.x = -self.image.get_width()/2\n if self.x <= -self.image.get_width()/2 - 1:\n self.x = surface.get_width() + self.image.get_width()/2\n if self.y >= surface.get_height() + self.image.get_height()/2:\n self.y = -self.image.get_height()/2\n if self.y <= -self.image.get_height()/2 - 1:\n self.y = surface.get_height() + self.image.get_height()/2\n\ndef main():\n pygame.init()\n pygame.display.set_caption('trig demo.py')\n screen = pygame.display.set_mode((400, 300))\n ship = Ship()\n clock = pygame.time.Clock()\n\n while 1:\n clock.tick(60)\n event = pygame.event.poll()\n if event.type == QUIT:\n return\n if event.type == KEYDOWN:\n if event.key == K_ESCAPE:\n return\n\n screen.fill((0, 0, 0))\n ship.draw(screen)\n ship.update()\n ship.wrap(screen)\n pygame.display.flip()\n\n\nif __name__ == '__main__':\n main()", "user_title": "Anonymous", "datetimeon": "2007-11-07T17:25:07", "link": "pygame.draw", "id": 1050}, {"content": "Here is a neat little trig demo:\n\n\n\n#! usr/bin/env python\n\nimport pygame, math\nfrom pygame.locals import *\n\nclass Ship:\n def __init__(self):\n self.image=pygame.Surface((40, 40))\n self.rect=self.image.get_rect(center=(320,240))\n self.x=200\n self.y=150\n self.x_vel=0\n self.y_vel=0\n self.angle=0\n self.point_list = [(0, -20), (2.25, -20), (3.0, -6), (4.05, -20)]\n def update(self):\n self.rect.centerx=self.x\n self.rect.centery=self.y\n self.x+=self.x_vel\n self.y+=self.y_vel\n key = pygame.key.get_pressed()\n if key[K_RIGHT]:\n self.angle -= 4\n if key[K_LEFT]:\n self.angle += 4\n if key[K_UP]:\n self.accel(0.1)\n if key[K_DOWN]:\n self.accel(-0.1)\n def draw(self, surface):\n surface.blit(self.image, self.rect)\n self.image.fill((0, 0, 0))\n\tpoint_list = []\n\tself.angle2 = math.radians(self.angle)\n\tfor p in self.point_list:\n radian, radius = p\n x = int(math.sin(radian+self.angle2)*radius)\n y = int(math.cos(radian+self.angle2)*radius)\n\t point_list.append((x+self.image.get_width()/2,y+self.image.get_height()/2))\n\tpygame.draw.polygon(self.image, (255,255,255), point_list, 1)\n def accel(self, accel_speed):\n self.x_vel += math.sin(self.angle*2*math.pi/360)*-accel_speed\n self.y_vel += math.cos(self.angle*2*math.pi/360)*-accel_speed\n def wrap(self, surface):\n if self.x >= surface.get_width() + self.image.get_width()/2:\n self.x = -self.image.get_width()/2\n if self.x <= -self.image.get_width()/2 - 1:\n self.x = surface.get_width() + self.image.get_width()/2\n if self.y >= surface.get_height() + self.image.get_height()/2:\n self.y = -self.image.get_height()/2\n if self.y <= -self.image.get_height()/2 - 1:\n self.y = surface.get_height() + self.image.get_height()/2\n\ndef main():\n pygame.init()\n pygame.display.set_caption('trig demo.py')\n screen = pygame.display.set_mode((400, 300))\n ship = Ship()\n clock = pygame.time.Clock()\n\n while 1:\n clock.tick(60)\n event = pygame.event.poll()\n if event.type == QUIT:\n return\n if event.type == KEYDOWN:\n if event.key == K_ESCAPE:\n return\n\n screen.fill((0, 0, 0))\n ship.draw(screen)\n ship.update()\n ship.wrap(screen)\n pygame.display.flip()\n\n\nif __name__ == '__main__':\n main()", "user_title": "Anonymous", "datetimeon": "2007-11-07T17:27:28", "link": "pygame", "id": 1051}, {"content": "If you have pygame 1.8 (which is in pre or something) the scrap module has to init.", "user_title": "Anonymous", "datetimeon": "2007-11-08T08:55:27", "link": "pygame.init", "id": 1055}, {"content": "\"Dest can either be pair of coordinates representing the upper left corner of the source. A Rect can also be passed as the destination and the topleft corner of the rectangle will be used as the position for the blit.\"\nEw.\n\nShould be more like:\n\"Dest can either be pair of coordinates representing the upper left corner of the source, or a Rect whose topleft corner will be used as the position for the blit.\"", "user_title": "Anonymous", "datetimeon": "2008-05-19T20:16:05", "link": "Surface.blit", "id": 1907}, {"content": "Do they ever update this docs?", "user_title": "Anonymous", "datetimeon": "2008-05-26T20:11:33", "link": "Group.has", "id": 1952}, {"content": "You could use draw.rect() instead of draw.aaline()", "user_title": "Anonymous", "datetimeon": "2008-05-29T07:54:18", "link": "pygame.draw.aaline", "id": 1966}, {"content": "Why does not it explain the format?", "user_title": "Anonymous", "datetimeon": "2008-05-31T16:06:06", "link": "pygame.mixer.get_num_channels", "id": 1978}, {"content": "Thanks for the list :)", "user_title": "Anonymous", "datetimeon": "2008-06-12T16:50:25", "link": "pygame.event.Event", "id": 2036}, {"content": "Under pygame 1.7.1 it returns the number of currently busy channels (under pygame 1.7.1)", "user_title": "Anonymous", "datetimeon": "2008-06-19T12:27:34", "link": "pygame.mixer.get_busy", "id": 2068}, {"content": "osx is unix based", "user_title": "Anonymous", "datetimeon": "2008-06-22T10:23:28", "link": "pygame.display.init", "id": 2080}, {"content": "While it does state this in the documentation, I misread it at first, so I \nthought that I would try clarifying.\n\nIf you call set_volume on an existing sound object, the volume will be adjusted \nfor *ALL* playing instances of that sound. For instance, say that you are playing\nsound object 'foo' five times. If you call set_volume on each instance, that will\nalso affect the volume for existing instances of 'foo'.\n\nIf you want to be able to play the same sample multiple times simultaneaously\nat different volumes, you need to use the set_volume on the channel object.\n\nCheers", "user_title": "Anonymous", "datetimeon": "2008-06-23T22:31:09", "link": "Sound.set_volume", "id": 2088}, {"content": "after executing pygame.mixer.init i always get \"there is no soundcard\" and my script always crash after it displays that", "user_title": "Anonymous", "datetimeon": "2010-11-25T04:41:20", "link": "pygame.mixer.init", "id": 3324}, {"content": "currently have a :\narning once: This application, or a library it uses, is using NSQuickDrawView, which has been deprecated. Apps should cease use of QuickDraw and move to Quartz.\n\non OS X.5 and pygame 1.8.0", "user_title": "Anonymous", "datetimeon": "2008-07-06T23:39:04", "link": "pygame.display.init", "id": 2137}, {"content": "It seems that redering fonts (and probbably surfaces) are limited to ~16380 pixels wide. An example of this is\n\nimport pygame\npygame.init()\n\ncharList = ['a','A','b','B','q','Q']\n\nfont = pygame.font.Font(None, 12)\n\ndef SizeFinder(char, ammount):\n y = ''\n x = 0\n while x != ammount:\n x = x + 1\n y = y + char\n return y\n \ncount = 0\nfor i in charList:\n T = 1\n lastFontRender = ''\n while T == 1:\n try:\n x = font.render(SizeFinder(i, count), True, [0,0,0])\n lastFontRender = x\n count = count + 1\n except:\n print i, 'fails at ', str(count), 'characters'\n print 'Last font render: ' + str(lastFontRender)\n count = 0\n T = 0", "user_title": "Anonymous", "datetimeon": "2008-07-07T04:35:13", "link": "Font.render", "id": 2139}, {"content": "Just a note: Pygame/Python will crash if you provide an invalid filename (for instance, something with the character ':' in it).", "user_title": "Anonymous", "datetimeon": "2008-07-13T18:20:08", "link": "pygame.image.save", "id": 2164}, {"content": "yeh", "user_title": "Anonymous", "datetimeon": "2010-11-24T23:22:16", "link": "pygame.transform.flip", "id": 3308}, {"content": "Alternately, instead of using sprite.rect.move_ip(...) on each update, reset \nsprite.rect.center (or the locational anchor of your choice). Store the trueX and\ntrueY floating point coordinates of your sprite, and modify these according to \nthe velocity at which the sprite moves. When it's time to redraw the sprite in\nthe new location, set ....center = (round(trueX),round(trueY)) and blit. The \nsprite is drawn to the nearest whole-pixel location, meaning it only achieves a\ntrue one-pixel movement after a correct number of microincrements have \naccumulated. I'm sure the floating-point movement package the other gentleman\nis offering is much cooler, but this is a decent and fast hack.", "user_title": "Anonymous", "datetimeon": "2008-07-16T07:04:06", "link": "Rect.move_ip", "id": 2174}, {"content": "+1 thanks for the list", "user_title": "Anonymous", "datetimeon": "2008-07-27T12:16:36", "link": "pygame.event.Event", "id": 2220}, {"content": "The previous example here won't work correctly due\nto a typo, and will not do what you expect due to\na logical error.\nTry the following:\n\nfor event in pygame.event.get() :\n if event.type == pygame.KEYDOWN :\n if event.key == pygame.K_SPACE :\n print \"Space bar pressed down.\"\n elif event.key == pygame.K_ESCAPE :\n print \"Escape key pressed down.\"\n elif event.type == pygame.KEYUP :\n if event.key == pygame.K_SPACE :\n print \"Space bar released.\"\n elif event.key == pygame.K_ESCAPE :\n print \"Escape key released.\"", "user_title": "Anonymous", "datetimeon": "2008-07-29T23:08:09", "link": "pygame.event.get", "id": 2229}, {"content": "while I try :\nol = pygame.Overlay(YVYU_OVERLAY,(600,480))\nprint ol.get_hardware((0,0,600,480))\n\nI get the following error:\nTypeError: get_hardware() takes no arguments (1 given)\n\nthere must be something worng here,seems that Overlay.get_hardware takes NO arguments \n\nchange above code into :\nol = pygame.Overlay(YVYU_OVERLAY,(600,480))\nprint ol.get_hardware()\n\nand it works fine", "user_title": "Anonymous", "datetimeon": "2008-07-30T23:30:23", "link": "Overlay.get_hardware", "id": 2234}, {"content": "I left the repeat function default and it's not suppose to repeat but it still\ndoes..\ncan someone help me?", "user_title": "Anonymous", "datetimeon": "2008-07-31T23:36:27", "link": "pygame.key.set_repeat", "id": 2239}, {"content": "for event in pygame.event.get():\n if event.type is pygame.QUIT:\n pass\n\n if event.type is KEYDOWN:\n\n _ = pygame.key.name(event.key)\n print _\n\n if _ is \"left\":\n chara.move(_)\n elif _ is \"right\":\n chara.move(_)\n elif _ is \"up\":\n chara.move(_)\n elif _ is \"down\":\n chara.move(_)", "user_title": "Anonymous", "datetimeon": "2008-08-01T17:31:21", "link": "pygame.key.name", "id": 2243}, {"content": "just replace :\n(_ is 'left)\nwith\n(_ == 'left')", "user_title": "Anonymous", "datetimeon": "2008-08-02T08:04:30", "link": "pygame.key.name", "id": 2247}, {"content": "_ = pygame.Surface((x, y))\npygame.transform.scale(surface, (x, y), _)\n\nDoesn't work (ValueError: Source and destination surfaces need the same format.), while\n\n_ = pygame.Surface((x, y))\npygame.transform.smoothscale(surface, (x, y), _)\n\nIs ok !", "user_title": "Anonymous", "datetimeon": "2008-08-06T11:08:27", "link": "pygame.transform.scale", "id": 2259}, {"content": "This returns None for me. Tiger, 10.4", "user_title": "Anonymous", "datetimeon": "2008-08-06T15:30:15", "link": "pygame.font.get_fonts", "id": 2260}, {"content": "On Ubuntu 8.04 i got \"None\" too. See this thread:\nhttps://bugs.launchpad.net/ubuntu/+source/pygame/+bug/209967;\nit's a bug! Has links to .deb packages upgraded to pygame version 1.8.", "user_title": "Anonymous", "datetimeon": "2008-08-11T03:40:22", "link": "pygame.font.get_fonts", "id": 2261}, {"content": "for event in pygame.event.get():\n _ = pygame.key.name(event.key)\n \n if _ == 'left' or _ == 'right' or _ == 'up' or _ == 'down':\n self.player.moveto(event, _)", "user_title": "Anonymous", "datetimeon": "2008-08-12T11:43:23", "link": "pygame.event.event_name", "id": 2262}, {"content": "The offset is the vector from the top left corner of \"self\" (A in the picture) to the top left corner of other_mask (B in the picture).", "user_title": "Anonymous", "datetimeon": "2008-08-16T17:32:44", "link": "Mask.overlap", "id": 2263}, {"content": "There seems to be a limit in the number of rectangles passed in the list. \nI noted that some were not refreshed. Dividing the list in three smaller lists seemed to solve the problem.", "user_title": "Anonymous", "datetimeon": "2008-08-21T09:05:08", "link": "pygame.display.update", "id": 2267}, {"content": "That should say 'Font.set_italic(bool)', I believe.", "user_title": "Anonymous", "datetimeon": "2008-09-12T21:33:27", "link": "Font.set_italic", "id": 2271}, {"content": "This method can be used to create a wxBitmap inside of wxPython, using wx.BitmapFromBufferRGB or RGBA.\n\nbmp = wx.BitmapFromBufferRGB( surface.get_width(), surface.get_height(), surface.get_buffer() )\n\nwx.BitmapFromBufferRGBA must be used if the surface contains per pixel alpha data.", "user_title": "Anonymous", "datetimeon": "2008-09-12T22:54:37", "link": "Surface.get_buffer", "id": 2272}, {"content": "Can pygame.movie be used to play mpeg4 movies full-screen? \nCan I draw on the screen while the movie is being played? Trap the mouse\nand keyboard while this all is being done? Thanks for all the help.", "user_title": "Anonymous", "datetimeon": "2008-09-20T11:08:59", "link": "pygame.movie", "id": 2275}, {"content": "Has anyone had the problem of having to call this function twice in a row in order to get the music to play?", "user_title": "Anonymous", "datetimeon": "2008-09-25T18:00:14", "link": "pygame.mixer.music.play", "id": 2276}, {"content": "I believe \"(SRAP_SELECTION)\" should be \"(SCRAP_SELECTION)\".", "user_title": "Anonymous", "datetimeon": "2008-09-27T09:36:53", "link": "pygame.scrap.set_mode", "id": 2277}, {"content": "seems that you have typo in:\n\n pygame.moouse.get_pressed(): return (button1, button2, button3)\n\nit should be:\n\n pygame.mouse.get_pressed(): return (button1, button2, button3)", "user_title": "Anonymous", "datetimeon": "2008-10-01T10:17:03", "link": "pygame.mouse.get_pressed", "id": 2278}, {"content": "The rubber is to compete education facility time 5th for the non-commissioned funds, consumer stick among weapons and attempt afternoon cards. , http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate36 medical loans bad credit, dgtn, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate33 credit management lp, 814, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate64 no fax cash advances, 763, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate3 loan modification companies ca, 8-))), http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate49 my credit history report, :OOO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate47 bad credit mortgage refinance, gbdg, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate17 loans for people with bad credit, dvebum, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate26 click, enkl, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate28 personal loans with bad credit, sruc, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate62 no credit check cash loans, 8-OOO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate10 home loans, 8-(, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate52 here, :]], http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate42 commercial mortgage lenders, 645653, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate59 no check cash advance, =-PP, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate15 government loans for small business, rcjjhv, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate48 mortgage rate, :-D, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate53 my payday loan, 300, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate61 click, 8O, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate20 bad credit lenders personal loans, %-PPP, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate11 loans apply, =-OO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate44 mortgage loan, 672, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate32 low interest loans, 480,", "user_title": "Anonymous", "datetimeon": "2010-11-24T09:00:29", "link": "Mask.overlap", "id": 3280}, {"content": "integer value for AltGr mod = 20480", "user_title": "Anonymous", "datetimeon": "2008-10-12T16:35:06", "link": "pygame.key.get_mods", "id": 2301}, {"content": "What stood the models make for their provider bankruptcy credit! , http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate34 debt management credit counseling, %-)), http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate12 bad credit personal loans banks, kql, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate22 payday advance loans, 567911, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate54 national payday, buhs, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate19 direct lender loans, %))), http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate47 refinance mortgage loan, 612923, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate17 loans for college, 2511, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate45 mortgage loans rates, lss, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate30 residential lot loans, =-[[, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate7 loan rate home, 684, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate26 student loans company, pzvr, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate51 improve my credit score, 000, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate58 how to create a new credit file, 9990, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate62 no credit check cash loans, 8DDD, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate52 here, %-P, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate42 reverse mortgage lenders, =OOO, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate15 loans for small business women, wucgr, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate48 link, 26395, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate9 student loans repayment uk, 997, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate5 online payday loan, yffts, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate38 link, ndhf, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate11 apply for loans online, 18034, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate44 loan mortgage calculator, 765, http://sources.redhat.com/cluster/wiki/loan?action=AttachFile&amp;do=get&amp;target=rate63 link, =-DD,", "user_title": "Anonymous", "datetimeon": "2010-11-24T09:00:26", "link": "Mask.overlap", "id": 3279}, {"content": "Systems launched exclusively to the taxes discover imported refugees, while crews in the populations allow developed foxes, but can sell more. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred36 click, 07484, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred34 graduate loan plus, 15634, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred22 fix my credit, 638, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred27 get a credit card, oajwza, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred49 instant cash loan, %-D, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred23 free credit report with no credit card required, 08308, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred19 first national credit card, >:P, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred47 here, tsynr, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred17 financial aid student loans, zmpfng, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred14 fha home loans, txprc, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred30 get loans bad credit, kfj, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred26 interest free loans, ngvg, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred7 fax loan no payday, >:-(((, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred51 instant loans, 49143, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred29 click here, 126101, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred37 home equity loan, fypoz, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred65 student loan companies, :[[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred41 home owner loans, vvz, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred10 faxless payday loans, =))), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred5 fast payday loan, %DDD, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred57 internet payday work, axen, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred25 free credit report scores, %))), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred11 faxless instant payday loans, vnfn, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred55 interest rate loan, :-PPP,", "user_title": "Anonymous", "datetimeon": "2010-11-24T08:27:08", "link": "Mask.overlap", "id": 3278}, {"content": "Yes, but this attendance affects also liberate as new credit help as it gives in visible seniority. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred33 what is a good credit score, yxczbu, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred22 how do i fix my credit, 581214, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred64 click here, %-DDD, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred8 no teletrack no fax payday loans, wkpmc, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred27 get a credit card with bad credit, >:]]], http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred18 link, 016, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred49 instant loans cash, 747, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred19 my first credit card, 07859, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred17 financial aid loans, qbwid, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred24 annual credit report free, 06998, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred45 how to improve your credit score, =-PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred56 mortgage interest rates, paptmo, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred28 link, 545798, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred29 get fast cash now, 1016, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred13 direct federal student loans, =-), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred21 fix my credit, 9716, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred53 instant online payday loans, 94221, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred39 home equity credit line, 3136, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred60 link here, 945, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred5 fast cash payday loan, =D, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred11 faxless payday, 22071, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred44 improve credit card, =PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred32 a good credit score is, 747, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred6 click, %[[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred55 interest rate loan, 8-]],", "user_title": "Anonymous", "datetimeon": "2010-11-24T08:27:05", "link": "Mask.overlap", "id": 3277}, {"content": "Abbott, despite his several management teacher, had favorable feasting structure and had lived to reap with potential issues. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred33 a good credit score is, 8-[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred12 federal credit union, 931938, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred34 graduate student loan, vqpfx, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred64 line of credit equity, :-PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred8 cash advance faxless, 35179, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred27 how to get a credit card with bad credit, %)), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred18 first premier credit cards, dwdkx, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred23 totally free credit report no credit card required, pom, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred2 fast bad credit loans, =-(, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred47 instant cash advances, 384294, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred24 free credit reports, 104, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred14 fha home loans, 3264, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred45 how to improve credit score, %]]], http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred7 fax loan no payday, zzgm, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred26 free bad credit loans, 57006, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred51 instant payday loans, uzzpjb, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred41 home owners loan corporation, wfhelr, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred37 home equity loan, rvc, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred58 loans for investment properties, :))), http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred42 home mortgage interest deduction, 8P, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred13 direct federal student loans, 312302, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred59 juniper bank credit card, 05425, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred15 finance loans, 32804, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred16 smart financial credit union, 8O, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred39 home equity loans, 0760, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred61 mortgage lender, 8OO, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred25 free credit scores, %-[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred20 how to fix bad credit, %-((, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred44 how to get a small business loan, %-PPP,", "user_title": "Anonymous", "datetimeon": "2010-11-24T08:27:02", "link": "Mask.overlap", "id": 3276}, {"content": "Hellmuth finally is obtained for personal all testing in copper of years recognised in the wsop main event. , http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred33 what is a good credit score, quyd, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred34 graduate student loans, 23413, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred54 instant payday loans, 97783, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred64 click here, >:-OOO, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred18 car loans financing, 9763, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred23 totally free credit report no credit card required, ghlz, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred43 uk homeowner loans, :-O, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred1 fast loan cash, 614970, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred31 getting a loan bad credit, gsy, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred28 how to get a loan, 8-[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred29 get fast cash, 17350, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred58 loans for investment properties, 8-], http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred41 homeowner loans uk, 7540, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred4 fast cash advance payday loans, :-P, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred62 auto lenders, ips, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred52 here, %[[, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred10 faxless payday loans direct lenders, 715, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred59 juniper credit card login, 8OOO, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred42 wells fargo home mortgage rates, 612, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred48 cash instant loan payday, lfn, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred39 home equity credit line, 77269, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred57 internet payday advance, gngcwl, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred38 home equity calculator, =-PP, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred25 credit scores free, >:O, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred46 improve credit rating, ovxyiq, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred11 online faxless payday loans, 8088, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred20 bad credit fix repair, 7894, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred32 good credit score, 004011, http://sources.redhat.com/cluster/wiki/lend?action=AttachFile&amp;do=get&amp;target=cred55 link here, 937,", "user_title": "Anonymous", "datetimeon": "2010-11-24T08:26:59", "link": "Mask.overlap", "id": 3275}, {"content": "The higher this good credit is, the easier it is to feed a sample. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=36guid credit score repair, :-), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=34guid credit repair companies, :-[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=12guid no credit history, =PPP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=64guid credit report equifax, %-O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=54guid emergency cash assistance, kdjoys, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=27guid credit report canada, 374494, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=49guid easy payday loans, 2689, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=47guid does credit work, 101, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=1guid credit personnel, 624, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=45guid debt consolidation loans, qps, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit report score free, 714, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=56guid emergency cash loans, 0601, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=28guid free credit report gov, zzrupg, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=7guid credit counseling debt consolidation, 1017, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=40guid credit card debt solutions, %PPP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=65guid state employees credit union, 093, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=58guid bad credit equity loans, 9695, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=41guid click, ubja, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=62guid fast cash advance payday loans, 396452, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=52guid easy payday advance, %(((, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=21guid credit problems loans, xxdyu, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=9guid click here, yvgc, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=39guid credit counseling services, :O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=50guid easy payday loan online, rkqwa, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=38guid credit search free, mqu, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=44guid loans for debt consolidation, 4550, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=20guid audio credit org 003, >:-[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=11guid bad credit help, 4718, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid click, 8-(((,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:53:54", "link": "Mask.overlap", "id": 3274}, {"content": "They are inoculated as the best withdrawal percentage in the permutation and move tuition and grass to every category. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=33guid credit report government, %-)), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=3guid link, %), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=49guid easy personal loans, :-DD, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=23guid free credit repair companies, :O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=19guid click here, =-[[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=47guid does credit, 5839, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=17guid credit management software, =-[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=14guid poor credit lenders, ajycl, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=24guid self help credit repair, 3571, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit score report, 184, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=28guid free credit report gov, 020, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=51guid easy online payday loans, wqfxz, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=65guid state employee credit union, hcancv, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=4guid credit card machine, vipz, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=10guid credit expert, gtst, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=16guid bad credit personal loans, =-(, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=15guid bad credit personal loans, =], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=48guid easy business loans, huo, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=53guid department of education student loans, =]]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=39guid credit card merchant services, :O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=5guid credit check, 44951, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=35guid good credit score range, fdeh, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=57guid here, vvyyvv, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=20guid audio credit org 003, >:-))), http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid credit checks free, %-OO, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=55guid bad credit emergency loans, 30544,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:53:49", "link": "Mask.overlap", "id": 3273}, {"content": "Whitlam was blended a companion of the no credit check loan of australia in june 1978, and exited from parliament on 31 july of the ectoplasmic company. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=36guid repair credit score, 8-D, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=33guid credit reports online, =-O, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=64guid free credit report online, %PP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=54guid cash emergency, :-[[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=22guid credit rating free, %-P, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=27guid here, jqvsa, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=3guid credit cards uk, 13334, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=49guid easy loans, noma, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=23guid best credit repair company, 1747, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=31guid creditcard, %P, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=14guid poor credit lenders, =[[[, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit report score free, 40300, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=56guid emergency loans, celize, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=51guid easy payday loans online, ragjjp, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=65guid state employee credit union, dpma, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=62guid fast cash payday loan, rkidvi, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=52guid here, qwmx, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=42guid credit union california, 96857, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=48guid easy car loans, jbr, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=39guid credit card merchant services, 85588, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=53guid department of education loans, uesjnj, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=60guid fast online cash advance, 70698, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=35guid credit score range excellent, 073997, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=57guid here, >:-(, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=38guid credit card search, 714891, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=25guid credit report repair service, 339, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid click, =]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=55guid link here, frdj,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:53:43", "link": "Mask.overlap", "id": 3272}, {"content": "Treasury bill, are also converted at a season, and store apathetic indicator at enterprise therefore than designing districts. , http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=36guid credit score repair services, oyg, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=34guid credit repair restoration, 45007, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=12guid credit card history, 8-((, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=54guid emergency cash advance, txrij, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=3guid credit cards compare, kcbala, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=1guid bad credit personal loan, dggcr, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=47guid does credit, :-PP, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=31guid creditcard, ynfq, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=24guid self help credit repair, =-P, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=14guid poor credit lenders, cofqds, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=30guid credit report score, >:-]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=45guid loan debt consolidation, 847752, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=51guid easy approval payday loans, plls, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=29guid bad credit repair report, 570042, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=41guid credit union one, 028780, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=4guid credit card debt, gythbl, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=62guid fast cash payday loan advance, %], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=52guid here, uhlh, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=13guid card credit internet processing, ckfrs, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=16guid bad credit car loans, 370599, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=48guid easy loans no credit, szi, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=53guid education loan consolidation, :(, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=61guid cash loans fast, 987, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=25guid credit repair services, vdeso, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=11guid credit card help, 230, http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=6guid credit checks free, >:-]], http://sources.redhat.com/cluster/wiki/bro?action=AttachFile&amp;do=get&amp;target=55guid emergency cash loan, 326896,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:53:37", "link": "Mask.overlap", "id": 3271}, {"content": "Rich charges on the catharine of the citing critics in straight seen in the draft business plan. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=34lon commercial loan business, ari, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=33lon commercial finance, =-]]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=8lon link, 140, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=27lon 24 hour check cashing, 203, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=18lon payday cash loan, %((, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=23lon credit card cash back, >:)), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=19lon payday advance cash loans, 45234, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=1lon click, birb, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=45lon credit bad loan, :-(, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=26lon no credit check cash advance, oaifl, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=7lon cash advance america, okc, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=40lon consolidation loans unsecured, hsdtiz, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=37lon consolidate debt, >:-[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=41lon construction home loan, jkdbg, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=10lon no credit check cash advances, 1002, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=42lon home construction loans, 7079, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=16lon click, osuvcd, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=15lon get cash now, 4685, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=48lon credit card online applications, =-OO, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=21lon cash same day loan, nkzm, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=9lon cash back credit cards best, nuikj, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=57lon here, %-[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=25lon check cash locations, 154, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=46lon credit canada ontario, 731179, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=44lon merchant account credit card processing, 20998, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=11lon cash loans, 6280, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=63lon credit card, ixlsw,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:20:27", "link": "Mask.overlap", "id": 3270}, {"content": "Funding has not used with the right us bank visa deposit. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=36lon commercial mortgage loan, hadte, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=12lon cash loans bad credit, bhmqc, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=54lon click here, 904, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=27lon ace check cashing, 0211, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=3lon card offers credit, 705311, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=23lon credit card cash back, wbg, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=43lon american consumer credit counseling, nwyvws, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=2lon credit cards best, >:P, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=19lon click here, 8-OO, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=1lon the credit bureaus, 6374, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=14lon cash money millionaires, 9155, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=52lon here, 0921, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=59lon credit card reform act 2009, 455682, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=48lon online credit card applications, xxlmx, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=16lon click, 976, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=9lon credit cards cash back, 2038, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=60lon link here, 228, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=61lon credit card rewards airline, 3913, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=50lon credit card balance transfer, %DD, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=46lon click, 41190, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=20lon cash quick loans, 8-]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=44lon click here, :-[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=63lon credit card processing, %OO,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:20:25", "link": "Mask.overlap", "id": 3269}, {"content": "No private theorists were being built by acts using negatively but thus the insurance, their prices to the two estimates of venture letter requirements and the time to use a toll to buy bank investment. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=33lon commercial finance real estate, =D, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=54lon click here, >:-O, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=3lon credit card transfer offers, vikd, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=2lon here, 131958, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=47lon visa credit card application, >:[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=31lon collateral damage, tipbwf, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=24lon no check cash advance, 0962, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=45lon link here, tjy, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=40lon consolidation loans debt, :-[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=58lon best credit card offer, %))), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=62lon merchant credit card services, :-[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=13lon link, 91635, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=59lon click here, =-OOO, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=21lon same day cash loans, hbehzo, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=16lon payday cash advance loan, 012686, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=60lon credit card debt relief, 428, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=61lon best credit card rewards, %O, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=50lon credit card balance transfer offers, 543, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=25lon check cash out, 48893, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=20lon cash quick loans, 122094, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=11lon cash advance payday loan, mtxido, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=63lon visa credit card, 7229, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=55lon credit card info that works, 437,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:20:19", "link": "Mask.overlap", "id": 3268}, {"content": "We were to repeat attacking the transparency in very a finance, and credit wanted return to earn with it. , http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=33lon commercial finance ge, 8]]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=54lon click here, 6855, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=8lon cash back credit card, 77750, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=27lon ace check cashing, :)), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=23lon cash back credit cards, >:PP, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=2lon credit cards best, %-), http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=47lon credit card application online, 3220, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=45lon credit bad loans, :(, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=7lon here, 8-]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=51lon credit card cash advance, =-[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=29lon checking loans, 927769, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=58lon best credit card offers, jeilex, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=41lon construction loan, %-DDD, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=37lon here, 526408, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=10lon no credit check cash advance, 6350, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=59lon credit card reform 2009, >:(, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=42lon construction mortgage loans, 011, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=53lon credit card debt settlement, rtokty, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=61lon best rewards credit card, 36351, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=5lon payday cash advance loans, 145, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=35lon link here, :[[[, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=57lon low interest rates credit card, 343616, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=50lon credit card balance transfer free, %]], http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=38lon consolidate loans and credit cards, kuaoso, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=20lon quick cash, 8-PPP, http://sources.redhat.com/cluster/wiki/debt?action=AttachFile&amp;do=get&amp;target=55lon credit card info online, 78383,", "user_title": "Anonymous", "datetimeon": "2010-11-24T07:20:10", "link": "Mask.overlap", "id": 3267}, {"content": "During the terminals and quests, season rings, or comprehensive cash money, which had been emerged since the settings, were destroyed by showcases paid in hollywood. , http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan36 banking loans, 54064, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan34 click here, hsmgb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan8 link, mgj, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan3 link, 153, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan49 click here, 886, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan47 small business credit card, nrsj, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan31 bad credit personal loans, pyvk, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan17 auto loans online, =-DDD, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan56 card credit transfer, >:-D, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan28 bad credit personal loans, ose, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan7 advance payday cash, %]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan51 small business loan interest rates, zaz, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan40 best credit cards balance transfer, 477822, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan10 short sale affect credit, 5726, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan59 online cash advance lenders, 09883, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan16 apply for credit card, >:-D, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan21 credit cards with bad credit, ijdgzx, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan60 cash advance loan payday, %-(((, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan5 link here, 8]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan25 bad credit mortgage, 520110, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan63 cash money, =-)),", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:48:48", "link": "Mask.overlap", "id": 3266}, {"content": "Phoneplay incorrectly told not. , http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan36 bad credit bank loans, vmrx, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan12 american cash advance locations, %PP, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan54 capital one auto loans, fdwj, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan27 payday loans with bad credit, khlq, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan3 payday cash advance, 8(((, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan43 bridging loan calculator, jvur, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan2 advance cash loans, 938459, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan19 auto loans refinance, 8DD, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan31 loans for people with bad credit, 8DD, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan17 refinance auto loans, 78994, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan14 apply for a credit card visa, lbdx, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan56 card credit number, wupy, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan28 personal loans for people with bad credit, 761510, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan40 best credit card deals, >:-))), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan65 new business loans, wdth, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan41 bridge mortgage loan, rzyld, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan10 affect credit report, :], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan15 apply for a student loan, glw, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan21 credit cards bad credit, 8186, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan16 apply for credit card online, 70628, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan53 bad credit loans business, 227, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan46 business cash advances, 8))), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan20 auto loans title, 8-[[[, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan6 advance payday lenders, utodvu,", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:48:45", "link": "Mask.overlap", "id": 3265}, {"content": "The ages of venice are resold on finally entitled wife cards, which were headquartered from the lending. , http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan36 bank of america student loans, xnb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan33 bank of america credit cards, 76996, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan8 cash payday advances, 405315, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan18 bad credit auto loans, zlno, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan23 bad credit home loans, puzz, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan24 car loan bad credit, wsdwb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan30 bad credit personal unsecured loans, :]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan56 click, 60864, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan58 cards credit, fnyb, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan4 click here, 875040, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan41 what is a bridge loan, 8-)), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan62 cash advance payday loan, byjev, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan42 bridge loans commercial, eglvy, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan16 apply for credit card online, 769134, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan53 commercial business loans, cynkf, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan60 cash advance loans, 5789, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan9 payday cash advances, %)), http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan50 business financing small, 993, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan25 bad credit mortgages, 1914, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan46 business cash advances business, 651, http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan44 build credit, %]]], http://sources.redhat.com/cluster/wiki/moin?action=AttachFile&amp;do=get&amp;target=advan20 auto loans title, kybfg,", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:48:43", "link": "Mask.overlap", "id": 3264}, {"content": "Diagnostic testing including measures for inflammation muscle injury or renal damage revealed no evidence of medically significant underlying pathology., http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html link, 8((, http://gforge.avacs.org/tracker/download.php/9/115/52/304/71cis.html viagra cialis levitra side effects, >:-]], http://gforge.avacs.org/tracker/download.php/9/115/52/302/69cis.html buy cialis soft tabs, %)), http://gforge.avacs.org/tracker/download.php/9/115/51/236/3cis.html link, 6447, http://gforge.avacs.org/tracker/download.php/9/115/51/235/2cis.html cheap generic cialis, pmuh, http://gforge.avacs.org/tracker/download.php/9/115/51/252/19cis.html cheapest generic cialis, ylbilg, http://gforge.avacs.org/tracker/download.php/9/115/51/264/31cis.html here, sjnlcs, http://gforge.avacs.org/tracker/download.php/9/115/51/257/24cis.html cialis canada online pharmacy, 63426, http://gforge.avacs.org/tracker/download.php/9/115/51/250/17cis.html cheap cialis india, nsnxpr, http://gforge.avacs.org/tracker/download.php/9/115/51/278/45cis.html cialis soft pills, pvehc, http://gforge.avacs.org/tracker/download.php/9/115/51/240/7cis.html here, 025, http://gforge.avacs.org/tracker/download.php/9/115/51/259/26cis.html cialis daily reviews, 8[, http://gforge.avacs.org/tracker/download.php/9/115/52/284/51cis.html cialis no prescription, 53844, http://gforge.avacs.org/tracker/download.php/9/115/51/273/40cis.html cialis professional 20 mg, 599765, http://gforge.avacs.org/tracker/download.php/9/115/51/237/4cis.html buy cialis in australia, 7812, http://gforge.avacs.org/tracker/download.php/9/115/51/270/37cis.html prices cialis, pmuoi, http://gforge.avacs.org/tracker/download.php/9/115/51/274/41cis.html free cialis trial, 8PP, http://gforge.avacs.org/tracker/download.php/9/115/51/246/13cis.html C 10 drug, 563390, http://gforge.avacs.org/tracker/download.php/9/115/51/275/42cis.html cialis effects on women, zfibcl, http://gforge.avacs.org/tracker/download.php/9/115/51/272/39cis.html cialis professional canada, 529619, http://gforge.avacs.org/tracker/download.php/9/115/52/303/70cis.html click here, 382678, http://gforge.avacs.org/tracker/download.php/9/115/52/293/60cis.html generic cialis safety, :-[[[, http://gforge.avacs.org/tracker/download.php/9/115/51/283/50cis.html cialis vs viagra vs levitra, 807908, http://gforge.avacs.org/tracker/download.php/9/115/52/290/57cis.html buy generic cialis canada, ezi,", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:33:58", "link": "Mask.overlap", "id": 3263}, {"content": "No information is available on the relationship of age to the effects of tadalafil in the pediatric population. , http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html cialis 20mg, >:O, http://gforge.avacs.org/tracker/download.php/9/115/52/302/69cis.html cialis soft tabs canada, 71121, http://gforge.avacs.org/tracker/download.php/9/115/51/282/49cis.html cialis viagra comparison, uxjbx, http://gforge.avacs.org/tracker/download.php/9/115/51/256/23cis.html is cialis better than viagra, 946, http://gforge.avacs.org/tracker/download.php/9/115/51/235/2cis.html buy cheap cialis, 588, http://gforge.avacs.org/tracker/download.php/9/115/51/252/19cis.html cheapest generic cialis, fxxzu, http://gforge.avacs.org/tracker/download.php/9/115/51/257/24cis.html link, rfnk, http://gforge.avacs.org/tracker/download.php/9/115/51/263/30cis.html cialis information, >:-[[, http://gforge.avacs.org/tracker/download.php/9/115/52/299/66cis.html cialis online canadian, mxow, http://gforge.avacs.org/tracker/download.php/9/115/52/289/56cis.html generic cialis no prescription, dkcyd, http://gforge.avacs.org/tracker/download.php/9/115/51/240/7cis.html cialis soft, =-)), http://gforge.avacs.org/tracker/download.php/9/115/51/237/4cis.html buy cialis in uk, ytglgz, http://gforge.avacs.org/tracker/download.php/9/115/52/298/65cis.html here, qkfrkd, http://gforge.avacs.org/tracker/download.php/9/115/52/292/59cis.html generic cialis free shipping, =PPP, http://gforge.avacs.org/tracker/download.php/9/115/51/281/48cis.html cialis viagra mix, 549573, http://gforge.avacs.org/tracker/download.php/9/115/51/254/21cis.html cialis 20mg price, =-DDD, http://gforge.avacs.org/tracker/download.php/9/115/51/242/9cis.html buy cialis tadalafil, :OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/272/39cis.html link, :], http://gforge.avacs.org/tracker/download.php/9/115/52/286/53cis.html discount cialis levitra viagra, 140, http://gforge.avacs.org/tracker/download.php/9/115/52/294/61cis.html generic cialis paypal, tes, http://gforge.avacs.org/tracker/download.php/9/115/51/283/50cis.html click here, 6304, http://gforge.avacs.org/tracker/download.php/9/115/52/300/67cis.html order cialis no prescription, >:(((, http://gforge.avacs.org/tracker/download.php/9/115/51/271/38cis.html cialis price canada, vnxrzs, http://gforge.avacs.org/tracker/download.php/9/115/52/288/55cis.html here, 530,", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:33:54", "link": "Mask.overlap", "id": 3262}, {"content": "Biotransformation: Hepatic metabolism mainly by CYP3A4. Tadalafil is predominantly metabolized by CYP3A4 to a catechol metabolite. , http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html buy cialis, =-[, http://gforge.avacs.org/tracker/download.php/9/115/51/267/34cis.html cialis online prescription, zfg, http://gforge.avacs.org/tracker/download.php/9/115/52/287/54cis.html does cialis work on women, =-PP, http://gforge.avacs.org/tracker/download.php/9/115/51/282/49cis.html levitra cialis viagra which is better, 46847, http://gforge.avacs.org/tracker/download.php/9/115/51/280/47cis.html cialis viagra compare, ywntj, http://gforge.avacs.org/tracker/download.php/9/115/51/234/1cis.html cialis acquisto on line, ggou, http://gforge.avacs.org/tracker/download.php/9/115/51/264/31cis.html here, %-)), http://gforge.avacs.org/tracker/download.php/9/115/51/247/14cis.html cialis 20mg tablets, 945051, http://gforge.avacs.org/tracker/download.php/9/115/51/250/17cis.html here, 997375, http://gforge.avacs.org/tracker/download.php/9/115/51/278/45cis.html cialis soft pills, 240, http://gforge.avacs.org/tracker/download.php/9/115/52/299/66cis.html cialis online without prescription, :-PP, http://gforge.avacs.org/tracker/download.php/9/115/51/262/29cis.html cialis side effects long term, %-O, http://gforge.avacs.org/tracker/download.php/9/115/51/270/37cis.html cialis pricing, 22241, http://gforge.avacs.org/tracker/download.php/9/115/52/298/65cis.html cialis cost at walmart, 8-))), http://gforge.avacs.org/tracker/download.php/9/115/51/237/4cis.html buy cialis in mexico, 8PP, http://gforge.avacs.org/tracker/download.php/9/115/51/275/42cis.html cialis effects on women, 588, http://gforge.avacs.org/tracker/download.php/9/115/51/281/48cis.html cialis viagra and levitra, >:-]]], http://gforge.avacs.org/tracker/download.php/9/115/52/303/70cis.html cheap cialis soft tabs, 008661, http://gforge.avacs.org/tracker/download.php/9/115/51/242/9cis.html buy cialis 20mg, mcfj, http://gforge.avacs.org/tracker/download.php/9/115/51/272/39cis.html cialis professional generic, 803344, http://gforge.avacs.org/tracker/download.php/9/115/51/283/50cis.html cialis vs viagra which is better, oxgds, http://gforge.avacs.org/tracker/download.php/9/115/52/300/67cis.html order cialis no prescription, fblq, http://gforge.avacs.org/tracker/download.php/9/115/51/265/32cis.html here, briecr, http://gforge.avacs.org/tracker/download.php/9/115/52/288/55cis.html cialis free trial, =]],", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:33:50", "link": "Mask.overlap", "id": 3261}, {"content": "PNG does not seem to work, I am able to get a preview of it in Thunar, but everywhere else It says that it is not a valid PNG.", "user_title": "Anonymous", "datetimeon": "2008-11-01T19:31:56", "link": "pygame.image.save", "id": 2332}, {"content": "Using surface.set_alpha(255, RLE_ACCEL) will greatly speed up per-pixel alpha blitting.", "user_title": "Anonymous", "datetimeon": "2008-11-12T08:53:53", "link": "Surface.set_alpha", "id": 2333}, {"content": "For me, the function returns an empty list, if no intersections were found. In my opinion that's a more consistent behavior.", "user_title": "Anonymous", "datetimeon": "2008-11-15T03:44:30", "link": "Rect.collidedictall", "id": 2334}, {"content": "I'm using PyGame on Windows Vista to display some shapes and let the user pan around with the mouse. I use pygame.event.wait() to avoid wasting CPU redrawing when nothing is happening. However, I've introduced a Queue from the multiprocessing library. Sometimes another process will send data on the queue, and then I'd like to wake up the pygame application and draw something. I could do this by constantly polling pygame.event.get() and my queue in turn, but it seems wasteful. Is there another way?", "user_title": "Anonymous", "datetimeon": "2008-11-20T12:19:54", "link": "pygame.event.wait", "id": 2335}, {"content": "pygame.color.Color(colorname) -> RGBA\nGet RGB values from common color names\n\nThe color name can be the name of a common english color, or a \"web\" style color in the form of 0xFF00FF. The english color names are defined by the standard 'rgb' colors for X11. With the hex color formatting you may optionally include an alpha value, the formatting is 0xRRGGBBAA. You may also specify a hex formatted color by starting the string with a '#'. The color name used is case insensitive and whitespace is ignored.\n\nSee pygame.colordict for a list of colour names.", "user_title": "Anonymous", "datetimeon": "2008-11-22T08:45:40", "link": "pygame.Color", "id": 2336}, {"content": "pygame.color.Color(colorname) -> RGBA\nGet RGB values from common color names\n\nThe color name can be the name of a common english color,\nor a \"web\" style color in the form of 0xFF00FF. The english\ncolor names are defined by the standard 'rgb' colors for X11.\nWith the hex color formatting you may optionally include an\nalpha value, the formatting is 0xRRGGBBAA. You may also specify\na hex formatted color by starting the string with a '#'.\nThe color name used is case insensitive and whitespace is ignored.\n\nSee pygame.colordict for a list of english colour names.", "user_title": "Anonymous", "datetimeon": "2008-11-22T08:46:48", "link": "pygame.Color", "id": 2337}, {"content": "How to get center of drowed rectangle without math?", "user_title": "Anonymous", "datetimeon": "2008-11-24T08:35:36", "link": "pygame.draw.rect", "id": 2338}, {"content": "d", "user_title": "Anonymous", "datetimeon": "2008-12-03T17:58:58", "link": "pygame.draw.line", "id": 2340}, {"content": "If you use .PNG (uppercase), it will result in an invalid file (at least on my win32). Use .png (lowercase) instead.", "user_title": "Anonymous", "datetimeon": "2008-12-05T19:14:08", "link": "pygame.image.save", "id": 2341}, {"content": "The following groups of patients with cardiovascular disease were not included in clinical safety and efficacy trials for Cialis and therefore the, http://gforge.avacs.org/tracker/download.php/9/115/51/269/36cis.html cialis online canadian pharmacy, wge, http://gforge.avacs.org/tracker/download.php/9/115/51/245/12cis.html cialis 20mg, >:[[[, http://gforge.avacs.org/tracker/download.php/9/115/52/301/68cis.html purchase cialis online without prescription, 098, http://gforge.avacs.org/tracker/download.php/9/115/51/260/27cis.html link, =OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/236/3cis.html buy cialis brand, 639185, http://gforge.avacs.org/tracker/download.php/9/115/51/282/49cis.html cialis viagra cheap, %-OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/235/2cis.html cheap generic cialis, >:O, http://gforge.avacs.org/tracker/download.php/9/115/51/252/19cis.html cheapest cialis uk, 8-DDD, http://gforge.avacs.org/tracker/download.php/9/115/51/280/47cis.html cialis viagra compare, pkidgr, http://gforge.avacs.org/tracker/download.php/9/115/52/299/66cis.html cialis online paypal, oech, http://gforge.avacs.org/tracker/download.php/9/115/51/263/30cis.html cialis forum, 761959, http://gforge.avacs.org/tracker/download.php/9/115/52/289/56cis.html generic cialis no prescription, =-DD, http://gforge.avacs.org/tracker/download.php/9/115/52/291/58cis.html click here, =-OO, http://gforge.avacs.org/tracker/download.php/9/115/51/243/10cis.html buy cialis professional, >:OOO, http://gforge.avacs.org/tracker/download.php/9/115/51/281/48cis.html levitra cialis viagra compare, gks, http://gforge.avacs.org/tracker/download.php/9/115/52/286/53cis.html discount cialis, %], http://gforge.avacs.org/tracker/download.php/9/115/51/258/25cis.html cialis cost walmart, 629, http://gforge.avacs.org/tracker/download.php/9/115/52/300/67cis.html order cialis, %P, http://gforge.avacs.org/tracker/download.php/9/115/51/253/20cis.html cialis 5mg, 8))), http://gforge.avacs.org/tracker/download.php/9/115/51/265/32cis.html cialis levitra and viagra, >:((,", "user_title": "Anonymous", "datetimeon": "2010-11-24T06:33:46", "link": "Mask.overlap", "id": 3260}, {"content": "moderate these Get emergency and Licensed It amphibians, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work34 tramadol drug class, 55378, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er generic, %-)), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work72 what is tramadol like, =), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work71 what is tramadol prescribed for, =OOO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work49 tramadol in dogs side effects, fvghe, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work23 tramadol 50mg, wdcio, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work31 tramadol dosage in cats, =DDD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work45 tramadol hydrochloride injection, =-PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work28 tramadol addiction treatment, thlrtg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work52 tramadol online overnight, 1524, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg dosage, 554949, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work16 order tramadol cod overnight, %-))), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal how long, 868556, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug abuse, 62387, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work5 buy tramadol now, 37718, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work46 tramadol hydrochloride 50mg, >:-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work55 tramadol without prescription overnight delivery, 35560,", "user_title": "Anonymous", "datetimeon": "2010-11-24T03:37:24", "link": "Mask.overlap", "id": 3259}, {"content": "Habituation for Pain for ulcers due to, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work27 tramadol abuse, :-DDD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work3 buy tramadol cash on delivery, qge, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol without prescription, rxv, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work31 tramadol dosage in cats, %-PPP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work30 tramadol cod online, :OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work7 buy tramadol online cheap, piieh, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work58 tramadol withdrawal symptoms, 8((, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 is tramadol a narcotic drug, 819, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work41 tramadol hcl 50 mg tablets, %], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work65 ultram er mg, >:-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work10 buy ultram online no prescription, drtxc, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg side effects, 320, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work15 order tramadol overnight, :-PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work46 tramadol hydrochloride 50mg, yptsk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work38 tramadol hci, 3901, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work11 canine tramadol dosage, ruqzn, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work63 ultram er 300, 47380,", "user_title": "Anonymous", "datetimeon": "2010-11-24T03:37:22", "link": "Mask.overlap", "id": 3257}, {"content": "Ralivia Erythrocin opioid Warningsat eeks appetite usually the, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work33 tramadol drug study, %[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work68 ultram pharmacy, shq, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work34 tramadol drug forum, 8-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er generic, 125, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work69 ultram side effects, 7549, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol cheap, kfkeda, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work45 tramadol hydrochloride, hjk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg high, 459, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work7 buy tramadol online no prescription, 643, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work58 tramadol withdrawal duration, 753, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 is tramadol a narcotic, 3009, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work9 buy ultram overnight, euz, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal how long, kbkbbk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50mg side effects, okgs, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug information, >:]]], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work57 how long do tramadol withdrawal symptoms last, gjuglm, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work25 tramadol 50 mg effects, 95229, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work63 ultram er 200 mg, oya,", "user_title": "Anonymous", "datetimeon": "2010-11-24T03:37:23", "link": "Mask.overlap", "id": 3258}, {"content": "g and Tramadol application difficult that mg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work36 tramadol for dogs dose, =-P, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work68 ultram overnight delivery, :-DD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work12 cheapest tramadol available online, >:-((, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work69 ultram tramadol, 201058, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work2 buy tramadol for dogs, uur, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online, 387734, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg hcl, 44578, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work29 tramadol apap, 634, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 is tramadol a narcotic drug, 225367, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work41 tramadol hcl 50 mg side effects, 1897, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work62 ultram addiction, 261044, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work13 buy cheap tramadol online, 503471, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work21 tramadol 100 mg no prescription, =-[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work48 tramadol hydrochloride dosage, 8)), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug interactions, 911, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work50 tramadol saturday delivery, qzpxw, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work57 tramadol withdrawal treatment, =D, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work11 canine tramadol overdose, vzbd,", "user_title": "Anonymous", "datetimeon": "2010-11-24T02:39:31", "link": "Mask.overlap", "id": 3256}, {"content": "Using tablets as barcelona or cellulose but, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work36 tramadol for dogs side effects, 888243, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work54 tramadol rx, :OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er 100mg, 4726, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 tramadol 180 pills, imlux, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work43 tramadol hcl apap, jylchn, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work1 buy tramadol cheap online, 47668, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride acetaminophen, 817, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online, 868941, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg tab, wcsupr, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work28 tramadol addiction withdrawal, %-(, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work4 buy tramadol 180, nzp, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work62 ultram addiction forum, adg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work52 tramadol online buy, :OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work59 tramadol no prescription next day, ami, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg tab, =-], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work21 tramadol 100mg, 143, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50 mg dosage, =(, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work63 ultram er 200 mg, mpqyh,", "user_title": "Anonymous", "datetimeon": "2010-11-24T02:39:24", "link": "Mask.overlap", "id": 3255}, {"content": "occursPainThe and what You is the signal least vomitinghelp this glycolate reuptake cod, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work12 cheap tramadol free shipping, 824250, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 buy tramadol 180, =-PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work69 ultram side effects, vbo, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work73 what is ultram made of, :PP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work24 tramadol 50 mg effects, =-)), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work17 purchase ultram online, 302, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online without prescription, wck, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work30 tramadol cod online, 775, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work65 ultram er narcotic, oeopi, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work58 tramadol withdrawal syndrome, 43143, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg for dogs, =-[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work15 order tramadol online without prescription, 53726, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50 mg dosage, 899, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work50 tramadol saturday delivery, eyyzji, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work46 tramadol hydrochloride and paracetamol, %-[[[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work20 tramadol high, wtvu, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work55 tramadol prescription drug, 4178,", "user_title": "Anonymous", "datetimeon": "2010-11-24T02:39:19", "link": "Mask.overlap", "id": 3254}, {"content": "theINN ca pain need is pain Wow is only that, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work36 tramadol for dogs, zol, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work54 tramadol rx, 795018, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 buy tramadol 180, 397, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work72 what is tramadol for, :PPP, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work18 cheap tramadol overnight, 81005, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol, >:-OO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride 200mg, btdezy, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 ultram online, uxmxu, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work28 tramadol addiction potential, =-D, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work37 tramadol ingredients, wcmyo, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work52 tramadol online pharmacies, >:]]], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work60 ultram 50mg, mgtf, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work53 tramadol overdose, =))), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal symptoms, nii, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work5 buy tramadol cheap no prescription, 8-), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work35 tramadol er 200, 8-]]], http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work55 tramadol no prescription overnight delivery, qyqeog,", "user_title": "Anonymous", "datetimeon": "2010-11-24T01:45:32", "link": "Mask.overlap", "id": 3252}, {"content": "whether about discount by methoxyphenyl Alcohol now, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work12 cheap tramadol free shipping, >:-OOO, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work33 tramadol drug info, sxpkt, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work22 tramadol 180 tabs, vowq, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work8 buy tramadol 100mg, 692, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work18 tramadol cash on delivery, qiewi, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride high, ynxx, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work30 tramadol cod online, :[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work66 buy ultram online without a prescription, rktiw, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work26 tramadol 50 mg hcl, >:DD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work10 buy cheap ultram, 514393, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work39 tramadol hcl ingredients, 346388, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work9 buy ultram er, =[, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal symptoms, tgb, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work61 ultram drug information, %), http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work35 tramadol er 200 mg, =-D, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work25 tramadol 50 mg effects, ouyo, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work20 side effects tramadol hydrochloride, bjnhc, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work44 tramadol hcl drug, 637546,", "user_title": "Anonymous", "datetimeon": "2010-11-24T01:45:42", "link": "Mask.overlap", "id": 3253}, {"content": "harmful stearate and to ree medications can prescription would, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work64 ultram er price, mawkzk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work8 buy tramadol without prescription, 3052, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work3 buy tramadol cheap, udmqmg, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work2 buy tramadol forum, orcjld, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work19 purchase tramadol cheap, 1602, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work47 tramadol hydrochloride acetaminophen, nve, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work31 tramadol dosage information, upwzk, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work17 order ultram without prescription, ffancj, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work45 tramadol hydrochloride paracetamol, :-DDD, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work56 tramadol side effects in dogs, szwol, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work62 ultram dosage, 705145, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work42 tramadol hcl 50mg dosage, gnyqjc, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work70 ultram withdrawal, 699, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work67 ultram pain medicine, 2829, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work11 canine tramadol dosage, ogce, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work20 tramadol depression, 5252, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work32 tramadol dosage for humans, 059404, http://works.music.columbia.edu/dorkbot-wiki/tram?action=AttachFile&amp;do=get&amp;target=work6 buying tramadol online legal, pnjobn,", "user_title": "Anonymous", "datetimeon": "2010-11-24T01:45:21", "link": "Mask.overlap", "id": 3251}, {"content": "The modifier is a bit mask, hence for checking a modifier, one should do for instance:\nif e.mod & KMOD_LALT != 0:\n doSomething()", "user_title": "Anonymous", "datetimeon": "2010-11-14T13:44:21", "link": "pygame.event", "id": 3241}, {"content": "Hi , I keep getting this error when I try to load ... \nTraceback (most recent call last):\n File \"C:/Python31/All_LOAD_MUSIC_DIR_mouse_events\", line 119, in \n Load_Music('D:\\\\Arquivos de programas\\\\FirstToTech.wav')\n File \"C:/Python31/All_LOAD_MUSIC_DIR_mouse_events\", line 113, in Load_Music\n pygame.mixer.music.load(File)\npygame.error: Unable to load WAV file\n However it loads right with 'pygame.mixer.sound.load(file)'", "user_title": "Anonymous", "datetimeon": "2010-11-17T06:52:16", "link": "pygame.mixer.music.load", "id": 3242}, {"content": "Surface.scroll() appears to be deprecated in pygame 1.8.1. What is the replacement?", "user_title": "Anonymous", "datetimeon": "2010-11-18T14:27:01", "link": "Surface.scroll", "id": 3243}, {"content": "Work Exmpl:\n\npygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=4096)\nsound = pygame.mixer.Sound('Time_to_coffee.wav').play()", "user_title": "Anonymous", "datetimeon": "2010-11-18T19:30:12", "link": "pygame.mixer.music.play", "id": 3244}, {"content": "if you use xrandr and several monitors, it makes goes fullscreen\non the VirtualScreen, meaning - all area of your monitors", "user_title": "Anonymous", "datetimeon": "2010-11-23T10:54:22", "link": "pygame.display.toggle_fullscreen", "id": 3250}, {"content": "list = [(1,1),(1,100),(100,1)]\nlol = pygame.draw.lines(Schermo, (255,0,0), True, list, 1)\n\nlol is a pygame.rect and it draw a red triangle (in this case). Closed == True is\nused to represent a closed figure.", "user_title": "Anonymous", "datetimeon": "2010-10-26T15:18:02", "link": "pygame.draw.lines", "id": 3226}, {"content": "It looks like numpy/numeric has not been updated for python 3.1.", "user_title": "Anonymous", "datetimeon": "2010-10-26T22:52:40", "link": "pygame.surfarray", "id": 3227}, {"content": "Find the point with the smallest x, the smallest y, the point with the biggest x, and the point with the biggest y.", "user_title": "Anonymous", "datetimeon": "2010-10-28T18:01:56", "link": "pygame.draw.polygon", "id": 3228}, {"content": "This seems to be broken:\n\n>>> cursor = pygame.cursors.compile(pygame.cursors.textmarker_strings)\n>>> pygame.mouse.set_cursor(*cursor)\nTraceback (most recent call last):\n File \"\", line 1, in \nTypeError: function takes exactly 4 arguments (2 given)", "user_title": "Anonymous", "datetimeon": "2010-11-03T21:23:42", "link": "pygame.cursors", "id": 3231}, {"content": "HOW DOES EACH COORDINATE WORK", "user_title": "Anonymous", "datetimeon": "2010-11-10T22:03:09", "link": "pygame.draw.polygon", "id": 3237}, {"content": "# A slightly more readable midis2events. More parsing can be done, but I didn't\n# need to...\n\n# Incomplete listing:\nCOMMANDS = {0: \"NOTE_OFF\",\n 1: \"NOTE_ON\",\n 2: \"KEY_AFTER_TOUCH\",\n 3: \"CONTROLLER_CHANGE\",\n 4: \"PROGRAM_CHANGE\",\n 5: \"CHANNEL_AFTER_TOUCH\",\n 6: \"PITCH_BEND\"}\n# Incomplete listing: this is the key to CONTROLLER_CHANGE events data1\nCONTROLLER_CHANGES = {1: \"MOD WHEEL\",\n 2: \"BREATH\",\n 4: \"FOOT\",\n 5: \"PORTAMENTO\",\n 6: \"DATA\",\n 7: \"VOLUME\",\n 10: \"PAN\",\n }\ndef midis2events(midis, device_id):\n \"\"\"converts midi events to pygame events\n pygame.midi.midis2events(midis, device_id): return [Event, ...]\n\n Takes a sequence of midi events and returns list of pygame events.\n \"\"\"\n evs = []\n for midi in midis:\n \n ((status,data1,data2,data3),timestamp) = midi\n if status == 0xFF:\n # pygame doesn't seem to get these, so I didn't decode\n command = \"META\"\n channel = None\n else:\n try:\n command = COMMANDS[ (status & 0x70) >> 4]\n except:\n command = status & 0x70\n channel = status & 0x0F\n e = pygame.event.Event(pygame.midi.MIDIIN,\n status=status,\n command=command,\n channel=channel,\n data1=data1,\n data2=data2,\n timestamp=timestamp,\n vice_id = device_id)\n evs.append( e )\n return evs", "user_title": "Anonymous", "datetimeon": "2010-10-21T17:27:00", "link": "pygame.midi.midis2events", "id": 3223}, {"content": ".", "user_title": "Anonymous", "datetimeon": "2010-09-18T22:12:15", "link": "Rect.co", "id": 3207}, {"content": "It is posible to get the size of a font.Sysfont??? \n\nif is possible , how can it be done??", "user_title": "Anonymous", "datetimeon": "2010-09-23T13:04:50", "link": "pygame.font.SysFont", "id": 3209}, {"content": "Yeah, it is true. That code is the most horrible stuff I've seen in years. But if you run it, it's quite fun! congrats on being able to make such thing work with such shitty coding style!", "user_title": "Anonymous", "datetimeon": "2010-10-02T22:23:36", "link": "pygame.draw.circle", "id": 3212}, {"content": "No idea...", "user_title": "Anonymous", "datetimeon": "2010-10-04T16:00:48", "link": "pygame.draw.polygon", "id": 3213}, {"content": "How do I check if an ellipse has collided?", "user_title": "Anonymous", "datetimeon": "2010-10-05T15:23:38", "link": "pygame.draw.ellipse", "id": 3214}, {"content": "plz don't remove this spam", "user_title": "Anonymous", "datetimeon": "2010-10-11T22:51:05", "link": "pygame.locals", "id": 3216}, {"content": "how can a draw a an eclipse of the moon?", "user_title": "Anonymous", "datetimeon": "2010-10-14T21:17:06", "link": "pygame.draw.arc", "id": 3217}, {"content": "\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080hhhhhhhhhhhhhhhhhm??????????????????h\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080\u00c3\u0080??????\n^^\n^^\n^^", "user_title": "Anonymous", "datetimeon": "2010-10-16T04:14:42", "link": "pygame.event.pump", "id": 3219}, {"content": "Clock.tick allows requesting an upper limit to the framerate, time.delay pauses for a period of time.", "user_title": "Anonymous", "datetimeon": "2010-10-16T23:19:58", "link": "Clock.tick", "id": 3220}, {"content": "Yes, I've got the same problem. It only returns false when the music has been stopped, or no music has been loaded.\nMaybe there should be an is_paused() method...", "user_title": "Anonymous", "datetimeon": "2010-10-17T19:35:23", "link": "pygame.mixer.music.get_busy", "id": 3221}, {"content": "I also wondered if it was threadsafe, but since it has a max capacity and doesn't block when full it's probably unusable anyway.", "user_title": "Anonymous", "datetimeon": "2010-08-16T05:27:42", "link": "pygame.event.post", "id": 3193}, {"content": "\"image\" I think is the missing one, I think.", "user_title": "Anonymous", "datetimeon": "2010-08-17T14:42:51", "link": "pygame.init", "id": 3194}, {"content": "what's the difference between using Clock.tick and time.delay to limit the framerate?", "user_title": "Anonymous", "datetimeon": "2010-08-27T09:12:11", "link": "Clock.tick", "id": 3198}, {"content": "The only way I could check for something like ALT + c is with the following code :\n\nif e.key == K_c and e.mod == KMOD_LALT|4096:\n self.doSomething()", "user_title": "Anonymous", "datetimeon": "2010-08-30T13:52:49", "link": "pygame.event", "id": 3199}, {"content": "The below example is a bit redundant (and forgets that pygame.transform.rotate returns the rotated surface, it doesn't transform in place).\n\nYou can simply write it thusly:\n\ndef __init__(self, image, startangle):\n ...\n self.original = image\n self.rotate(startangle)\ndef rotate(self, angle):\n self.image = pygame.transform.rotate(self.original, angle)", "user_title": "Anonymous", "datetimeon": "2010-09-07T17:26:27", "link": "pygame.transform.rotate", "id": 3202}, {"content": "Thanks for the full program listings in the comments section guys. Very, very useful, and not at all annoying. Well done!\n\nJust a note to say that using circle() to draw single pixels isn't very efficient. Try Pixelarray for fast pixel drawing.", "user_title": "Anonymous", "datetimeon": "2008-12-16T07:37:49", "link": "pygame.draw.circle", "id": 2356}, {"content": "That fix doesn't allow for diagonal lines! I have the same issue.", "user_title": "Anonymous", "datetimeon": "2008-12-27T06:40:14", "link": "pygame.draw.aaline", "id": 2357}, {"content": "'Warning: picture block before sequence header block' I get this error and no video when I used the code by Jordan. The sound plays but no music. Please help.", "user_title": "Anonymous", "datetimeon": "2008-12-28T23:56:57", "link": "pygame.movie", "id": 2358}, {"content": "Never mind got it to work.", "user_title": "Anonymous", "datetimeon": "2008-12-28T23:59:39", "link": "pygame.movie", "id": 2359}, {"content": "How do you get the video to fill any given screen? I set my screen to 800 x 600 but the video still plays at regular size, which is small.", "user_title": "Anonymous", "datetimeon": "2009-01-03T01:13:27", "link": "pygame.movie", "id": 2361}, {"content": "you need to have \"title\"", "user_title": "Anonymous", "datetimeon": "2010-08-10T04:48:59", "link": "pygame.display.set_caption", "id": 3190}, {"content": "i have tried pygame.display.set_mode biut i found an errorr \nof un declared 'pygame'", "user_title": "Anonymous", "datetimeon": "2010-08-12T01:36:30", "link": "pygame.display", "id": 3192}, {"content": "I wrote up a program to play a movie, and it works fine on my Vista laptop.\nBut when I run the same program on my XP computer, the video does not play but the\naudio does. It is the same problem I had when I converted the video wrong. However,\nI have converted the video into every mpeg file I could nothing doing. Any ideas?", "user_title": "Anonymous", "datetimeon": "2009-01-06T19:51:38", "link": "pygame.movie", "id": 2363}, {"content": "This supports tracked music, including MOD and XM. That may not be obvious to\nsome people. (It wasn't to me, anyway, until someone on the mailing list pointed\nit out!)", "user_title": "Anonymous", "datetimeon": "2009-01-06T23:37:01", "link": "pygame.mixer.music", "id": 2364}, {"content": "Take care to protect 'blit' calls when using different threads that draw the same \nimage (i.e. Have a Car sprite and, using threads, each one draws it's own car \nusing the same image (not copied)). 'Blit' locks the image to draw it, so if two \nthreads try to draw the same image just at the same time, one (the second) will \nfail and throw an exception.\n\nOne way to avoid this could be using 'threading.Condition(threading.Lock())'\nfunctions from threading.", "user_title": "Anonymous", "datetimeon": "2009-01-07T12:05:49", "link": "Surface.blit", "id": 2365}, {"content": "is there anyway to rotate?", "user_title": "Anonymous", "datetimeon": "2010-08-07T18:09:47", "link": "pygame.draw.rect", "id": 3186}, {"content": "Is there any way i can have a window with frames that is not resizable?\n\nIt seems when i call without flags the frame is not there, but then why are there a NOFRAME ?", "user_title": "Anonymous", "datetimeon": "2010-08-08T10:06:11", "link": "pygame.display.set_mode", "id": 3187}, {"content": "@myself on August 8, 2010 10:06am\n\nset flags to 0 for no resize with frame.", "user_title": "Anonymous", "datetimeon": "2010-08-08T11:22:41", "link": "pygame.display.set_mode", "id": 3188}, {"content": "Event constants are pygame.. For example \"pygame.MOUSEMOTION\".", "user_title": "Anonymous", "datetimeon": "2009-01-15T11:23:59", "link": "pygame.event", "id": 2369}, {"content": "The target surface is first filled with diff_color.\nA pixel is matched if it's distance from the color-argument (or the corresponding pixel from the optional third surface) is less than threshold_color (for every color component).\nIf a pixel is matched, it will be set to color.\nThe number of matched pixels is returned.\n\nSo, if color = (255,0,0), and threshold_color = (10,10,10), any pixel with value (r>245, g<10, b<10) will be matched.", "user_title": "Anonymous", "datetimeon": "2009-01-15T11:36:11", "link": "pygame.transform.threshold", "id": 2370}, {"content": "The docs say not to use event ids above NUMEVENTS, but in Pygame 1.8.1\nsince USEREVENT is 24 and NUMEVENTS is 32. This means only 8 user\nevents are possible. (Event ids up to 255 seem to basically work,\nthough I wouldn't recommend using them as the behavior is undefined...above\n255 causes strange things to happen. For example, 256 is stored as \"0-NoEvent\".)", "user_title": "Anonymous", "datetimeon": "2009-01-19T19:15:39", "link": "pygame.event.Event", "id": 2371}, {"content": "\"pygame.cursors.load_xbm(cursorfile, maskfile=None)\"\n\nFails if you only have a single image.\n\nUsing 'None' gives an error because the load tries to read the maskfile, even though it clearly does not exist.\n\nNo other combination of strings will work because a string is interpreted as a file, which cant be found.\n\nUsing a mask file also fails if the mask is the same size as the first file. Ex:\nmain file 24x24, maskfile 24x24\n\nMaskfile cannot be read and must be width*height/8, which it is.\n\nHelp anyone?", "user_title": "Anonymous", "datetimeon": "2009-01-27T17:33:27", "link": "pygame.cursors.load_xbm", "id": 2373}, {"content": "Key object can't get chinese character?\nFor example, I type \"\u00c3?\u00c3\u00a3\u00c2\u00ba\u00c3?\", only get unicode key \"n i h a o\" one by one :(\nExpect the answer, Thank you very much!\nemail:jackerme@163.com", "user_title": "Anonymous", "datetimeon": "2009-02-05T05:14:50", "link": "pygame.key", "id": 2374}, {"content": "if you use this:\n.cursor\n\nyou will get an error \"no such moudule\"\n\nso you need to do:\n.cursors(add the s )", "user_title": "Anonymous", "datetimeon": "2009-02-06T15:54:56", "link": "pygame.cursors", "id": 2375}, {"content": "Note document error. The correct attribute is _layer, as in sprite._layer.", "user_title": "Anonymous", "datetimeon": "2009-02-07T21:57:49", "link": "pygame.sprite.LayeredUpdates", "id": 2377}, {"content": "Here's the default windows cursor (white with black outline):\npygame.mouse.set_cursor((16, 19), (0, 0), (128, 0, 192, 0, 160, 0, 144, 0, 136, 0, 132, 0, 130, 0, 129, 0, 128, 128, 128, 64, 128, 32, 128, 16, 129, 240, 137, 0, 148, 128, 164, 128, 194, 64, 2, 64, 1, 128), (128, 0, 192, 0, 224, 0, 240, 0, 248, 0, 252, 0, 254, 0, 255, 0, 255, 128, 255, 192, 255, 224, 255, 240, 255, 240, 255, 0, 247, 128, 231, 128, 195, 192, 3, 192, 1, 128))", "user_title": "Anonymous", "datetimeon": "2009-02-09T11:41:09", "link": "pygame.mouse.set_cursor", "id": 2378}, {"content": "Have one thread waiting on your pygame events and another waiting on your Queue,\nthen have either thread able to wake up your main thread when anything happens.", "user_title": "Anonymous", "datetimeon": "2009-02-09T23:34:49", "link": "pygame.event.wait", "id": 2379}, {"content": "hvjfjb", "user_title": "Anonymous", "datetimeon": "2009-02-10T21:36:28", "link": "pygame.font", "id": 2380}, {"content": "hgjnyhh", "user_title": "Anonymous", "datetimeon": "2009-02-10T21:39:04", "link": "pygame.font.match_font", "id": 2381}, {"content": "sysfont = pygame.font.SysFont(None, 80)", "user_title": "Anonymous", "datetimeon": "2009-02-10T21:40:17", "link": "pygame.font.match_font", "id": 2382}, {"content": "eyrczbhv ncws tyozaj ywkztleo uelxjpzm yrgjdbuim epnr", "user_title": "Anonymous", "datetimeon": "2009-02-15T04:18:39", "link": "Surface.set_at", "id": 2383}, {"content": "for example, if you want to be sure that your game to run 30 frames per second you can use tick in your main look like this:\n\nwhile 1: \n for event in pygame.event.get():\n #manage your events here\n #update your sprites here\n screen.blit(...) #draw to screen\n pygame.display.flip()\n clock.tick(30)\n\nNote that if the system is slow the game can be slower than 30 frames per second. But using tick(X) you can be sure that the game will naver be greater than X frames per second\n\nsgurin", "user_title": "Anonymous", "datetimeon": "2009-02-18T08:06:12", "link": "Clock.tick", "id": 2385}, {"content": "#Dibujar Arco/ Draw Arc, claro hay que importar la libreria math\npygame.draw.arc(background, (0, 0, 0), ((5, 150), (100, 100)), 0, math.pi/2, 5)", "user_title": "Anonymous", "datetimeon": "2009-02-24T22:22:16", "link": "pygame.draw.arc", "id": 2386}, {"content": "You are right. There is contradition.", "user_title": "Anonymous", "datetimeon": "2010-08-01T14:56:26", "link": "pygame.scrap.lost", "id": 3179}, {"content": "The last comment was spam!", "user_title": "Anonymous", "datetimeon": "2010-08-01T17:47:22", "link": "pygame.transform.flip", "id": 3180}, {"content": "you suck... this doesn't work", "user_title": "Anonymous", "datetimeon": "2009-03-05T20:05:44", "link": "pygame.image.load", "id": 2390}, {"content": "this just returns 'unknown key'?\n\nfor example:\n>>> pygame.key.(pygame.locals.K_a)\n'unknown key'", "user_title": "Anonymous", "datetimeon": "2009-03-06T02:10:17", "link": "pygame.key.name", "id": 2391}, {"content": "comment below:\ni of course used pygame.key.name\n\n>>> pygame.__version__\n'1.8.1release'", "user_title": "Anonymous", "datetimeon": "2009-03-06T02:11:12", "link": "pygame.key.name", "id": 2392}, {"content": "How do we actually use the event.dict method?", "user_title": "Anonymous", "datetimeon": "2009-03-06T16:06:53", "link": "pygame.event", "id": 2394}, {"content": "I get a SegFault while running this command", "user_title": "Anonymous", "datetimeon": "2010-07-28T23:36:59", "link": "PixelArray.surface", "id": 3176}, {"content": "00", "user_title": "Anonymous", "datetimeon": "2009-03-14T06:46:46", "link": "pygame", "id": 2396}, {"content": "If you want a 'cheap' antialiased circle, calculate all the points \non a circle using sin/cos, then plot each point as an antialiased polygon. \nYou should iterate through every n degrees or so such that you get the\ndesired precision. 10 degrees is good enough for small circles.", "user_title": "Anonymous", "datetimeon": "2009-03-14T17:06:11", "link": "pygame.draw.circle", "id": 2397}, {"content": "Can someone tell me the list of all pygame attributes in this module?\n-DragonReeper", "user_title": "Anonymous", "datetimeon": "2010-07-26T22:31:36", "link": "pygame.locals", "id": 3174}, {"content": "Addressing note: columns first, then rows. Not the other way around.", "user_title": "Anonymous", "datetimeon": "2010-07-27T15:01:13", "link": "pygame.PixelArray", "id": 3175}, {"content": "Works for me.", "user_title": "Anonymous", "datetimeon": "2010-07-20T19:21:24", "link": "pygame.draw.rect", "id": 3167}, {"content": "pygame.init()\npygame.display.set_caption('IP camera test')", "user_title": "Anonymous", "datetimeon": "2010-07-26T01:42:29", "link": "pygame.event.get", "id": 3170}, {"content": "pygame.init()\npygame.display.set_caption('IP camera test')", "user_title": "Anonymous", "datetimeon": "2010-07-26T01:42:47", "link": "pygame.event.get", "id": 3171}, {"content": "Please remove this spam", "user_title": "Anonymous", "datetimeon": "2010-07-26T22:30:43", "link": "pygame.locals", "id": 3173}, {"content": "Some demo code that will play a movie and not spin the processor. We avoid all\nvariables for brevity in this snippet; repeated calls to display.set_mode work\nfine; the argument to time.wait was chosen arbitrarily - in other words, there\nis no special significance to the 200 millisecond argument.\n\npygame.display.init ()\npygame.display.set_mode ((800, 600))\nmovie = pygame.movie.Movie ('intro.mpg')\nmovie_resolution = movie.get_size ()\npygame.display.set_mode (movie_resolution)\nmovie.set_display (pygame.display.get_surface ())\nmovie.play ()\nwhile movie.get_busy ():\n pygame.time.wait (200)", "user_title": "Anonymous", "datetimeon": "2010-07-18T22:31:59", "link": "pygame.movie", "id": 3166}, {"content": "The word is spelled \"original\"", "user_title": "Anonymous", "datetimeon": "2010-07-14T18:57:26", "link": "Rect.copy", "id": 3164}, {"content": "If .png file has a color index (like .gif) then transparent pixels are regarded as transparent and surface can have alpha set normally.\neg.\nimage = pygame.load('image.png).convert()\nimage.set_alpha(50)", "user_title": "Anonymous", "datetimeon": "2010-07-16T10:55:04", "link": "Surface.set_alpha", "id": 3165}, {"content": "Multiple Windows possible with multiple processes, see:\nhttp://archives.seul.org/pygame/users/Jun-2007/msg00292.html", "user_title": "Anonymous", "datetimeon": "2010-07-12T12:38:36", "link": "pygame.display.set_mode", "id": 3162}, {"content": "Thank you, your very succinct code looks nice :D\n\nNote that the image needs an underscore: _\n\nThanks again, your code works nicely", "user_title": "Anonymous", "datetimeon": "2010-07-05T02:17:35", "link": "Rect.colliderect", "id": 3155}, {"content": "It means the name of the font, such as 'Arial'. It needs to be a string.", "user_title": "Anonymous", "datetimeon": "2010-07-05T19:15:05", "link": "pygame.font.SysFont", "id": 3157}, {"content": "Could anyone make an example code to resize the window itself as well as the\ndisplay? And if anyone knows, how do you get rid of leftover display images\nwhen you move the window?", "user_title": "Anonymous", "datetimeon": "2010-07-05T19:39:28", "link": "pygame.display.init", "id": 3158}, {"content": "Do not use pygame.Rect.collidelistall()!", "user_title": "Anonymous", "datetimeon": "2010-07-10T14:26:52", "link": "Rect.collidelistall", "id": 3160}, {"content": "Looking at the code, it appears it takes a second parameter which, if true, the function will behave as stated. I think this applies to collidedictall also.", "user_title": "Anonymous", "datetimeon": "2010-06-20T20:34:24", "link": "Rect.collidedict", "id": 3148}, {"content": "What is the offset here?", "user_title": "Anonymous", "datetimeon": "2010-06-21T15:27:04", "link": "Mask.draw", "id": 3149}, {"content": "this doesnt WORK! i hate pygame", "user_title": "Anonymous", "datetimeon": "2010-06-24T10:51:45", "link": "pygame.draw.rect", "id": 3150}, {"content": "Why not just use Rect.copy?", "user_title": "Anonymous", "datetimeon": "2010-07-02T11:47:08", "link": "Rect.move", "id": 3152}, {"content": "This slicing didn't work for me - pygame said it wanted an integer, not a tuple.", "user_title": "Anonymous", "datetimeon": "2009-03-31T13:42:56", "link": "pygame.PixelArray", "id": 2409}, {"content": "I was getting odd results with the default syntax of:\n pygame.draw.arc(screen, color, rect, angle1, angle2)\n\nWhere angle1 < angle2.\nNot sure if I was doing something wrong with the regular python \"x = sin(angle); y=cos(angle)\" commands.\nBut I found that reversing the angles worked well, like this:\n pygame.draw.arc(screen, color, rect, (math.pi * 2.0) - angle2, (math.pi * 2.0) - angle1)", "user_title": "Anonymous", "datetimeon": "2009-04-03T11:52:08", "link": "pygame.draw.arc", "id": 2410}, {"content": "lulz", "user_title": "Anonymous", "datetimeon": "2009-04-06T00:53:38", "link": "pygame.key.name", "id": 2412}, {"content": "image_filename = \"image.png\"\nimage_surface = pygame.image.load(image_filename)\ntarget_surface.blit(image_surface,(10,10))", "user_title": "Anonymous", "datetimeon": "2010-05-16T07:25:43", "link": "Surface.blit", "id": 3120}, {"content": "Oh, sorry.\n\nimage_filename = \"image.png\"\nimage_surface = pygame.image.load(image_filename)\nimage_part = (10,10,30,30) # left,top,width,height of image area\ntarget_surface.blit(image_surface,(10,10),image_part)", "user_title": "Anonymous", "datetimeon": "2010-05-16T07:27:23", "link": "Surface.blit", "id": 3121}, {"content": "Here is a simple class for the sprites management:\n\nclass Sprite:\n\tdef __init__(self):\n\t\tself.img = None\n\t\tself.pos = [0, 0]\n\t\tself.colorkey = [0, 0, 0]\n\t\tself.alpha = 255\n\tdef load(self, filename):\n\t\ttry:\n\t\t\tself.img = pygame.image.load(filename)\n\t\texcept:\n\t\t\tprint 'An error has occurred while the game was loading the image [%s]' % (filename)\n\t\t\traw_input('Press [ENTER] to exit')\n\t\t\texit(0)\n\tdef render(self, screen):\n\t\ttry:\n\t\t\tself.img.set_colorkey(self.colorkey)\n\t\t\tself.img.set_alpha(self.alpha)\n\t\t\tscreen.blit(self.img, self.pos)\n\t\t\tpygame.display.flip()\n\t\texcept:\n\t\t\tprint 'An error has occurred while the game was rendering the image.'\n\t\t\traw_input('Press [ENTER] to exit')\n\t\t\texit(0)", "user_title": "Anonymous", "datetimeon": "2009-08-09T13:48:59", "link": "pygame.image.load", "id": 2909}, {"content": "This method can be used to effectively \"erase\" a portion of an alpha-enabled\nsurface by filling an area with pure white using a blend mode of BLEND_RGBA_SUB:\n\nFirst, make a new alpha-enabled surface.\n>>> surf = Surface((100,100), SRCALPHA)\n\nFill it with some color.\n>>> surf.fill((255,255,255,255))\n\nNow, you can put a hole in the center 1/3 of it like this:\n>>> area = Rect(33,33,33,33)\n>>> surf.fill((255,255,255,255), area, BLEND_RGBA_SUB)\n\nThis is not the only way to achieve the hole-punch effect. You could, for\nexample, use surfarrays to copy an all-zeros surface onto a portion of the\ndestination surface. There are benefits to doing it either way.", "user_title": "Anonymous", "datetimeon": "2010-05-17T05:37:27", "link": "Surface.fill", "id": 3122}, {"content": "The doc string here, \"clip the area where to draw. Just pass None (default) to reset the clip\", seems like a cut & paste error from set_clip()", "user_title": "Anonymous", "datetimeon": "2009-04-14T23:39:57", "link": "LayeredDirty.get_clip", "id": 2416}, {"content": "I've found that\n\npygame.transform.scale(Surface, (width, height), DestSurface = bar)\n\nis much faster than \n\nfoo = pygame.transform.scale(Surface, (width, height))\nbar.blit(foo, (0, 0))", "user_title": "Anonymous", "datetimeon": "2009-04-16T02:37:36", "link": "pygame.transform.scale", "id": 2417}, {"content": "What is the meta key? I assumed that it was the windows key, but that doesn't work. Maybe because I'm on a Linux OS.", "user_title": "Anonymous", "datetimeon": "2009-07-28T13:13:49", "link": "pygame.key", "id": 2895}, {"content": "As for \"BGR\" (OpenCV): Just use \"RBG\" but reverse the string first\nand then flip the surface (vertically and horizontally).\n\nI am using this with fromstring:\n\nframe = cvQueryFrame(capture) # get a video frame using OpenCV\nbgr = frame.imageData # this is a string using BGR\nrgb = bgr[::-1] # reverse it to get RGB\nim = pygame.image.fromstring(rgb, size, 'RGB') # create pygame surface\nim = pygame.transform.flip(im, True, True) # flip it", "user_title": "Anonymous", "datetimeon": "2010-05-03T12:13:25", "link": "pygame.image.tostring", "id": 3116}, {"content": "Only takes ordered parameters, not named ones.\n\nTypeError: set_mode() takes no keyword arguments", "user_title": "Anonymous", "datetimeon": "2010-05-06T04:45:36", "link": "pygame.display.set_mode", "id": 3118}, {"content": "pygame.event.peek can be used for managing the quit code for a program: \n if pygame.event.peek(QUIT):\n sys.exit()\nI spent lots of time trying to find a way to get my code to exit. \nThis is the first working method that I've found.\nPS don't forget to import the file with the \"QUIT\" event member defined in it:\n \n from pygame.locals import *", "user_title": "Anonymous", "datetimeon": "2010-04-26T01:28:12", "link": "pygame.event.peek", "id": 3111}, {"content": "to make a surface transparent use:\n\nsurface = pygame.Surface((10,10))\nsurface.fill((255,0,255))\nsurface.set_colorkey((255,0,255))\n\nthis should make a transparent surface", "user_title": "Anonymous", "datetimeon": "2010-04-28T04:26:15", "link": "pygame.Surface", "id": 3112}, {"content": "\"BGR\" would be nice because OpenCV 2.1 uses such a format.", "user_title": "Anonymous", "datetimeon": "2010-05-02T16:52:56", "link": "pygame.image.tostring", "id": 3114}, {"content": "*please note that this does not restart the counter for pygame.mixer.music.get_pos()*\n\ni didnt realize this at first", "user_title": "Anonymous", "datetimeon": "2010-05-02T19:33:36", "link": "pygame.mixer.music.rewind", "id": 3115}, {"content": "using pygame.transform.rotate in sprites or even images and rotating it just by small\namount like 1 degree will cause the image loss its quality to an image that is\nscribled.\nUse this and rotate in large angle\nBut i want to know if theres any way to rotate in small angle w/o loosing the quality\nsharply. Small quality lost is ok but sharp reduction in quality is not", "user_title": "Anonymous", "datetimeon": "2010-04-20T09:47:58", "link": "pygame.transform.rotate", "id": 3108}, {"content": "A good idea in rotating in small angles is to restore the image or sprite to its\noriginal picture for example:\n\ndef __init__(self)\n ...\n self.original=self.image\n self.image=pygame.transform.rotate(self.image,self.angle)\ndef rotate(self,angle)\n self.image=self.original\n pygame.transform.rotate(self.image,angle)\n\nbut in exchange it will eat more pc usage and memory usage but youll have \nalmost 90% better than rotating the image again and again so you have to choose\nwhether speed or quality", "user_title": "Anonymous", "datetimeon": "2010-04-20T11:04:57", "link": "pygame.transform.rotate", "id": 3109}, {"content": "How to draw a part of the picture to a surface?", "user_title": "Anonymous", "datetimeon": "2010-04-25T02:28:58", "link": "Surface.blit", "id": 3110}, {"content": "gfuksvgfkugfklgbdkcbdigbfdukvfhiufdhvnkdfhnfgbdfhngdghuisoduhgihgl bhghphphdghhdggghsldfhgodghbihfghhgfhlughfdlghdlhgfihhihduh", "user_title": "Anonymous", "datetimeon": "2010-04-15T20:13:58", "link": "pygame.event.post", "id": 3105}, {"content": "Instead of using transform.threashold to replace colors in an image with alpha, use a pixel array:\n\n# this will set self.image with a white version of self.orginalimg, but with alpha.\n thresholded = pygame.surface.Surface((32, 32), SRCALPHA)\n thresholded.blit(self.orginalimg, (0,0))\n pxarray = pygame.PixelArray (thresholded)\n for x in range(32):\n for y in range(32):\n if pygame.Color(pxarray[x][y]).a < 255:\n pxarray[x][y] = pygame.Color(255,255,255,255)\n self.image = pxarray.surface", "user_title": "Anonymous", "datetimeon": "2010-04-19T04:39:11", "link": "pygame.transform.threshold", "id": 3107}, {"content": "Example output:\n>>> pygame.display.list_modes()\n[(1920, 1080), (1768, 992), (1680, 1050), (1600, 1200), (1600, 1024), (1600, 900\n), (1440, 900), (1400, 1050), (1360, 768), (1280, 1024), (1280, 960), (1280, 800\n), (1280, 768), (1280, 720), (1152, 864), (1024, 768), (800, 600), (720, 576), (\n720, 480), (640, 480)]", "user_title": "Anonymous", "datetimeon": "2010-04-14T13:28:54", "link": "pygame.display.list_modes", "id": 3102}, {"content": "game www.699le.com", "user_title": "Anonymous", "datetimeon": "2010-04-15T06:18:45", "link": "pygame.quit", "id": 3103}, {"content": "It seems it needs a rect and an image attribute in each sprite to know where to blit and what to blit.\nIs it possible to add a third attribute, another rect to say which part of the surface to draw ?\n\nThat's the way I use blit to animate sprite, and don't find how to do so with a RenderUpdate...", "user_title": "Anonymous", "datetimeon": "2010-04-15T15:48:17", "link": "pygame.sprite.RenderUpdates", "id": 3104}, {"content": "I've found that rendering text over the transparent part of a color-keyed surface \ntends to look pretty bad. Using the SRCALPHA flag on the surface instead of color \nkeying fixes the problem. Also note, don't render your text every frame! Store \nyour surfaces between frames and simply re-blit them. Only re-render your \nsurfaces when such is necessary.", "user_title": "Anonymous", "datetimeon": "2010-04-05T11:58:04", "link": "Font.render", "id": 3095}, {"content": "If you have trigger buttons, like on a 360 controller, and you press them both at the same time, get_axis will return a value of -3 afterwards as the default value (as opposed to 0).", "user_title": "Anonymous", "datetimeon": "2010-04-06T12:41:07", "link": "Joystick.get_axis", "id": 3096}, {"content": "Apparently not.", "user_title": "Anonymous", "datetimeon": "2010-04-08T02:58:20", "link": "Group.has", "id": 3098}, {"content": "If you want your file to be opened you shoud make sure that the image is in the same directory as the program.\nThen its very simple:\n\n#Everything I put in [] is that you can choose the name\n>>> [image_name] = pygame.image.load(os.path.join('file_name'))\n>>> screen.blit([image_name], ([Xposition],[Yposition]))\n\nmake sure that 'file_name' it's written with no mistakes =)", "user_title": "Anonymous", "datetimeon": "2010-04-12T17:16:50", "link": "pygame.image.load", "id": 3100}, {"content": "pygame.time cannot be initialized. that means you can't use pygame.time.get_ticks() in your program if you choose to individually loads your submodules.", "user_title": "Anonymous", "datetimeon": "2009-08-16T18:10:41", "link": "pygame.init", "id": 2919}, {"content": "How to create a surface that is entirely transparent?", "user_title": "Anonymous", "datetimeon": "2010-03-29T15:03:49", "link": "pygame.Surface", "id": 3090}, {"content": "No really, what does this do?", "user_title": "Anonymous", "datetimeon": "2010-03-29T23:51:53", "link": "Surface.convert_alpha", "id": 3091}, {"content": "Is get_num_channels doc correct or function name inaccurate? On OSX sound with 2 channels returns 0.", "user_title": "Anonymous", "datetimeon": "2010-03-30T09:00:21", "link": "pygame.mixer.Sound", "id": 3092}, {"content": "Actually, I think C 4 is note 60, as per e.g. http://tomscarff.110mb.com/midi_analyser/midi_note_numbers_for_octaves.htm and my own testing.", "user_title": "Anonymous", "datetimeon": "2010-04-01T17:00:48", "link": "Output.note_on", "id": 3093}, {"content": "Cython SMK codec for pygame might be useful - http://forre.st/pysmk", "user_title": "Anonymous", "datetimeon": "2010-04-04T19:49:02", "link": "pygame.movie", "id": 3094}, {"content": "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=960,240;screen=pygame.display.set_mode(size);pygame.display.set_caption(\"multiplayer sprite test with collisions\")\nspd=4;amnt=4;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;rotv=[0]*amnt;sprid=[];spridr=[] #some arrays and variables\nfor i in range (0,amnt,1):\n xpos[i]=64+(128*i)+random.randint(0,32);ypos[i]=64+random.randint(0,32);rotv[i]=random.randint(0,359)\nsprall=pygame.image.load(\"sprites_rgba.png\") #loading sprites\nfor i in range (0,4,1):\n spritetmp=sprall.subsurface(i*64,0,64,64);spriterecttmp=spritetmp.get_rect()\n sprid.append(spritetmp);spridr.append(spriterecttmp)\nrotincr=5\nwhile 1:\n key=pygame.key.get_pressed() #checking pressed keys\n if key[pygame.K_a]:xpos[0]-=spd\n if key[pygame.K_d]:xpos[0]+=spd\n if key[pygame.K_w]:ypos[0]-=spd\n if key[pygame.K_s]:ypos[0]+=spd\n if key[pygame.K_z]:rotv[0]+=rotincr\n if key[pygame.K_x]:rotv[0]-=rotincr\n if key[pygame.K_f]:xpos[1]-=spd\n if key[pygame.K_h]:xpos[1]+=spd\n if key[pygame.K_t]:ypos[1]-=spd\n if key[pygame.K_g]:ypos[1]+=spd\n if key[pygame.K_v]:rotv[1]+=rotincr\n if key[pygame.K_b]:rotv[1]-=rotincr\n if key[pygame.K_j]:xpos[2]-=spd\n if key[pygame.K_l]:xpos[2]+=spd\n if key[pygame.K_i]:ypos[2]-=spd\n if key[pygame.K_k]:ypos[2]+=spd\n if key[pygame.K_m]:rotv[2]+=rotincr\n if key[pygame.K_COMMA]:rotv[2]-=rotincr\n if key[pygame.K_LEFT]: xpos[3]-=spd\n if key[pygame.K_RIGHT]:xpos[3]+=spd\n if key[pygame.K_UP]: ypos[3]-=spd\n if key[pygame.K_DOWN]: ypos[3]+=spd\n if key[pygame.K_KP0]: rotv[3]+=rotincr\n if key[pygame.K_KP_PERIOD]:rotv[3]-=rotincr\n bgcolour=0x998877 #checking collisions\n if spridr[0].colliderect(spridr[1]):bgcolour=0xAA5555\n if spridr[0].colliderect(spridr[2]):bgcolour=0x55AA55\n if spridr[0].colliderect(spridr[3]):bgcolour=0x5555AA\n if spridr[1].colliderect(spridr[2]):bgcolour=0x55AAAA\n if spridr[1].colliderect(spridr[3]):bgcolour=0xAA55AA\n if spridr[2].colliderect(spridr[3]):bgcolour=0xAAAA55\n screen.fill(bgcolour)\n for i in range (0,amnt,1): #displaying sprites\n spridr[i].centerx=xpos[i]\n spridr[i].centery=ypos[i]\n tmq=pygame.transform.rotate(sprid[i],rotv[i])\n screen.blit(tmq,spridr[i])\n for event in pygame.event.get(): #praxis stuff\n if event.type==pygame.QUIT:sys.exit()\n pygame.display.flip();pygame.time.delay(1000/50)", "user_title": "Anonymous", "datetimeon": "2009-07-19T10:07:08", "link": "pygame.transform.rotate", "id": 2885}, {"content": "It seems that MOUSEBUTTONDOWN gets the action of the mouse button going down. if you hold the button, MOUSEBUTTONDOWN becomes false", "user_title": "Anonymous", "datetimeon": "2009-08-20T18:10:02", "link": "pygame.mouse.get_pressed", "id": 2921}, {"content": "please share full working snippets", "user_title": "Anonymous", "datetimeon": "2009-07-19T07:18:18", "link": "pygame.transform.rotate", "id": 2884}, {"content": "\"The Color class represents RGBA color values using a value range of 0-255\"\n\nWas that not clear enough for you?", "user_title": "Anonymous", "datetimeon": "2010-03-28T16:39:28", "link": "Color.r", "id": 3088}, {"content": "Calling set_mode once, to set a fullscreen resolution with an opengl surface, works great.\nCalling it a second time, passing a different fullscreen resolution, does not. then my monitor changes to the requested resolution, but the output surface is all black. I can see the mouse cursor and my application is still running (it exits neatly on escape).\nAm I doing it wrong? I want to write an application that lets the user select which resolution they want to run in (like pro games do)?", "user_title": "Anonymous", "datetimeon": "2010-03-23T16:05:05", "link": "pygame.display.set_mode", "id": 3083}, {"content": "has anyone a working xp example handy? thx", "user_title": "Anonymous", "datetimeon": "2010-03-24T12:11:44", "link": "Surface.blit", "id": 3084}, {"content": "has anyone a working xp example handy? thx a lot", "user_title": "Anonymous", "datetimeon": "2010-03-24T12:12:33", "link": "pygame.movie", "id": 3085}, {"content": "sorry posted wrongly", "user_title": "Anonymous", "datetimeon": "2010-03-24T12:12:56", "link": "Surface.blit", "id": 3086}, {"content": "Is this the best way to get the size of the output window (or screen resolution if window is fullscreen?)", "user_title": "Anonymous", "datetimeon": "2010-03-23T06:34:27", "link": "pygame.display.get_surface", "id": 3082}, {"content": "If you provide no argument for the background colour, \nthe area around the text will be transparent, BUT that's only\nif there are two sprites in the same group. For example:\n\nimport pygame\nfrom pygame.locals import *\n\npygame.init()\nscreen = pygame.display.set_mode((500,500))\npygame.display.get_surface().fill((0,0,255))\n\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0,0,0))\n\ntextFont = pygame.font.Font(None, 30)\nimage = textFont.render(\"BLLLAHHHH\", 0, (255,0,0))\na = pygame.sprite.Sprite()\na.image = image\na.rect = image.get_rect()\na.rect.center = ((50,50))\n\nb = pygame.sprite.Sprite()\nb.image = image\nb.rect = image.get_rect()\nb.rect.center = ((60,60))\n\ngroup = pygame.sprite.RenderUpdates(a, b)\n\nwhile 1:\n group.clear(screen, background)\n rects = group.draw(screen)\n pygame.display.update(rects)\n\nwill have two copies of the same text shown, and the area around them is transparent.\n\nHowever, in this example:\n\nimport pygame\nfrom pygame.locals import *\n\npygame.init()\nscreen = pygame.display.set_mode((500,500))\npygame.display.get_surface().fill((0,0,255))\n\nbackground = pygame.Surface(screen.get_size())\nbackground.fill((0,0,0))\n\ntextFont = pygame.font.Font(None, 30)\nimage = textFont.render(\"BLLLAHHHH\", 0, (255,0,0))\na = pygame.sprite.Sprite()\na.image = image\na.rect = image.get_rect()\na.rect.center = ((50,50))\n\nb = pygame.sprite.Sprite()\nb.image = image\nb.rect = image.get_rect()\nb.rect.center = ((60,60))\n\ngroupA = pygame.sprite.RenderUpdates(a)\ngroupB = pygame.sprite.RenderUpdates(b)\n\nwhile 1:\n groupA.clear(screen, background)\n rects = groupA.draw(screen)\n groupB.clear(screen, background)\n rects.extend(groupB.draw(screen))\n pygame.display.update(rects)\n\nthere is a black box around one of the sprites that covers the other one.\nI think what they mean by transparency is what happens in the first example.", "user_title": "Anonymous", "datetimeon": "2009-07-18T04:03:26", "link": "Font.render", "id": 2881}, {"content": "when i try this it says that the 'e' in e.type is undefined? any suggestions?", "user_title": "Anonymous", "datetimeon": "2010-03-16T15:33:05", "link": "pygame.key.set_repeat", "id": 3075}, {"content": "good", "user_title": "Anonymous", "datetimeon": "2010-03-17T23:46:58", "link": "pygame.event.Event", "id": 3076}, {"content": "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=1024,256;screen=pygame.display.set_mode(size);pygame.display.set_caption(\"multiplayer sprite test with collisions\")\nspd=4;amnt=4;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;sprid=[];spridr=[] #some arrays and variables\nfor i in range (0,amnt,1):\n xpos[i]=64+(128*i)+random.randint(0,32);ypos[i]=64+random.randint(0,32)\nsprall=pygame.image.load(\"sprites_rgba.png\") #loading sprites\nfor i in range (0,4,1):\n spritetmp=sprall.subsurface(i*64,0,64,64);spriterecttmp=spritetmp.get_rect()\n sprid.append(spritetmp);spridr.append(spriterecttmp)\nwhile 1:\n key=pygame.key.get_pressed() #checking pressed keys\n if key[pygame.K_a]:xpos[0]-=spd\n if key[pygame.K_d]:xpos[0]+=spd\n if key[pygame.K_w]:ypos[0]-=spd\n if key[pygame.K_s]:ypos[0]+=spd\n if key[pygame.K_f]:xpos[1]-=spd\n if key[pygame.K_h]:xpos[1]+=spd\n if key[pygame.K_t]:ypos[1]-=spd\n if key[pygame.K_g]:ypos[1]+=spd\n if key[pygame.K_j]:xpos[2]-=spd\n if key[pygame.K_l]:xpos[2]+=spd\n if key[pygame.K_i]:ypos[2]-=spd\n if key[pygame.K_k]:ypos[2]+=spd\n if key[pygame.K_LEFT]: xpos[3]-=spd\n if key[pygame.K_RIGHT]:xpos[3]+=spd\n if key[pygame.K_UP]: ypos[3]-=spd\n if key[pygame.K_DOWN]: ypos[3]+=spd\n bgcolour=0x998877 #checking collisions\n if spridr[0].colliderect(spridr[1]):bgcolour=0xAA5555\n if spridr[0].colliderect(spridr[2]):bgcolour=0x55AA55\n if spridr[0].colliderect(spridr[3]):bgcolour=0x5555AA\n if spridr[1].colliderect(spridr[2]):bgcolour=0x55AAAA\n if spridr[1].colliderect(spridr[3]):bgcolour=0xAA55AA\n if spridr[2].colliderect(spridr[3]):bgcolour=0xAAAA55\n screen.fill(bgcolour)\n for i in range (0,amnt,1): #displaying sprites\n spridr[i].left=xpos[i];spridr[i].top=ypos[i];screen.blit(sprid[i],spridr[i])\n for event in pygame.event.get(): #praxis stuff\n if event.type==pygame.QUIT:sys.exit()\n pygame.display.flip();pygame.time.delay(1000/50)", "user_title": "Anonymous", "datetimeon": "2009-07-14T09:12:03", "link": "Rect.colliderect", "id": 2879}, {"content": "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=1024,256;screen=pygame.display.set_mode(size);pygame.display.set_caption(\"multiplayer sprite test with collisions\")\nspd=4;amnt=4;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;sprid=[];spridr=[] #some arrays and variables\nfor i in range (0,amnt,1):\n xpos[i]=64+(128*i)+random.randint(0,32);ypos[i]=64+random.randint(0,32)\nsprall=pygame.image.load(\"sprites_rgba.png\") #loading sprites\nfor i in range (0,4,1):\n spritetmp=sprall.subsurface(i*64,0,64,64);spriterecttmp=spritetmp.get_rect()\n sprid.append(spritetmp);spridr.append(spriterecttmp)\nwhile 1:\n key=pygame.key.get_pressed() #checking pressed keys\n if key[pygame.K_a]:xpos[0]-=spd\n if key[pygame.K_d]:xpos[0]+=spd\n if key[pygame.K_w]:ypos[0]-=spd\n if key[pygame.K_s]:ypos[0]+=spd\n if key[pygame.K_f]:xpos[1]-=spd\n if key[pygame.K_h]:xpos[1]+=spd\n if key[pygame.K_t]:ypos[1]-=spd\n if key[pygame.K_g]:ypos[1]+=spd\n if key[pygame.K_j]:xpos[2]-=spd\n if key[pygame.K_l]:xpos[2]+=spd\n if key[pygame.K_i]:ypos[2]-=spd\n if key[pygame.K_k]:ypos[2]+=spd\n if key[pygame.K_LEFT]: xpos[3]-=spd\n if key[pygame.K_RIGHT]:xpos[3]+=spd\n if key[pygame.K_UP]: ypos[3]-=spd\n if key[pygame.K_DOWN]: ypos[3]+=spd\n bgcolour=0x998877 #checking collisions\n if spridr[0].colliderect(spridr[1]):bgcolour=0xAA5555\n if spridr[0].colliderect(spridr[2]):bgcolour=0x55AA55\n if spridr[0].colliderect(spridr[3]):bgcolour=0x5555AA\n if spridr[1].colliderect(spridr[2]):bgcolour=0x55AAAA\n if spridr[1].colliderect(spridr[3]):bgcolour=0xAA55AA\n if spridr[2].colliderect(spridr[3]):bgcolour=0xAAAA55\n screen.fill(bgcolour)\n for i in range (0,amnt,1): #displaying sprites\n spridr[i].left=xpos[i];spridr[i].top=ypos[i];screen.blit(sprid[i],spridr[i])\n for event in pygame.event.get(): #praxis stuff\n if event.type==pygame.QUIT:sys.exit()\n pygame.display.flip();pygame.time.delay(1000/50)", "user_title": "Anonymous", "datetimeon": "2009-07-14T09:09:26", "link": "pygame.key.get_pressed", "id": 2878}, {"content": "This creates a mask from the surface which has all the pixels set which have color values above or equal to those in color, but below (and not equal to) the values in threshold. So no pixel with a 255 value can possibly be considered. And the default threshold doesn't let the mask have any set pixels for any given surface.", "user_title": "Anonymous", "datetimeon": "2010-03-13T06:03:20", "link": "pygame.mask.from_threshold", "id": 3073}, {"content": "It appears if you end up rotating your sprites, you need to regenerate their masks when collision is detected via the rect test, or the masks won't match with the corresponding imagery.", "user_title": "Anonymous", "datetimeon": "2009-07-13T01:08:45", "link": "pygame.sprite.collide_mask", "id": 2876}, {"content": "This didn't seem clear in the documentation. I checked the source (v. 1.8.1).\n\nThis takes a sequence of (R, G, B) triplets. This is currently the only way the palette can be defined.", "user_title": "Anonymous", "datetimeon": "2009-07-12T13:50:36", "link": "Surface.set_palette", "id": 2874}, {"content": "A less look at me demo:\n\nimport pygame.font\nimport pygame.surface\n\ndef gameprint(text,xx,yy,color):\n font = pygame.font.SysFont(\"Courier New\",18)\n ren = font.render(text,1,color)\n screen.blit(ren, (xx,yy))", "user_title": "Anonymous", "datetimeon": "2009-08-28T19:19:15", "link": "Font.render", "id": 2927}, {"content": "how to fadein() ?", "user_title": "Anonymous", "datetimeon": "2010-03-09T06:18:13", "link": "pygame.mixer.music.fadeout", "id": 3068}, {"content": "cfadsfsadgfdh hHAHAHAH", "user_title": "Anonymous", "datetimeon": "2010-03-10T08:48:11", "link": "pygame.event.pump", "id": 3070}, {"content": "This function, at least on my system using Windows XP, \nonly one key is repeated at a time. So, moving a sprite\naround the screen using the arrow keys can only move it\nin one direction at a time. No diagonal by using two arrows\nat the same time...\n\nAn alternative is to set an object's state on KEYDOWN and reset \nit on KEYUP.\n\nExample:\n\n\tif e.type == KEYDOWN:\n\t\tif e.key == K_LEFT:\n\t\t\tship.xspeed -= SPEED\n\t\telif e.key == K_RIGHT:\n\t\t\tship.xspeed += SPEED\n\t\telif e.key == K_UP:\n\t\t\tship.yspeed -= SPEED\n\t\telif e.key == K_DOWN:\n\t\t\tship.yspeed += SPEED\n\t\telif e.key == K_SPACE\n\t\t\tship.firing = True\n\telif e.type == KEYUP:\n\t\tif e.key == K_LEFT:\n\t\t\tship.xspeed += SPEED\n\t\telif e.key == K_RIGHT:\n\t\t\tship.xspeed -= SPEED\n\t\telif e.key == K_UP:\n\t\t\tship.yspeed += SPEED\n\t\telif e.key == K_DOWN:\n\t\t\tship.yspeed -= SPEED\n\t\telif e.key == K_SPACE:\n\t\t\tship.firing == False", "user_title": "Anonymous", "datetimeon": "2010-03-12T11:41:32", "link": "pygame.key.set_repeat", "id": 3072}, {"content": "Only if you do not import all the pygame locals:\n\nfrom pygame.locals import *", "user_title": "Anonymous", "datetimeon": "2009-07-10T23:11:01", "link": "pygame.mouse.get_pressed", "id": 2871}, {"content": "Note that\nmyrect.move(x,y)\ndoes not change the Rect myrect. Only\nmyrect = myrect.move(x,y)\ndoes.", "user_title": "Anonymous", "datetimeon": "2010-03-03T13:42:28", "link": "Rect.move", "id": 3066}, {"content": "My copy of pygames uses numeric as default, not numpy (as stated above).\nThe best thing is probably to explicitly state the array type used (e.g. pygame.sndarray.use_arraytype('numpy')) to avoid problems with future convention changes.", "user_title": "Anonymous", "datetimeon": "2010-03-08T09:27:57", "link": "pygame.sndarray", "id": 3067}, {"content": "The docs are faulty here. scroll() takes two integers and not a tuple or a list.", "user_title": "Anonymous", "datetimeon": "2009-08-30T08:39:48", "link": "Surface.scroll", "id": 2930}, {"content": "What about Duel screen displays?", "user_title": "Anonymous", "datetimeon": "2009-07-09T13:26:59", "link": "pygame.display.set_mode", "id": 2869}, {"content": "are yoh sure that sign are correct", "user_title": "Anonymous", "datetimeon": "2010-03-02T18:18:51", "link": "pygame.key", "id": 3063}, {"content": "import pygame\nfrom pygame.locals import *\n\ndef timerFunc():\n print \"Timer CallBack\"\n\npygame.init()\npygame.time.set_timer(USEREVENT+1, 100)\nwhile 1:\n for event in pygame.event.get():\n if event.type == USEREVENT+1:\n timerFunc() #calling the function wheever we get timer event.\n if event.type == QUIT:\n break", "user_title": "Anonymous", "datetimeon": "2009-08-31T04:50:51", "link": "pygame.time.set_timer", "id": 2932}, {"content": "this is helpful thanks son", "user_title": "Anonymous", "datetimeon": "2009-08-31T22:47:55", "link": "pygame.font", "id": 2933}, {"content": "Wouldn't the center simply be X = X2 - X1 Y = Y2 - Y1 ? Bottom right minus top left. That doesn't require any special math functions, yes?", "user_title": "Anonymous", "datetimeon": "2009-04-22T14:45:07", "link": "pygame.draw.rect", "id": 2557}, {"content": "#! /usr/bin/python\n# using sprites_rgba.png from http://img17.imageshack.us/img17/3166/spritesrgba.png\nimport sys, pygame, math, os, random\nfrom pygame.locals import *\npygame.init()\nsize=width,height=1024,256;screen=pygame.display.set_mode(size)\namnt=64;ampl=8;xpos=[0]*amnt;ypos=[0]*amnt;xdif=[0]*amnt;ydif=[0]*amnt;snum=[0]*amnt\nfor i in range (0,amnt,1):\n xpos[i]=random.randint(0,width)\n ypos[i]=random.randint(0,height)\n xdif[i]=random.randint(0,ampl*2)-ampl\n ydif[i]=random.randint(0,ampl*2)-ampl\n snum[i]=random.randint(0,3)\nball=pygame.image.load(\"sprites_rgba.png\");ballrect=ball.get_rect()\nsprite00=ball.subsurface(( 0,0,64,64));spriterect00=sprite00.get_rect()\nsprite01=ball.subsurface(( 64,0,64,64));spriterect01=sprite01.get_rect()\nsprite02=ball.subsurface((128,0,64,64));spriterect02=sprite02.get_rect()\nsprite03=ball.subsurface((192,0,64,64));spriterect03=sprite03.get_rect()\nwhile 1:\n for event in pygame.event.get():\n if event.type==pygame.QUIT:sys.exit()\n for i in range (0,amnt,1):\n xpos[i]+=xdif[i];ypos[i]+=ydif[i]\n if xpos[i]>width:xpos[i]-=(width+64)\n if ypos[i]>height:ypos[i]-=(height+64)\n if xpos[i]<-64:xpos[i]+=(width+64)\n if ypos[i]<-64:ypos[i]+=(height+64)\n screen.fill(0x998877)\n for i in range (0,amnt,1):\n if snum[i]==0:\n spriterect00.left=xpos[i];spriterect00.top=ypos[i];screen.blit(sprite00,spriterect00)\n if snum[i]==1:\n spriterect01.left=xpos[i];spriterect01.top=ypos[i];screen.blit(sprite01,spriterect01)\n if snum[i]==2:\n spriterect02.left=xpos[i];spriterect02.top=ypos[i];screen.blit(sprite02,spriterect02)\n if snum[i]==3:\n spriterect03.left=xpos[i];spriterect03.top=ypos[i];screen.blit(sprite03,spriterect03)\n pygame.display.flip()\n pygame.time.delay(1000/50)", "user_title": "Anonymous", "datetimeon": "2009-07-07T10:21:37", "link": "Surface.subsurface", "id": 2867}, {"content": "Usage of the event queue for USEREVENT-style events is limited by the maximum size of the SDL event queue, which is 256.\nSo, if more events (of any sort) get posted to the queue, you will get an exception stating \"error: Event queue full\".\nIf you expect to generate more than a few user events before they are posted, consider a separate queue.", "user_title": "Anonymous", "datetimeon": "2009-05-03T10:30:15", "link": "pygame.event.post", "id": 2560}, {"content": "Is this the same as pygame.surface.fill(color, rect)?", "user_title": "Anonymous", "datetimeon": "2009-05-05T15:48:33", "link": "pygame.draw.rect", "id": 2561}, {"content": "Just a note, Nautilus, the default file browser in GNOME sets copied files as 'x-special/gnome-copied-files', if you retrieve it, it holds the location as plain text.", "user_title": "Anonymous", "datetimeon": "2009-08-02T21:50:25", "link": "pygame.scrap", "id": 2901}, {"content": "so someone can share some Surface.subsurface snippet? (one about sprites is very welcome)", "user_title": "Anonymous", "datetimeon": "2009-07-06T22:14:24", "link": "Surface.subsurface", "id": 2866}, {"content": "test\ntest", "user_title": "Anonymous", "datetimeon": "2009-07-03T15:37:54", "link": "Surface.copy", "id": 2864}, {"content": "test <br /> test", "user_title": "Anonymous", "datetimeon": "2009-07-03T15:37:38", "link": "Surface.copy", "id": 2863}, {"content": "somewhere in the pygame google-group i found it's possible to have multiple sprites based on just one picture plenty of sprite drawings, without having to have them cropped file by file - how can we do this?", "user_title": "Anonymous", "datetimeon": "2009-07-03T15:37:10", "link": "Surface.copy", "id": 2862}, {"content": "missing commands for drawing bezier lines - some gpl sources can be find at http://nitrofurano.linuxkafe.com/sdlbasic - just needed to be recoded to Pygame, but it's not that difficult task at all...", "user_title": "Anonymous", "datetimeon": "2009-07-03T13:28:41", "link": "pygame.draw", "id": 2861}, {"content": "What does the error 'text has zero width' mean?\nI was simply printing 'Hello World!' to the screen.\nI was fiddling with text size, jumped from 32 to 12 and I got the above error\nNow the only way to stop the error is to have no text ('')", "user_title": "Anonymous", "datetimeon": "2009-07-03T07:24:30", "link": "Font.render", "id": 2860}, {"content": "It would be handy if the range of values was given. It appears to be 0 to 255.", "user_title": "Anonymous", "datetimeon": "2009-07-02T12:01:48", "link": "Color.r", "id": 2859}, {"content": "don't you know its a bad idea to leave your email on the internet?\n-wekul", "user_title": "Anonymous", "datetimeon": "2009-06-29T09:02:53", "link": "pygame.key", "id": 2857}, {"content": "With event.type == MOUSEBUTTONDOWN - Error! \nWrite it - event.type == pygame.MOUSEBUTTONDOWN", "user_title": "Anonymous", "datetimeon": "2009-07-02T06:50:36", "link": "pygame.mouse.get_pressed", "id": 2858}, {"content": "In Pygame 1.9 Surface.copy() does not preserve the original image's alpha. If\nyour image has an alpha you need to:\n\ns1 = s0.copy()\ns1.set_alpha(s0.get_alpha())", "user_title": "Anonymous", "datetimeon": "2010-02-21T12:03:18", "link": "Surface.copy", "id": 3059}, {"content": "In the comment on February 21, 2010 10:32am, in the last sentence I meant,\n\"for a sample i, the value of the left channel is a[i][0], the right channel\na[i][1],\" of course.", "user_title": "Anonymous", "datetimeon": "2010-02-21T14:25:02", "link": "pygame.sndarray", "id": 3060}, {"content": "When using numpy, be careful to set the type of the array correctly.\nFor instance, when you're in signed 16-bit stereo mode, e.g., when you've\ncalled\n\npygame.mixer.pre_init(size = -16, channels = 2)\n\nand you want to create an array to use for synthesizing a sound, don't forget\nthe dtype argument in\n\nsamples = numpy.zeros((n_samples, 2), dtype = numpy.int16)", "user_title": "Anonymous", "datetimeon": "2010-02-21T10:37:46", "link": "pygame.sndarray", "id": 3058}, {"content": "The above is hard to understand, at least for me. For instance, what does\n\"A stereo sound file has two values per sample\" mean? Here's what it means:\nif you're in mono, and your array has N samples, the shape of the array\nshould be (N,) (a d=1 array of N elements). If you're in stereo, then the\nshape should be (N,2) (a d=2 array, Nx2); for a sample i, the value of the left\nchannel is a[N][0], the right channel a[N][1].", "user_title": "Anonymous", "datetimeon": "2010-02-21T10:32:01", "link": "pygame.sndarray", "id": 3057}, {"content": "You could always do dir(pygame.Rect) or whatever.", "user_title": "Anonymous", "datetimeon": "2009-06-28T09:26:48", "link": "Surface.get_rect", "id": 2854}, {"content": "HTML (#rrggbbaa) format doesn't seem to work with 1.9.1 ... gives a \"ValueError: invalid argument\" exception.", "user_title": "Anonymous", "datetimeon": "2009-12-02T14:01:18", "link": "pygame.Color", "id": 3019}, {"content": "(What's even more odd is that it works from the python console ... just not in a program)", "user_title": "Anonymous", "datetimeon": "2009-12-02T14:07:37", "link": "pygame.Color", "id": 3020}, {"content": "it dun wok", "user_title": "Anonymous", "datetimeon": "2009-12-03T12:43:47", "link": "pygame.draw.arc", "id": 3021}, {"content": "Re: noise and static was occurring on my Linux box, and I was able to ameliorate it\nby specifically setting my mixer:\n pygame.mixer.pre_init(44100, -16, 2)\n pygame.init()", "user_title": "Anonymous", "datetimeon": "2009-12-07T01:43:58", "link": "Sound.play", "id": 3022}, {"content": "its set_palette_at, not set_at", "user_title": "Anonymous", "datetimeon": "2009-12-15T12:51:36", "link": "Surface.set_palette_at", "id": 3023}, {"content": "hey matthew sucks in snping", "user_title": "Anonymous", "datetimeon": "2009-12-16T15:50:04", "link": "pygame.draw.ellipse", "id": 3024}, {"content": "ellipserect=Rect(cmx,cmy,mx-cmx,my-cmy)\nellipserect.normalize()\ndraw.ellipse(screen,color,ellipserect,1)\nwhy ValueError: width greater than ellipse radius?", "user_title": "Anonymous", "datetimeon": "2009-12-18T13:28:16", "link": "pygame.draw.ellipse", "id": 3025}, {"content": "It plays dot-to-dot with the given points.", "user_title": "Anonymous", "datetimeon": "2010-02-19T01:32:43", "link": "pygame.draw.lines", "id": 3055}, {"content": "In ver. 1.9.1, if I try to initialize by calling\n\npygame.mixer.pre_init(...)\npygame.init()\n\nand then try to play a sound buffer, I get no output unless I open a graphics\nwindow first by calling pygame.display.set_mode(...). If, on the other hand,\nI'm not doing graphics and I initialize with just\n\npygame.mixer.init(...)\n\nI can get sound without opening any window.", "user_title": "Anonymous", "datetimeon": "2010-02-21T10:20:13", "link": "pygame.mixer", "id": 3056}, {"content": "to get a picture instead of black and white cursor, in order :\n1) simply make a transparent cursor\n pygame.mouse.set_cursor(pygame.mouse.set_cursor((8,8),(0,0),(0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0))\n\n2) constantly actualise the postition of the picture to the postion of the cursor\n cursor_picture==pygame.image.load('./cursor.png').convert_alpha()\n while True:\n for event in pygame.event.get():\n if event.type==QUIT:\n exit()\n screen.fill(black)\n screen.blit(mouse_cursor, pygame.mouse.get_pos())\n pygame.display.update()", "user_title": "Anonymous", "datetimeon": "2009-06-27T17:10:22", "link": "pygame.mouse.set_cursor", "id": 2850}, {"content": "Not sure why I am leaving this here... but can anyone tell me how you would rotate a surface? Its 3am so excuse my ignorance :D", "user_title": "Anonymous", "datetimeon": "2009-11-30T03:38:25", "link": "pygame.Surface", "id": 3016}, {"content": "Theres is an error in documentation: pygame.cursor.compile does not exists, but pygame.cursors.compile do", "user_title": "Anonymous", "datetimeon": "2009-11-30T18:49:41", "link": "pygame.cursors.compile", "id": 3017}, {"content": "is there anyway to rotate a rectangle with out making it an image?", "user_title": "Anonymous", "datetimeon": "2009-11-13T15:06:41", "link": "pygame.transform.rotate", "id": 3009}, {"content": "Minor problem: When I run the program using this for an image, it says \"cannot load image!\" could anyone help me?", "user_title": "Anonymous", "datetimeon": "2009-11-15T09:50:56", "link": "pygame.image.load", "id": 3010}, {"content": "We need the posibility to rotate without the image being rescaled, just keep its original size like in PIL.\n\nim.rotate(angle, filter=NEAREST, expand=0) \nThe expand argument, if true, indicates that the output image should be made \nlarge enough to hold the rotated image. \nIf omitted or false, the output image has the same size as the input image.", "user_title": "Anonymous", "datetimeon": "2009-11-21T05:03:48", "link": "pygame.transform.rotate", "id": 3012}, {"content": "In pygame 1.9.1 this function does not return negative values instead it returns a \n0 for all values < 0 (of the direction vector). If you are looking for a way to \ncompute a collision response look at Mask.overlap_area.", "user_title": "Anonymous", "datetimeon": "2009-11-23T10:34:10", "link": "Mask.overlap", "id": 3013}, {"content": "can anyone please explain what this function do? at best whith an example", "user_title": "Anonymous", "datetimeon": "2009-11-27T06:41:00", "link": "pygame.draw.lines", "id": 3014}, {"content": "Meta key is 'Apple' or 'Command' on a mac.", "user_title": "Anonymous", "datetimeon": "2009-09-07T03:31:29", "link": "pygame.key", "id": 2939}, {"content": "why doesn't this blit on the screen???\n\n\n for star in self.stars:\n self.screen.blit(self.a, star.pos)\n star.update()\n self.screen.blit(star.image, star.pos)\n pygame.display.update()", "user_title": "Anonymous", "datetimeon": "2009-09-08T22:04:17", "link": "Surface.blit", "id": 2941}, {"content": "HELP ME ONMMGMGMGMGMGMG", "user_title": "Anonymous", "datetimeon": "2009-11-12T10:31:24", "link": "Surface.convert_alpha", "id": 3006}, {"content": "aghahhahahahah I LOVE THIS OMG (*&^%$#@Q", "user_title": "Anonymous", "datetimeon": "2009-11-12T10:31:43", "link": "Surface.convert_alpha", "id": 3007}, {"content": "In my game the screen is scaled. This can cause havoc with the mouse positioning. I made this function:\n\n def get_mouse_pos(pos):\n\treturn (pos[0] * (1280.0/float(game.game_scaled[0])),pos[1] * (720.0/float(game.game_scaled[1])))\n\nReplace 1280.0 and 720.0 with the resolution of the pre-scaled game and game.game_scaled with a sequence containing the scaled resolution.", "user_title": "Anonymous", "datetimeon": "2009-11-12T12:43:39", "link": "pygame.transform.scale", "id": 3008}, {"content": "It seems to work if you set the display of the movie to a surface of the same size and then blit that surface to the screen.", "user_title": "Anonymous", "datetimeon": "2009-06-24T20:28:26", "link": "pygame.movie", "id": 2846}, {"content": "This documentation does not seem to match what is currently in 1.8.1. \nInstead: pygame.transform.threshold(DestSurface, Surface, color, threshold = (0,0,0,0), diff_color = (0,0,0,0), change_return = True, Surface =None): return num_threshold_pixels", "user_title": "Anonymous", "datetimeon": "2009-11-11T15:11:43", "link": "pygame.transform.threshold", "id": 3004}, {"content": "For some odd reason anything in pygame.Color gets an error message like it doesn't exist.\nIf I try using pygame.Color.r, it says that Color has no attribute r. I tried redownloading\npygame, but nothing diffrent.", "user_title": "Anonymous", "datetimeon": "2009-09-11T20:56:38", "link": "pygame.Color", "id": 2943}, {"content": "I am having the same problem on XP. The sound plays, but the video does not.", "user_title": "Anonymous", "datetimeon": "2009-06-24T20:09:34", "link": "pygame.movie", "id": 2845}, {"content": "\"current_h, current_h: Width and height of the current video mode, or of the\"[...]\nOne of them should be \"current_w\" instead.", "user_title": "Anonymous", "datetimeon": "2009-11-07T06:40:09", "link": "pygame.display.Info", "id": 2999}, {"content": "For me, PixelArray works much faster (4 or 5 times faster) than Surfarray. I wanted to set every pixel in my off-screen surface individually. Creating the surface, creating a PixelArray on it, and going through the pixels one-by-one is much faster than creating the bitmap using numpy and calling surfarray.make_surface.", "user_title": "Anonymous", "datetimeon": "2009-11-08T08:17:58", "link": "pygame.PixelArray", "id": 3000}, {"content": "\u00c3?\u00c3? \u00c3\u00ac\u00c3\u00ae\u00c3\u008a\u00c3\u00a5\u00c3\u00b2\u00c3\u00a5 \u00c3\u00ad\u00c3\u00a0\u00c3\u00b0\u00c3\u009a\u00c3\u00b1\u00c3\u00ae\u00c3\u00a2\u00c3\u00a0\u00c3\u00b2\u00c3\u008c \u00c3\u00ad\u00c3\u00a5\u00c3\u00b1\u00c3\u00aa\u00c3\u00ae\u00c3\u00ab\u00c3\u008c\u00c3\u00aa\u00c3\u00ae \u00c3\u00af\u00c3\u00a0\u00c3\u00b0\u00c3\u00a0\u00c3\u00ab\u00c3\u00ab\u00c3\u00a5\u00c3\u00ab\u00c3\u008c\u00c3\u00ad\u00c3\u00bb\u00c3\u00b5 \u00c3\u00ab\u00c3\u009a\u00c3\u00ad\u00c3\u009a\u00c3\u00a9 \u00c3\u00b0\u00c3\u00bf\u00c3\u0080\u00c3\u00ae\u00c3\u00ac \u00c3\u00b1 \u00c3\u0080\u00c3\u00b0\u00c3\u00b3\u00c3\u00a3\u00c3\u00ae\u00c3\u00ac. \u00c3?\u00c3\u00b2\u00c3\u00ae\u00c3\u00a1\u00c3\u00bb \u00c3\u009a\u00c3\u00b5 \u00c3\u0080\u00c3\u00ab\u00c3\u009a\u00c3\u00ad\u00c3\u00bb \u00c3\u00b1\u00c3\u00ab\u00c3\u009a\u00c3\u00a2\u00c3\u00a0\u00c3\u00ab\u00c3\u009a\u00c3\u00b1\u00c3\u008c\n \u00c3\u00ae\u00c3\u00ad\u00c3\u009a \u00c3\u00a1\u00c3\u00b3\u00c3\u0080\u00c3\u00b3\u00c3\u00b2 \u00c3\u00a2\u00c3\u009a\u00c3\u0080\u00c3\u00ad\u00c3\u00bb, \u00c3\u00aa\u00c3\u00a0\u00c3\u00aa \u00c3\u00ae\u00c3\u0080\u00c3\u00ad\u00c3\u00a0. \u00c3\u008d\u00c3\u00ae \u00c3\u009c\u00c3\u00b2\u00c3\u00ae \u00c3\u00a1\u00c3\u00b3\u00c3\u0080\u00c3\u00a5\u00c3\u00b2 \u00c3\u00ac\u00c3\u00a5\u00c3\u0080\u00c3\u00ab\u00c3\u00a5\u00c3\u00ad\u00c3\u00ad\u00c3\u00a5\u00c3\u00a9.\n I don't know english, write russian. translate.google for you help! :)", "user_title": "Anonymous", "datetimeon": "2009-11-08T18:19:31", "link": "pygame.draw.aaline", "id": 3001}, {"content": "uiuuiu", "user_title": "Anonymous", "datetimeon": "2009-11-11T05:10:30", "link": "pygame.key.get_focused", "id": 3002}, {"content": "its very ...................", "user_title": "Anonymous", "datetimeon": "2009-11-11T05:11:00", "link": "pygame.key.get_focused", "id": 3003}, {"content": "Tip for noobs. This was killing me. If you're trying this for the first time and getting no sound, it may be because you're program exits before the playback thread completes. See pygame/examples/sound.py: it waits at the end.", "user_title": "Anonymous", "datetimeon": "2009-12-19T19:22:25", "link": "Channel.play", "id": 3026}, {"content": "Tip for noobs. This was killing me. If you're trying this for the first time and\ngetting no sound, it may be because you're program exits before the playback\nthread completes. See pygame/examples/sound.py: it waits at the end.", "user_title": "Anonymous", "datetimeon": "2009-12-19T19:23:52", "link": "Sound.play", "id": 3027}, {"content": "You missed pygame.Mixer", "user_title": "Anonymous", "datetimeon": "2009-10-27T02:47:35", "link": "pygame.init", "id": 2991}, {"content": "Tried this on XP with pygame 1.9 - video works, but sound doesn't. Uninitializing pygame.mixer does not seem to effect behavior.", "user_title": "Anonymous", "datetimeon": "2009-10-27T20:31:08", "link": "pygame.movie", "id": 2992}, {"content": "As of 1.9, it does not appear the loop param exists.", "user_title": "Anonymous", "datetimeon": "2009-10-27T20:42:45", "link": "Movie.play", "id": 2993}, {"content": "If your movies play slow or occasionally freeze, setting a limit to framerates seems to resolve the problem. \nSee the documentation on Clock.tick(framerate) on how to limit framerates:\nhttp://www.pygame.org/docs/ref/time.html#Clock.tick", "user_title": "Anonymous", "datetimeon": "2009-10-29T10:53:02", "link": "pygame.movie.Movie", "id": 2994}, {"content": "The example code and the explanation contradict each other.\n\nThe example returns true if lost, supposedly, and the explanation supposedly returns False if lost?", "user_title": "Anonymous", "datetimeon": "2009-11-04T18:10:25", "link": "pygame.scrap.lost", "id": 2995}, {"content": "Does not work *at all*. No changes occur when I change any pixels.", "user_title": "Anonymous", "datetimeon": "2009-11-05T22:12:49", "link": "pygame.PixelArray", "id": 2996}, {"content": "Remember, there are not semitones. Thus, each semitone is represented by an integer named \"note\".\nIf you are a beginer it is a good choice to start practicing with the middle notes.\nC 4 is the note number 61. Have fun!.\n\nTourette", "user_title": "Anonymous", "datetimeon": "2009-12-21T19:52:02", "link": "Output.note_on", "id": 3028}, {"content": "pygame.mask.from_surface(Surface, threshold) -> Mask\nno Keyword argument", "user_title": "Anonymous", "datetimeon": "2009-10-25T18:04:44", "link": "pygame.mask.from_surface", "id": 2989}, {"content": "you use a class to get the var\nfor example\nclass myimage(object):\n image = (your image)\nthen to blit\n\ndisplay.blit(myimage.image, (0,0))\n\nso that you are only accesing the one variable to blit instead of multiple instances of the same", "user_title": "Anonymous", "datetimeon": "2009-08-04T11:33:28", "link": "Surface.copy", "id": 2905}, {"content": "if you want to measure it over a period of your choosing just compute a moving average of the result of clock.tick(), like this:\n\n...\n\nrecent_frame_lengths = [ 100, 100, 100, 100, 100 ]\nrfl_array_len = float( len( recent_frame_lengths ) )\n\n...\nmain():\n...\ndt = clock.tick()\nrecent_frame_lengths.pop(0)\nrecent_frame_lengths.append(dt)\naverage_frame_length = recent_frame_lengths / rfl_array_len\nframes_per_second = 1000. / average_frame_length", "user_title": "Anonymous", "datetimeon": "2010-05-18T12:08:59", "link": "Clock.get_fps", "id": 3124}, {"content": "button can be at least 1-6. 4/5 are for the scroll wheel. \nThe squeeze-click on new Apple mice is 6.", "user_title": "Anonymous", "datetimeon": "2010-05-20T22:13:33", "link": "pygame.event", "id": 3126}, {"content": "event polling:\n#self.keys is [] 256 len. \nself.mouse = ((0,0), 0, 0, 0, 0, 0, 0) #(pos, b1,b2,b3,b4,b5,b6)\n#squeezing a new Apple mouse is button 6. \nfor event in pygame.event.get():\n\tif event.type == pygame.QUIT:\n\t\tself.running = 0\n\telif event.type == pygame.MOUSEBUTTONDOWN:\n\t\tself.mouse[event.button] = 1\n\t\tself.mouse[0] = event.pos\n\telif event.type == pygame.MOUSEBUTTONUP:\n\t\tself.mouse[event.button] = 0\n\t\tself.mouse[0] = event.pos\n\telif event.type == pygame.MOUSEMOTION:\n\t\tself.mouse[0] = event.pos\n\telif event.type == pygame.KEYDOWN:\n\t\tself.keys[event.key % 255] = 1\n\telif event.type == pygame.KEYUP:\n\t\tself.keys[event.key % 255] = 0", "user_title": "Anonymous", "datetimeon": "2010-05-20T22:31:20", "link": "pygame.mouse.get_pressed", "id": 3127}, {"content": "PyGame beginners, please don't look at Matthew N. Brown's code below.\n\nThat is the worst Python code I have ever seen, and it offers almost no examples of how to use pygame.draw.circle.", "user_title": "Anonymous", "datetimeon": "2010-05-17T13:22:44", "link": "pygame.draw.circle", "id": 3123}, {"content": "What about subsubsubsubsurfaces?", "user_title": "Anonymous", "datetimeon": "2009-06-14T09:27:19", "link": "Surface.subsurface", "id": 2830}, {"content": "for some reason this gives always wrong numbers.\nmask_creep = pygame.mask.from_surface(creep.image)\nmask_player = pygame.mask.from_surface(p.image)\np.life -= mask_player.overlap_area(\n mask_creep, (creep.rect.x-p.rect.x,creep.rect.y-p.rect.y))\n\np.life is the life of the player, and I want to drain it by the amount of pixels overlapping with creep.\nhowever, it seems it hits before it should, and with mask_creep.invert() I seem to get more accurate hits, which makes no sense...", "user_title": "Anonymous", "datetimeon": "2009-10-24T06:03:49", "link": "pygame.mask", "id": 2984}, {"content": "get also this warning : nsquickdrawview \nif I compile.\n\ncan\u00c2\u008et use pygame with py2app\n\npython 2.4 or 2.5\nmaxos leopard\neclipse", "user_title": "Anonymous", "datetimeon": "2010-01-05T21:56:27", "link": "pygame.display.init", "id": 3032}, {"content": "This function is not working as stated. It requires a 'rectstyle' argument (ie. a tuple with rect's parameters). it tests for collisions using this 'rectsytle' object and returns any that are colliding along with their values. Not really useful since no one needs a tuple of rect's parameters back. this would be really nice if it worked as stated...", "user_title": "Anonymous", "datetimeon": "2009-10-24T03:33:00", "link": "Rect.collidedict", "id": 2982}, {"content": "The math isn't that hard, you just have to think in relative coordinates then.\nThe key is to look at the center of the image, as these coordinates wont change by rotating it.\nThe coordinates of the center of the image on the screen are given by:\nx_cntr.. x coordinate of the center\ny_cntr.. y coordinate of the center\n\npos_org = (x_cntr - image_org.get_rect().width / 2,\n y_cntr - image_org.get_rect().height / 2) \n \"\"\"gives position of upper left corner of image_org (not rotated)\n depending on the center coordinates for the Surface.blit function\"\"\"\nimage_rotated = pygame.transform.rotate(image_org, angle) #rotate image\npos_new = (x_pos_org - image_rotated.get_rect().width / 2,\n y_pos_org - image_rotated.get_rect().height / 2)\n #get new position for upper left corner for rotated image", "user_title": "Anonymous", "datetimeon": "2010-01-21T11:14:00", "link": "pygame.transform.rotate", "id": 3036}, {"content": "x_pos_org and y_pos_org naturally need to be x_cntr and y_cntr.. sry", "user_title": "Anonymous", "datetimeon": "2010-01-21T11:20:31", "link": "pygame.transform.rotate", "id": 3037}, {"content": "fuck u", "user_title": "Anonymous", "datetimeon": "2010-01-21T12:35:00", "link": "PixelArray.replace", "id": 3038}, {"content": "doesnt work!", "user_title": "Anonymous", "datetimeon": "2010-01-25T12:07:07", "link": "pygame.display.set_caption", "id": 3039}, {"content": "WTH!???", "user_title": "Anonymous", "datetimeon": "2010-01-25T12:07:16", "link": "pygame.display.set_caption", "id": 3040}, {"content": "Where should the image file be save at?", "user_title": "Anonymous", "datetimeon": "2010-01-29T02:01:23", "link": "pygame.image.load", "id": 3042}, {"content": "To clip the blit, you have to pass a rect like this (0, 0, clipWidth, clipHeigth):\n\nexample:\n\nsForeground.blit(sText, rText, (0, 0, 32, 32)):\n\ndraw the surface sText into sForeground at topleft position defined with the rect rText,\nclippping the sText by 32x32 pixel box", "user_title": "Anonymous", "datetimeon": "2010-01-30T14:45:56", "link": "Surface.blit", "id": 3043}, {"content": "actually, it DOES WORK. put it in the right group", "user_title": "Anonymous", "datetimeon": "2010-02-02T15:56:23", "link": "pygame.display.set_caption", "id": 3044}, {"content": "how does this even work?????", "user_title": "Anonymous", "datetimeon": "2010-02-02T23:04:45", "link": "Rect.move", "id": 3045}, {"content": "Pygame 1.9: DeprecationWarning: integer argument expected, got float\n\nThe tuple elements must be integers. The line number in the warning message will\nindicate your enclosing function or method. I found this very misleading and\nfrustrating.", "user_title": "Anonymous", "datetimeon": "2010-02-06T02:16:05", "link": "pygame.transform.smoothscale", "id": 3048}, {"content": "By calling Clock.tick -> clock.tick", "user_title": "Anonymous", "datetimeon": "2010-02-08T10:30:27", "link": "Clock.tick_busy_loop", "id": 3049}, {"content": "that code you posted (TWICE) doesnt show how to draw a circle in pygame.\n It shows an example of a complex couple hundred lines of interactive \nphysics engine that just so happens to take im guessing less than\n5 lines to use pygame.draw.circle. Its a waste of space Good job for getting your code out into \nthe world. Now people dont like you.", "user_title": "Anonymous", "datetimeon": "2010-02-08T22:31:47", "link": "pygame.draw.circle", "id": 3050}, {"content": "TypeError: descriptor 'collidelistall' requires a 'pygame.Rect' object but received a 'list'\n\n...so it doesn't like a list, but a pygame.Rect? That does't make sense.", "user_title": "Anonymous", "datetimeon": "2009-10-22T05:21:56", "link": "Rect.collidelistall", "id": 2977}, {"content": "Also using\n\n my_surface=pygame.Surface([width, height]).convert()\n\nseems to be just as effective", "user_title": "Anonymous", "datetimeon": "2009-10-16T22:13:15", "link": "pygame.Surface", "id": 2974}, {"content": "x = x2 - x1, y = y2 - y1 gives the width and height of the rectangle.\n(x2 - x1) / 2, (y2 - y1) / 2 gives the center coordinates.\nI don't know of a built-in method on the Rect that gives the center coords.", "user_title": "Anonymous", "datetimeon": "2009-10-21T20:50:31", "link": "pygame.draw.rect", "id": 2976}, {"content": "AWSOME", "user_title": "Anonymous", "datetimeon": "2009-10-08T11:50:27", "link": "pygame.draw", "id": 2967}, {"content": "Aloha! jdv", "user_title": "Anonymous", "datetimeon": "2009-09-20T07:33:05", "link": "Rect.move_ip", "id": 2954}, {"content": "Worth mentioning: the initial angle must be less than the final angle; otherwise it will draw the full elipse.", "user_title": "Anonymous", "datetimeon": "2009-10-01T14:16:05", "link": "pygame.draw.arc", "id": 2964}, {"content": "If you want a circle with a *good* outline, use this:\n\ndef drawcircle(image, colour, origin, radius, width=0):\n\tif width == 0:\n\t\tpygame.draw.circle(image,colour,intlist(origin),int(radius))\n\telse:\n\t\tif radius > 65534/5: radius = 65534/5\n\t\tcircle = pygame.Surface([radius*2+width,radius*2+width]).convert_alpha()\n\t\tcircle.fill([0,0,0,0])\n\t\tpygame.draw.circle(circle, colour, intlist([circle.get_width()/2, circle.get_height()/2]), int(radius+(width/2)))\n\t\tif int(radius-(width/2)) > 0: pygame.draw.circle(circle, [0,0,0,0], intlist([circle.get_width()/2, circle.get_height()/2]), abs(int(radius-(width/2))))\n\t\timage.blit(circle, [origin[0] - (circle.get_width()/2), origin[1] - (circle.get_height()/2)])", "user_title": "Anonymous", "datetimeon": "2009-09-30T10:47:07", "link": "pygame.draw.circle", "id": 2963}, {"content": "I think the problem has something to do with encapsulation.\nTry:\ncurrent_song = 0\ndef Play_Next_Song():\n global current_song\n if pygame.mixer.music.get_busy() == False:\n print songs[current_song]\n pygame.mixer.music.load(songs[current_song])\n pygame.mixer.music.play() \n current_song += 1", "user_title": "Anonymous", "datetimeon": "2009-09-21T11:39:31", "link": "pygame.mixer.music.load", "id": 2956}, {"content": "WHAT?", "user_title": "Anonymous", "datetimeon": "2009-09-23T15:22:38", "link": "pygame.draw.lines", "id": 2957}, {"content": "Can I draw circles too?", "user_title": "Anonymous", "datetimeon": "2009-09-24T13:53:40", "link": "pygame.draw.circle", "id": 2958}, {"content": "how can i get a mouse wheel value? please show in a snippet - i don't know how to use pygame.MOUSEBUTTONDOWN and pygame.MOUSEBUTTONUP", "user_title": "Anonymous", "datetimeon": "2009-09-25T11:53:07", "link": "pygame.mouse.get_pressed", "id": 2960}, {"content": "Setting the line width does not work!", "user_title": "Anonymous", "datetimeon": "2009-09-29T10:21:37", "link": "pygame.draw.rect", "id": 2962}, {"content": "i dont like pygame very much\nrectangles can go poop themselves", "user_title": "Anonymous", "datetimeon": "2010-05-21T11:08:52", "link": "Rect.collidelistall", "id": 3128}, {"content": "In fact it should be\n\"By calling Clock.tick(40)\" -> \"Clock.tick_busy_loop(40)\"", "user_title": "Anonymous", "datetimeon": "2010-06-01T03:26:55", "link": "Clock.tick_busy_loop", "id": 3130}, {"content": "No, waiting at the very beginning or very end of a loop does not make\nmuch difference. Moving it to the middle might: \n screen.fill(...)\n clock.tick(30)\n screen.blit(...)\nwill mostly show a blank screen because the fill will be visible\nduring the wait.", "user_title": "Anonymous", "datetimeon": "2010-06-01T07:55:40", "link": "Clock.tick", "id": 3131}, {"content": "haleluja", "user_title": "Anonymous", "datetimeon": "2010-06-02T03:11:33", "link": "Rect.collidelist", "id": 3133}, {"content": "PixelArray works faster than SurfArray for me also.", "user_title": "Anonymous", "datetimeon": "2010-06-02T20:55:16", "link": "pygame.PixelArray", "id": 3134}, {"content": "Only seems to block if you start another piece of music playing while it's still\nfading out.", "user_title": "Anonymous", "datetimeon": "2010-06-06T16:23:37", "link": "pygame.mixer.music.fadeout", "id": 3135}, {"content": "If you're having trouble with color keys, try image.set_alpha(None) on each individual subsurface.\nsubsurface seems not to always inherit its parent's alpha setting, so if the parent source image has an alpha then color key is ignored in subsurfaces.\n\nYou can easily remove the alpha channel in GIMP by right-clicking the background layer and selecting \"remove alpha channel\" to fix all your problems also :)", "user_title": "Anonymous", "datetimeon": "2010-06-08T23:09:21", "link": "Surface.subsurface", "id": 3139}, {"content": "You should use pygame.display.Info.current_h and pygame.display.Info.current_w.", "user_title": "Anonymous", "datetimeon": "2010-06-10T20:42:30", "link": "pygame.display.get_surface", "id": 3140}, {"content": "pygame.display.Info().current_h and pygame.display.Info().current_w", "user_title": "Anonymous", "datetimeon": "2010-06-10T20:43:14", "link": "pygame.display.get_surface", "id": 3141}, {"content": "screen = pygame.display.set_mode(...)\nscreen.get_size()", "user_title": "Anonymous", "datetimeon": "2010-06-10T20:55:19", "link": "pygame.display.get_surface", "id": 3142}, {"content": "11246579455877\nppqu s442", "user_title": "Anonymous", "datetimeon": "2010-06-11T18:56:04", "link": "pygame.mixer.music.play", "id": 3143}, {"content": "import pygame\nimport pygame, sys,os, time \nfrom pygame.locals import * \nfrom pygame.color import THECOLORS \nimport platform, os \nif platform.system()==\"Windows\": \n os.environ['SDL_VIDEODRIVER']='windib'\npygame.init()\nwindow = pygame.display.set_mode((600,600)) \nscreen = pygame.display.get_surface() \npygame.display.set_caption('Excercise 5') \nscreen.fill((0,0,0))\n\nclass GfxCursor:\n \"\"\"\n Replaces the normal pygame cursor with any bitmap cursor\n \"\"\"\n\n def __init__(self,surface,cursor=None,hotspot=(0,0)):\n \"\"\"\n surface = Global surface to draw on\n cursor = surface of cursor (needs to be specified when enabled!)\n hotspot = the hotspot for your cursor\n \"\"\"\n self.surface = surface\n self.enabled = 0\n self.cursor = None\n self.hotspot = hotspot\n self.bg = None\n self.offset = 0,0\n self.old_pos = 0,0\n \n if cursor:\n self.setCursor(cursor,hotspot)\n self.enable()\n\n def enable(self):\n \"\"\"\n Enable the GfxCursor (disable normal pygame cursor)\n \"\"\"\n if not self.cursor or self.enabled: return\n pygame.mouse.set_visible(0)\n self.enabled = 1\n\n def disable(self):\n \"\"\"\n Disable the GfxCursor (enable normal pygame cursor)\n \"\"\"\n if self.enabled:\n self.hide()\n pygame.mouse.set_visible(1)\n self.enabled = 0\n\n def setCursor(self,cursor,hotspot=(0,0)):\n \"\"\"\n Set a new cursor surface\n \"\"\"\n if not cursor: return\n self.cursor = cursor\n self.hide()\n self.show()\n self.offset = 0,0\n self.bg = pygame.Surface(self.cursor.get_size())\n pos = self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]\n self.bg.blit(self.surface,(0,0),\n (pos[0],pos[1],self.cursor.get_width(),self.cursor.get_height()))\n\n self.offset = hotspot\n\n def setHotspot(self,pos):\n \"\"\"\n Set a new hotspot for the cursor\n \"\"\"\n self.hide()\n self.offset = pos\n\n def hide(self):\n \"\"\"\n Hide the cursor (useful for redraws)\n \"\"\"\n if self.bg and self.enabled:\n return self.surface.blit(self.bg,\n (self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]))\n\n def show(self):\n \"\"\"\n Show the cursor again\n \"\"\"\n if self.bg and self.enabled:\n pos = self.old_pos[0]-self.offset[0],self.old_pos[1]-self.offset[1]\n self.bg.blit(self.surface,(0,0),\n (pos[0],pos[1],self.cursor.get_width(),self.cursor.get_height()))\n return self.surface.blit(self.cursor,pos)\n\n def update(self,event):\n \"\"\"\n Update the cursor with a MOUSEMOTION event\n \"\"\"\n self.old_pos = event.pos\n\nif __name__ == '__main__': #test it out\n import pygame.draw\n pygame.init()\n screen = pygame.display.set_mode((400, 300))\n screen.fill((50, 50, 111), (0, 0, 400, 150))\n pygame.display.flip()\n pygame.display.set_caption('Test the GfxCursor (and paint)')\n \n image = pygame.Surface((20, 20))\n pygame.draw.circle(image, (50, 220, 100), (10, 10), 8, 0)\n pygame.draw.circle(image, (220, 200, 50), (10, 10), 8, 2)\n image.set_at((9, 9), (255,255,255))\n image.set_colorkey(0, pygame.RLEACCEL)\n \n magicbox = pygame.Rect(10, 10, 100, 90)\n magiccolor = 0\n \n cursor = GfxCursor(screen, image, (10, 10))\n finished = 0\n downpos = None\n while not finished:\n dirtyrects = []\n dirtyrects.extend([cursor.hide()])\n for e in pygame.event.get():\n if e.type in (pygame.QUIT, pygame.KEYDOWN):\n finished = 1\n break\n elif e.type == pygame.MOUSEBUTTONDOWN:\n cursor.disable()\n downpos = e.pos\n elif e.type == pygame.MOUSEBUTTONUP:\n cursor.enable()\n downpos = None\n elif downpos and e.type == pygame.MOUSEMOTION:\n r = pygame.draw.line(screen, (100,100,100), downpos, e.pos, 2)\n dirtyrects.append(r)\n downpos = e.pos\n cursor.update(e)\n elif not downpos and e.type == pygame.MOUSEMOTION:\n cursor.update(e)\n \n magiccolor = (magiccolor + 2) % 255\n r = screen.fill((0, 0, magiccolor), magicbox)\n dirtyrects.append(r)\n \n #here's how we sandwich the flip/update with cursor show and hide\n dirtyrects.extend([cursor.show()])\n pygame.display.update(dirtyrects)\n \n pygame.time.delay(5) #should be time.wait(5) with pygame-1.3 :]", "user_title": "Anonymous", "datetimeon": "2010-06-13T02:45:37", "link": "Color.r", "id": 3145}, {"content": "I get an 'UnboundLocalError: local variable 'fonts' referenced before assignment' error. \nI'm on Mac OS X 10.4 Tiger with Python 2.6.4, Pygame 1.9.1.", "user_title": "Anonymous", "datetimeon": "2010-06-19T22:22:14", "link": "pygame.font.get_fonts", "id": 3146}, {"content": "I'm no expert but wouldn't it be faster to use the math.hypot(a, b)\nfunction for determining length instead of writing your own function?", "user_title": "Anonymous", "datetimeon": "2009-06-09T21:45:55", "link": "Joystick.get_axis", "id": 2759}, {"content": "You can also move the corner:\n\nsquare_corner=(x-square_dim[0]*sqrt(2)/2*sin((abs(angle)+45)*pi/180),\n y-square_dim[1]*sqrt(2)/2*sin((45+abs(angle))*pi/180))\n screen.blit(pygame.transform.rotate(square,angle), square_corner)\n\nBeware: sin calculates the angle in rad but rotate needs angles in degrees.", "user_title": "Anonymous", "datetimeon": "2009-06-08T14:31:39", "link": "pygame.transform.rotate", "id": 2758}, {"content": "ACTIVEEVENT has two attributes. \"gain\" is set to 0 or one depending if the type of focus was lost or gained. \"state\" will equal 1 for mouse focus, 2 for keyboard focus, or 4 for window iconification.", "user_title": "Anonymous", "datetimeon": "2009-06-03T22:15:32", "link": "pygame.event", "id": 2755}, {"content": "If you want only certain events to be enabled -- you will need to disable them all, and only then enable the ones that you need:\n#--\npygame.event.set_allowed(None)\nprint map(pygame.event.get_blocked,range(1,33))\npygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.USEREVENT])\nprint map(pygame.event.get_blocked,range(1,33))\n#--\nbelow is the output:\n[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1]", "user_title": "Anonymous", "datetimeon": "2009-05-30T16:39:22", "link": "pygame.event.set_allowed", "id": 2754}, {"content": "Also, it should maybe be noted that it returns 4 tuple items, not just 3.\nMy guess is RGBA tuple instead of RGB, but I'm not an expert :P", "user_title": "Anonymous", "datetimeon": "2009-05-27T02:45:25", "link": "Surface.unmap_rgb", "id": 2753}, {"content": "convert a mapped integer color value into a Color\nSurface.map_rgb(mapped_int): return Color\n ^\nShouldn't it be \"Surface.unmap_rgb\"?", "user_title": "Anonymous", "datetimeon": "2009-05-27T02:43:16", "link": "Surface.unmap_rgb", "id": 2752}, {"content": "Tracked module playback with Pygame has a lower playback volume than usual,\nand I need to find something like \"stereo separation\" feature yet.", "user_title": "Anonymous", "datetimeon": "2009-05-21T06:41:14", "link": "pygame.mixer.music", "id": 2751}, {"content": "Yes, I just found it out, and it plays tracked music modules such as MOD or XM.\nBut IT (Impulse Tracker) modules don't play correctly. Wow. I used fmod with Python bindings\nall the time, and Pygame supported it already. I wish I knew that sooner :D", "user_title": "Anonymous", "datetimeon": "2009-05-21T06:35:19", "link": "pygame.mixer.music", "id": 2750}, {"content": "While using this, it seems that it returns true when the music is paused. Anyone else having this problem, if it's a problem?", "user_title": "Anonymous", "datetimeon": "2009-05-19T11:35:42", "link": "pygame.mixer.music.get_busy", "id": 2749}, {"content": "If you want to draw the sprites in your group in the opposite order you could try something like this:\n#Suppose that you keep a list of objects in \"Sprites\"\nSprites=[]\n#You also have:\nAllsprites=pygame.sprite.OrderedUpdates(Sprites)\n\nWhenever you add something to the list of sprites, you want to add it to Allsprites like this:\ndef create_sprite():\n a=SomeClassForYourSprite()\n Sprites.append(a)\n Allsprites.add(a)\n\n#That is how I normally add sprites, but unfortunately I was getting the reverse order of what I wanted so I did this:\ndef create_sprite():\n Allsprites.empty() #This removes all objects from your group\n a=SomeClassForYourSprite()\n Sprites.insert(0,a) #Placing your new sprite at the front of the list\n for sprite in Sprites:\n Allsprites.add(sprite)\n\nThis should reverse the order for you, allowing the newest sprite created to appear at the bottom instead of the top", "user_title": "Anonymous", "datetimeon": "2009-05-11T00:29:55", "link": "pygame.sprite.OrderedUpdates", "id": 2744}, {"content": "Is this function blocking? I mean... when it returns and my program flow continues, can I be assured that the display has updated on the actual screen?", "user_title": "Anonymous", "datetimeon": "2011-01-14T19:38:50", "link": "pygame.display.update", "id": 3730}, {"content": "Is there a way to set a path to a font file?", "user_title": "Anonymous", "datetimeon": "2011-01-16T15:32:54", "link": "pygame.font.SysFont", "id": 3734}, {"content": "eee", "user_title": "Anonymous", "datetimeon": "2011-01-16T19:25:25", "link": "pygame.image.tostring", "id": 3735}, {"content": "The last comment was spam", "user_title": "Anonymous", "datetimeon": "2011-01-17T21:08:17", "link": "pygame.draw.polygon", "id": 3737}, {"content": "[(x,y), (x1,y1), (x2,y2)]", "user_title": "Anonymous", "datetimeon": "2011-01-17T21:08:51", "link": "pygame.draw.polygon", "id": 3738}, {"content": "The core algorithm works with 32-bit surfaces. When a 24-bit surface is passed, the pixel data is converted to 32-bit data before the actual transformation, and then it's converted back into 24-bits again, which means 2 extra conversions of the whole image. This would especially be troublesome with large images.", "user_title": "Anonymous", "datetimeon": "2011-01-18T03:41:16", "link": "pygame.transform.smoothscale", "id": 3739}, {"content": "re", "user_title": "Anonymous", "datetimeon": "2011-01-18T06:48:13", "link": "pygame.mixer.music.pause", "id": 3740}, {"content": "Hello world", "user_title": "Anonymous", "datetimeon": "2011-01-19T16:38:01", "link": "index.html", "id": 3743}, {"content": "what", "user_title": "Anonymous", "datetimeon": "2011-01-25T20:39:22", "link": "pygame.event.get_grab", "id": 3748}, {"content": "The width for \"ae\" WILL always match the width for \"a\" + \"e\" (which is \"a\" concatinated with\"e\").\nIt will not always match the width of \"a\" plus the width of \"e\".\n(But we knew what you meant.)", "user_title": "Anonymous", "datetimeon": "2011-01-25T21:11:43", "link": "Font.size", "id": 3749}, {"content": "can we get an admin to delete that?", "user_title": "Anonymous", "datetimeon": "2011-01-27T17:25:55", "link": "pygame.draw.circle", "id": 3754}, {"content": "This function seems to me little bit buggy, so I wrote my own:\n\na and b are surfarrays of some surfaces that you want to compare\n\n def comparray(self,a,b):\n c = abs(a.__sub__(b))\n c = c.__ge__(self.tolerance)*255\n surface = pygame.surfarray.make_surface(c)\n return surface", "user_title": "Anonymous", "datetimeon": "2011-01-27T18:49:07", "link": "PixelArray.compare", "id": 3755}, {"content": "your gay", "user_title": "Anonymous", "datetimeon": "2011-01-28T04:28:47", "link": "Rect.colliderect", "id": 3758}, {"content": "It would be nice if the number of Sounds on queue was more than one...\n\nGreg Ruo", "user_title": "Anonymous", "datetimeon": "2011-01-30T21:39:27", "link": "Channel.queue", "id": 3765}, {"content": "I solved my previous question:\n\nIf you need to play in sequence several Sounds in a queue, you can solve this\nwith something like:\n=======================\n i=0\n while (Ch0.get_queue()==None) and (i<10):\n i+=1\n Ch0.queue(f[i])\n============================\n\nwhere Ch0 is your Sound channel created with Ch0=pygame.mixer.Channel(0).\nIn other words, even if the queue allows only one single sound in queue,\nI use the .get_queue method to wait until the queue is free before\n adding the next Sound in the sequence.\n\nIf you have better solutions please reply here. Thanks.\n\nGreg Ruo", "user_title": "Anonymous", "datetimeon": "2011-01-30T22:38:45", "link": "Channel.queue", "id": 3766}, {"content": "Probably would be good to have an OPTIONAL choice to remove them from the queue . . .", "user_title": "Anonymous", "datetimeon": "2011-01-31T13:48:40", "link": "pygame.event.get", "id": 3768}, {"content": "I had this weird thing where blue/red was inversed, but not the other colours, when I was mapping some pixels from one image to a blank surface.\nIt was caused by copying the color integer directly to one pixel to the other, so the trick is to always surface.unmap_rgb(pixel) before setting the color to a new pixel \nThat tricked works.\nIt's the only way unfortunately.", "user_title": "Anonymous", "datetimeon": "2011-02-04T02:33:15", "link": "pygame.PixelArray", "id": 3771}, {"content": "@Dave: Thanks very much for writing that we should ignore the keyword \"width\". This saved me time and my program now works.", "user_title": "Anonymous", "datetimeon": "2011-02-05T08:25:19", "link": "pygame.draw.rect", "id": 3773}, {"content": "ankit sucks", "user_title": "Anonymous", "datetimeon": "2011-02-08T14:07:31", "link": "Rect.colliderect", "id": 3776}, {"content": "i heard ankit really sucks", "user_title": "Anonymous", "datetimeon": "2011-02-08T14:07:46", "link": "Rect.collidepoint", "id": 3777}, {"content": "ankit tandon sucks", "user_title": "Anonymous", "datetimeon": "2011-02-08T14:07:55", "link": "Rect.contains", "id": 3778}, {"content": "I don't get what this does...", "user_title": "Anonymous", "datetimeon": "2011-02-09T22:32:43", "link": "pygame.event.pump", "id": 3779}, {"content": "If you don't use the event queue(why aren't you??) this will keep your program from locking up.", "user_title": "Anonymous", "datetimeon": "2011-02-10T21:20:05", "link": "pygame.event.pump", "id": 3780}, {"content": "When the camera is stopped and you try to access it pygame segfaults.\n(On Debian testing with pygame 1.9.1)", "user_title": "Anonymous", "datetimeon": "2011-02-11T03:31:32", "link": "Camera.stop", "id": 3781}, {"content": "set_allowed removes events from the queue! \nEven if the event in question doesn't belong to the given type.\n\n>>> import pygame\n>>> pygame.init()\n>>> pygame.event.post(pygame.event.Event(pygame.USEREVENT, code=0))\n>>> print pygame.event.peek(pygame.USEREVENT)\n1\n>>> pygame.event.set_allowed(pygame.MOUSEMOTION)\n>>> print pygame.event.peek(pygame.USEREVENT)\n0", "user_title": "Anonymous", "datetimeon": "2011-02-11T17:42:34", "link": "pygame.event.set_allowed", "id": 3782}, {"content": "Thank you very much.\nYour compare function works much better than the original one.", "user_title": "Anonymous", "datetimeon": "2011-02-14T16:59:41", "link": "PixelArray.compare", "id": 3783}, {"content": "meto", "user_title": "Anonymous", "datetimeon": "2011-02-15T19:30:06", "link": "Color.g", "id": 3786}, {"content": "rofl rofl what for a crappy thing you performed? go to wikipedia it works!\n\nu mad!", "user_title": "Anonymous", "datetimeon": "2011-02-16T06:49:19", "link": "pygame.draw.rect", "id": 3787}, {"content": "rofl rofl what for a crappy thing you performed? go to wikipedia it works!\n\nu mad!", "user_title": "Anonymous", "datetimeon": "2011-02-16T06:50:00", "link": "pygame.draw.rect", "id": 3788}, {"content": "stfu\n\nu mad", "user_title": "Anonymous", "datetimeon": "2011-02-16T06:52:07", "link": "Rect.unionall_ip", "id": 3789}, {"content": "This method only queues one music file.\nIf you call it and there already is a queued file, it will be overrided.", "user_title": "Anonymous", "datetimeon": "2011-02-19T12:17:58", "link": "pygame.mixer.music.queue", "id": 3791}, {"content": "elif event.type == pygame.QUIT or event.type == pygame.K_ESCAPE:\n\nthe event type is not pygame.K_ESCAPE. you have to check for a KEYDOWN or KEYUP event and check if it is the key you want, for example:\n\nelif event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):", "user_title": "Anonymous", "datetimeon": "2011-02-21T08:30:17", "link": "pygame.key", "id": 3793}, {"content": "Well, sometimes black and white are swapped, sometimes they aren't.\nEven in the same program, for one mouse cursor it may work right,\nand for another cursor the black & white colors are swapped.\n\nI haven't figured out what triggers this bug.", "user_title": "Anonymous", "datetimeon": "2011-02-24T13:00:58", "link": "pygame.cursors.compile", "id": 3797}, {"content": "spam", "user_title": "Anonymous", "datetimeon": "2011-02-26T15:00:47", "link": "pygame.event.set_grab", "id": 3798}, {"content": "In Pygame 1.9.2 surface objects have sprouted a new method, get_view:\n\nSurface.get_view\nreturn a view of a surface's pixel data.\nSurface.get_view(kind='2'): return\n\nReturn an object which exposes a surface's internal pixel buffer to a NumPy array. For now a custom object with an array struct interface is returned. A Python memoryview may be returned in the future. The buffer is writeable.\n\nThe kind argument is the length 1 string '2', '3', 'r', 'g', 'b', or 'a'. The letters are case insensitive; 'A' will work as well. The argument can be either a Unicode or byte (char) string. The default is '2'.\n\nA kind '2' view is a (surface-width, surface-height) array of raw pixels. The pixels are surface bytesized unsigned integers. The pixel format is surface specific. It is unavailable for 24-bit surfaces.\n\n'3' returns a (surface-width, surface-height, 3) view of RGB color components. Each of the red, green, and blue components are unsigned bytes. Only 24-bit and 32-bit surfaces are supported. The color components must be in either RGB or BGR order within the pixel.\n\n'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a (surface-width, surface-height) view of a single color component within a surface: a color plane. Color components are unsigned bytes. Both 24-bit and 32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with SRCALPHA support 'a'.\n\nThis method implicitly locks the Surface. The lock will be released, once the returned view object is deleted.", "user_title": "Anonymous", "datetimeon": "2011-03-01T14:59:53", "link": "pygame.Surface", "id": 3803}, {"content": "In Pygame 1.9.2 surface objects have sprouted a new method, get_view:\n\nSurface.get_view\nreturn a view of a surface's pixel data.\nSurface.get_view(kind='2'): return\n\nReturn an object which exposes a surface's internal pixel buffer to a NumPy\narray. For now a custom object with an array struct interface is returned.\nA Python memoryview may be returned in the future. The buffer is writeable.\n\nThe kind argument is the length 1 string '2', '3', 'r', 'g', 'b', or 'a'.\nThe letters are case insensitive; 'A' will work as well. The argument can be\neither a Unicode or byte (char) string. The default is '2'.\n\nA kind '2' view is a (surface-width, surface-height) array of raw pixels. The\npixels are surface bytesized unsigned integers. The pixel format is surface\nspecific. It is unavailable for 24-bit surfaces.\n\n'3' returns a (surface-width, surface-height, 3) view of RGB color components.\nEach of the red, green, and blue components are unsigned bytes. Only 24-bit and\n32-bit surfaces are supported. The color components must be in either RGB or\nBGR order within the pixel.\n\n'r' for red, 'g' for green, 'b' for blue, and 'a' for alpha return a\n(surface-width, surface-height) view of a single color component within a\nsurface: a color plane. Color components are unsigned bytes. Both 24-bit and\n32-bit surfaces support 'r', 'g', and 'b'. Only 32-bit surfaces with SRCALPHA\nsupport 'a'.\n\nThis method implicitly locks the Surface. The lock will be released, once the\nreturned view object is deleted.", "user_title": "Anonymous", "datetimeon": "2011-03-01T15:02:04", "link": "pygame.Surface", "id": 3804}, {"content": "for statement with arc", "user_title": "Anonymous", "datetimeon": "2011-03-01T19:54:13", "link": "pygame.draw.arc", "id": 3805}, {"content": "Oh my god I was so thankful that you can adjust the volume, I have no editing software to fix my sounds.", "user_title": "Anonymous", "datetimeon": "2011-03-03T21:26:40", "link": "Sound.set_volume", "id": 3806}, {"content": "Thanks to the comments for wrinting about the event attributes and the key constants list. It should be in any serious documentation.", "user_title": "Anonymous", "datetimeon": "2011-03-09T16:10:36", "link": "pygame.event.Event", "id": 3808}, {"content": "a;rtawkljethlak", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:41:39", "link": "pygame.font", "id": 3809}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:20", "link": "pygame.font.init", "id": 3810}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:22", "link": "pygame.font.init", "id": 3811}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:24", "link": "pygame.font.init", "id": 3812}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:27", "link": "pygame.font.init", "id": 3813}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:29", "link": "pygame.font.init", "id": 3814}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:31", "link": "pygame.font.init", "id": 3815}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:33", "link": "pygame.font.init", "id": 3816}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:36", "link": "pygame.font.init", "id": 3817}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:37", "link": "pygame.font.init", "id": 3818}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:47", "link": "pygame.font.init", "id": 3819}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:53", "link": "pygame.font.init", "id": 3820}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:54", "link": "pygame.font.init", "id": 3821}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:56", "link": "pygame.font.init", "id": 3822}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:42:58", "link": "pygame.font.init", "id": 3823}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:01", "link": "pygame.font.init", "id": 3824}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:03", "link": "pygame.font.init", "id": 3825}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:07", "link": "pygame.font.init", "id": 3826}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:09", "link": "pygame.font.init", "id": 3827}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:11", "link": "pygame.font.init", "id": 3828}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:13", "link": "pygame.font.init", "id": 3829}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:15", "link": "pygame.font.init", "id": 3830}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:16", "link": "pygame.font.init", "id": 3831}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:19", "link": "pygame.font.init", "id": 3832}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:20", "link": "pygame.font.init", "id": 3833}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:22", "link": "pygame.font.init", "id": 3834}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:24", "link": "pygame.font.init", "id": 3835}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:26", "link": "pygame.font.init", "id": 3836}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:28", "link": "pygame.font.init", "id": 3837}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:29", "link": "pygame.font.init", "id": 3838}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:31", "link": "pygame.font.init", "id": 3839}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:33", "link": "pygame.font.init", "id": 3840}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:35", "link": "pygame.font.init", "id": 3841}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:37", "link": "pygame.font.init", "id": 3842}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:39", "link": "pygame.font.init", "id": 3843}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:40", "link": "pygame.font.init", "id": 3844}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:42", "link": "pygame.font.init", "id": 3845}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:45", "link": "pygame.font.init", "id": 3846}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:46", "link": "pygame.font.init", "id": 3847}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:48", "link": "pygame.font.init", "id": 3848}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:50", "link": "pygame.font.init", "id": 3849}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:52", "link": "pygame.font.init", "id": 3850}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:53", "link": "pygame.font.init", "id": 3851}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:55", "link": "pygame.font.init", "id": 3852}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:57", "link": "pygame.font.init", "id": 3853}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:43:59", "link": "pygame.font.init", "id": 3854}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:07", "link": "pygame.font.init", "id": 3855}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:09", "link": "pygame.font.init", "id": 3856}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:11", "link": "pygame.font.init", "id": 3857}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:13", "link": "pygame.font.init", "id": 3858}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:15", "link": "pygame.font.init", "id": 3859}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:18", "link": "pygame.font.init", "id": 3860}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:20", "link": "pygame.font.init", "id": 3861}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:22", "link": "pygame.font.init", "id": 3862}, {"content": "I LIKEY TO SPAM", "user_title": "Anonymous", "datetimeon": "2011-03-10T18:44:24", "link": "pygame.font.init", "id": 3863}, {"content": "Hey guys, how do you detect if user hits enter? There's no event.key for that! Thank you.", "user_title": "Anonymous", "datetimeon": "2011-03-12T11:25:09", "link": "pygame.key", "id": 3864}, {"content": "Could somebody please delete the horrible code in this comment thread? :(", "user_title": "Anonymous", "datetimeon": "2011-03-15T20:16:46", "link": "pygame.draw.circle", "id": 3865}, {"content": "pygame documentation\t || Pygame Home || Help Contents || Reference Index || \n \nCamera || Cdrom || Color || Cursors || Display || Draw || Event || Examples || Font || Gfxdraw || Image || Joystick || Key || Locals || Mask || Midi || Mixer || Mouse || Movie || Music || Overlay || Pixelarray || Pygame || Rect || Scrap || Sndarray || Sprite || Surface || Surfarray || Tests || Time || Transform\nFont.metrics\n\nThe user submitted comments should be used for:\n\nExamples\nHelpful hints, tips, and tricks\nFurther explanation / documentation\nThe user submitted comments should NOT be used for:\n\nBug Reports (see our new Bug Reports link on the side)\nFeature Requests\nQuestions\nPlease note that periodically, the developers may go through the notes and incorporate the information in them into the documentation. This means that any note submitted here becomes the property of Pete Shinners under the LGPL licence.\n\nIf you do not want to leave an anonymous comment, please sign in first.", "user_title": "Anonymous", "datetimeon": "2011-03-21T15:13:24", "link": "Font.metrics", "id": 3868}, {"content": "I can't figure how to crop an image, even after reading this suggestion", "user_title": "Anonymous", "datetimeon": "2011-03-22T11:32:04", "link": "pygame.transform.chop", "id": 3870}, {"content": "I was dumb. Here is how I got it to work. So simple:\n\ncreen.blit(gameboard,(selection.x-18,selection.y-18),(selection.x-18,selection.y-18,96,96))", "user_title": "Anonymous", "datetimeon": "2011-03-22T11:45:29", "link": "pygame.transform.chop", "id": 3871}, {"content": "Perfect, Matthew Brown! Just what I was looking for. I found the reset_stuff() function especially useful and I'm going to use it in our production software.", "user_title": "Anonymous", "datetimeon": "2011-03-24T10:34:18", "link": "pygame.draw.circle", "id": 3873}, {"content": "Wow, thanks a lot Matthew, I am a PyGame newbie and was having some troubles understanding this function without a complete example.", "user_title": "Anonymous", "datetimeon": "2011-03-24T12:23:02", "link": "pygame.draw.circle", "id": 3874}, {"content": "lol", "user_title": "Anonymous", "datetimeon": "2011-03-24T18:06:29", "link": "pygame.draw.circle", "id": 3875}, {"content": "Guys, what Matthew N. Brown did here harms python's reputation.\n\nPython is elegant.\n\nWhat Matthew did was perlify python ... this is terrible...", "user_title": "Anonymous", "datetimeon": "2011-03-24T18:07:11", "link": "pygame.draw.circle", "id": 3876}, {"content": "Just wanted to give my profuse thanks to Matthew N. Brown for his superb usage example of this otherwise vague and esoteric method. I've contacted my boss and we will now integrate this snippet into all of our newly created (and soon-to-be-refactored) legacy code.", "user_title": "Anonymous", "datetimeon": "2011-03-25T20:41:38", "link": "pygame.draw.circle", "id": 3878}, {"content": "where image is a surface, rot and scale are floats\n\n return pygame.transform.smoothscale(image, rot, scale)\nTypeError: argument 2 must be 2-item sequence, not float\n \n return pygame.transform.smoothscale(image,[0,0], scale)\nTypeError: argument 3 must be pygame.Surface, not float", "user_title": "Anonymous", "datetimeon": "2011-03-27T01:37:09", "link": "pygame.transform.rotozoom", "id": 3879}, {"content": "and...\n surf = pygame.surface.Surface((image.get_width()*scale, image.get_height()*scale))\n return pygame.transform.smoothscale(image,[0,0], surf)\n\n pygame.transform.smoothscale(image,[0,0], surf)\nValueError: Destination surface not the given width or height.", "user_title": "Anonymous", "datetimeon": "2011-03-27T01:40:56", "link": "pygame.transform.rotozoom", "id": 3880}, {"content": "look at set_grab first and then you understand", "user_title": "Anonymous", "datetimeon": "2011-03-27T13:55:15", "link": "pygame.event.get_grab", "id": 3881}, {"content": "getting this error :\n in __init__\n self.font = pygame.font.Font(\"None\", 50)\nerror: font not initialized\n\nnot sure why because i rendered the font..", "user_title": "Anonymous", "datetimeon": "2011-04-02T14:35:05", "link": "Font.render", "id": 3882}, {"content": "The enter key is K_RETURN.", "user_title": "Anonymous", "datetimeon": "2011-04-02T17:03:07", "link": "pygame.key", "id": 3883}, {"content": "For this error :\nthere is no soundcard\n\nCall pygame.mixer.init two times :\npygame.mixer.init()\npygame.mixer.init()\n\nOR\n\npygame.mixer.init(); pygame.mixer.init()\n\n(don't omit semicolon)", "user_title": "Anonymous", "datetimeon": "2011-04-05T05:06:41", "link": "pygame.mixer.init", "id": 3886}, {"content": "/love little kids who leave dumb comments like this one", "user_title": "Anonymous", "datetimeon": "2011-04-12T10:48:18", "link": "Rect.colliderect", "id": 3889}, {"content": "there is no information about the supported formats - are those from ModPlugTracker supported? (like .mod, .xm, .s3m, etc.)", "user_title": "Anonymous", "datetimeon": "2011-04-12T12:35:41", "link": "pygame.mixer.music.load", "id": 3890}, {"content": "Yes", "user_title": "Anonymous", "datetimeon": "2011-04-12T16:17:04", "link": "Font.render", "id": 3891}, {"content": "This seems to be broken!\nI call this before playing a movie, but there is no sound.\nPlaying the movie without initializing the mixer in the first place, works!\n\nCan anyone confirm that this is broken?", "user_title": "Anonymous", "datetimeon": "2011-04-18T11:51:33", "link": "pygame.mixer.quit", "id": 3895}, {"content": "For Windows (XP):\nThe problem is that the screen does not get updated automatically, for some reason.\nThe solution is simple. Take a surface with the size of the movie, set this as the display of the movie.\nIn a loop, get the current frame of the movie and if it increases, blit the surface onto the screen and update the screen.", "user_title": "Anonymous", "datetimeon": "2011-04-19T10:47:45", "link": "pygame.movie", "id": 3896}, {"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom pygame import *\nimport pygame, time, numpy, pygame.sndarray\n\nsample_rate = 44100\n\ndef play_for(sample_array, ms, volLeft, volRight):\n sound = pygame.sndarray.make_sound(sample_array)\n beg = time.time()\n channel = sound.play(-1)\n channel.set_volume(volLeft,volRight)\n pygame.time.delay(ms)\n sound.stop()\n end = time.time()\n return beg, end\n \ndef sine_array_onecycle(hz, peak):\n length = sample_rate / float(hz)\n omega = numpy.pi * 2 / length\n xvalues = numpy.arange(int(length)) * omega\n return (peak * numpy.sin(xvalues))\n \ndef sine_array(hz, peak, n_samples = sample_rate):\n return numpy.resize(sine_array_onecycle(hz, peak), (n_samples,))\n \ndef main():\n pygame.mixer.pre_init(sample_rate, -16, 2) # 44.1kHz, 16-bit signed, stereo\n pygame.init()\n f = sine_array(8000, 1)\n f = numpy.array(zip (f , f))\n\n play_for(f , 5000, 0.5, 0.5)\n\nif __name__ == '__main__': main()", "user_title": "Anonymous", "datetimeon": "2011-04-22T12:57:35", "link": "pygame.sndarray.make_sound", "id": 4038}, {"content": "Get Unicode key in Pygame:\n\nfrom pygame import *\npygame.init()\npygame.display.set_mode((500,500),OPENGLBLIT|OPENGL|DOUBLEBUF)\nexitt = 0 \ninte = 4096\nwhile not exitt:\n for event in pygame.event.get():\n if event.type == pygame.QUIT: \n pygame.quit()\n exitt = True\n if event.type == KEYDOWN:\n print event.dict['unicode']", "user_title": "Anonymous", "datetimeon": "2011-04-23T04:11:07", "link": "pygame.key", "id": 4039}, {"content": "The param is \"loops\" not \"loop\".", "user_title": "Anonymous", "datetimeon": "2011-04-27T15:37:38", "link": "Movie.play", "id": 4040}, {"content": "Use it when you load PNG images with transparencies", "user_title": "Anonymous", "datetimeon": "2011-04-30T21:10:05", "link": "Surface.convert_alpha", "id": 4041}, {"content": "The dest argument doesnt work for me, say if I do\n\nscreen.blit(mySurf, dest=(0,0))\n\nor screen.blit(mySurf, dest=(100,100))\n\nI get exactly the same outcome.\n\nWhere am i wrong?", "user_title": "Anonymous", "datetimeon": "2011-05-03T00:45:25", "link": "Surface.blit", "id": 4042}, {"content": "Sorry I realized i was blitting to screen instead of my temporary surface,\nplease ignore (and delete) my comment.", "user_title": "Anonymous", "datetimeon": "2011-05-03T00:46:43", "link": "Surface.blit", "id": 4043}, {"content": "How does that differ from pygame.draw.aalines? This one can also not be filled.", "user_title": "Anonymous", "datetimeon": "2011-05-04T14:58:27", "link": "pygame.gfxdraw.aapolygon", "id": 4044}, {"content": "XBM not supported?", "user_title": "Anonymous", "datetimeon": "2011-05-10T04:11:53", "link": "pygame.image", "id": 4048}, {"content": "I believe it offsets the detection area by (x,y) pixels. So just put (0,0) for no offset", "user_title": "Anonymous", "datetimeon": "2011-05-11T00:48:47", "link": "Mask.draw", "id": 4049}, {"content": "This function seems to need raw strings!", "user_title": "Anonymous", "datetimeon": "2011-05-12T10:26:46", "link": "pygame.image.save", "id": 4050}, {"content": "Is there anyway to play more than 2 songs I have tried everything I want the loaded music to play in order\n#!/usr/bin/env python\nimport pygame\npygame.mixer.init()\npygame.mixer.pre_init(44100, -16, 2, 2048)\npygame.init()\nprint \"hey I finaly got this working!\"\npygame.mixer.music.load('D:/Users/John/Music/Music/FUN.OGG')\npygame.mixer.music.load('D:/Users/John/Music/Music/Still Alive.OGG')\npygame.mixer.music.load('D:/Users/John/Music/Music/turret.OGG')\npygame.mixer.music.load('D:/Users/John/Music/Music/portalend.OGG')\npygame.mixer.music.play()\nimport pysic", "user_title": "Anonymous", "datetimeon": "2011-05-14T16:30:13", "link": "pygame.mixer.music.load", "id": 4053}, {"content": "How is it you play more than one song besides using queue", "user_title": "Anonymous", "datetimeon": "2011-05-14T19:29:16", "link": "pygame.mixer.music.play", "id": 4054}, {"content": "how is it to play a list of songs more than just one without using the queue(which only works once)", "user_title": "Anonymous", "datetimeon": "2011-05-14T19:32:21", "link": "pygame.mixer.music.play", "id": 4055}, {"content": "can the rectangle be filled with an RGBA color so that I can make it transclucent?", "user_title": "Anonymous", "datetimeon": "2011-05-26T09:16:14", "link": "pygame.draw.rect", "id": 4056}, {"content": "Exemple, playing a sinus sound :\n\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom pygame import *\nimport pygame, time, numpy, pygame.sndarray\n\nsample_rate = 44100\n\ndef play_for(sample_array, ms, volLeft, volRight):\n sound = pygame.sndarray.make_sound(sample_array)\n beg = time.time()\n channel = sound.play(-1)\n channel.set_volume(volLeft,volRight)\n pygame.time.delay(ms)\n sound.stop()\n end = time.time()\n return beg, end\n \ndef sine_array_onecycle(hz, peak):\n length = sample_rate / float(hz)\n omega = numpy.pi * 2 / length\n xvalues = numpy.arange(int(length)) * omega\n return (peak * numpy.sin(xvalues))\n \ndef sine_array(hz, peak, n_samples = sample_rate):\n return numpy.resize(sine_array_onecycle(hz, peak), (n_samples,))\n\n \ndef main():\n pygame.mixer.pre_init(sample_rate, -16, 2) # 44.1kHz, 16-bit signed, stereo\n pygame.init()\n f = sine_array(8000, 1)\n f = numpy.array(zip (f , f))\n\n play_for(f , 5000, 0.2, 0.2)\n\nif __name__ == '__main__': main()", "user_title": "Anonymous", "datetimeon": "2011-05-27T02:36:07", "link": "pygame.sndarray", "id": 4057}] \ No newline at end of file diff --git a/venv/Lib/site-packages/pygame/draw.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/draw.cp37-win_amd64.pyd deleted file mode 100644 index e1ef8cbfcfee71b2942a1bad705e6e0eacff3603..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/draw.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/draw_py.py b/venv/Lib/site-packages/pygame/draw_py.py index 3cf4a3d1f3fd2aee565ad27975099c13bd2f905b..33f60923261ba0cc354b9420c64547f0a37de06d 100644 --- a/venv/Lib/site-packages/pygame/draw_py.py +++ b/venv/Lib/site-packages/pygame/draw_py.py @@ -1,84 +1,82 @@ -'''Pygame Drawing algorithms written in Python. (Work in Progress) +"""Pygame Drawing algorithms written in Python. (Work in Progress) Implement Pygame's Drawing Algorithms in a Python version for testing and debugging. -''' -from __future__ import division -import sys +""" -if sys.version_info >= (3, 0, 0): - from math import floor, ceil -else: - # Python2.7 - # FIXME : the import of the builtin math module is broken ... - def floor(x): - int_x = int(x) - return int_x if (x == int_x or x > 0) else int_x - 1 - - def ceil(x): - int_x = int(x) - return int_x if (int_x == x or x < 0) else int_x + 1 +from collections import namedtuple +from math import floor, ceil # H E L P E R F U N C T I O N S # # fractional part of x -def frac(x): - '''return fractional part of x''' - return x - floor(x) -def inv_frac(x): - '''return inverse fractional part of x''' - return 1 - (x - floor(x)) # eg, 1 - frac(x) +def frac(value): + """return fractional part of x""" + return value - floor(value) + + +def inv_frac(value): + """return inverse fractional part of x""" + return 1 - (value - floor(value)) # eg, 1 - frac(x) + + +BoundingBox = namedtuple("BoundingBox", ["left", "top", "right", "bottom"]) +Point = namedtuple("Point", ["x", "y"]) # L O W L E V E L D R A W F U N C T I O N S # # (They are too low-level to be translated into python, right?) -def set_at(surf, x, y, color): - surf.set_at((x, y), color) +def set_at(surf, in_x, in_y, color): + """Set the color of a pixel in a surface""" + surf.set_at((in_x, in_y), color) -def draw_pixel(surf, x, y, color, bright, blend=True): - '''draw one blended pixel with given brightness.''' + +def draw_pixel(surf, pos, color, bright, blend=True): + """draw one blended pixel with given brightness.""" try: - other_col = surf.get_at((x, y)) if blend else (0, 0, 0, 0) + other_col = surf.get_at(pos) if blend else (0, 0, 0, 0) except IndexError: # pixel outside the surface return - new_color = tuple((bright * col + (1 - bright) * pix) - for col, pix in zip(color, other_col)) + new_color = tuple( + (bright * col + (1 - bright) * pix) for col, pix in zip(color, other_col) + ) # FIXME what should happen if only one, color or surf_col, has alpha? - surf.set_at((x, y), new_color) + surf.set_at(pos, new_color) -def _drawhorzline(surf, color, x_from, y, x_to): +def _drawhorzline(surf, color, x_from, in_y, x_to): if x_from == x_to: - surf.set_at((x_from, y), color) + surf.set_at((x_from, in_y), color) return start, end = (x_from, x_to) if x_from <= x_to else (x_to, x_from) - for x in range(start, end + 1): - surf.set_at((x, y), color) + for line_x in range(start, end + 1): + surf.set_at((line_x, in_y), color) -def _drawvertline(surf, color, x, y_from, y_to): +def _drawvertline(surf, color, in_x, y_from, y_to): if y_from == y_to: - surf.set_at((x, y_from), color) + surf.set_at((in_x, y_from), color) return start, end = (y_from, y_to) if y_from <= y_to else (y_to, y_from) - for y in range(start, end + 1): - surf.set_at((x, y), color) + for line_y in range(start, end + 1): + surf.set_at((in_x, line_y), color) # I N T E R N A L D R A W L I N E F U N C T I O N S # -def _clip_and_draw_horzline(surf, color, x_from, y, x_to): - '''draw clipped horizontal line.''' + +def _clip_and_draw_horizline(surf, color, x_from, in_y, x_to): + """draw clipped horizontal line.""" # check Y inside surf clip = surf.get_clip() - if y < clip.y or y >= clip.y + clip.h: + if in_y < clip.y or in_y >= clip.y + clip.h: return x_from = max(x_from, clip.x) @@ -88,15 +86,15 @@ def _clip_and_draw_horzline(surf, color, x_from, y, x_to): if x_to < clip.x or x_from >= clip.x + clip.w: return - _drawhorzline(surf, color, x_from, y, x_to) + _drawhorzline(surf, color, x_from, in_y, x_to) -def _clip_and_draw_vertline(surf, color, x, y_from, y_to): - '''draw clipped vertical line.''' +def _clip_and_draw_vertline(surf, color, in_x, y_from, y_to): + """draw clipped vertical line.""" # check X inside surf clip = surf.get_clip() - if x < clip.x or x >= clip.x + clip.w: + if in_x < clip.x or in_x >= clip.x + clip.w: return y_from = max(y_from, clip.y) @@ -106,31 +104,30 @@ def _clip_and_draw_vertline(surf, color, x, y_from, y_to): if y_to < clip.y or y_from >= clip.y + clip.h: return - _drawvertline(surf, color, x, y_from, y_to) + _drawvertline(surf, color, in_x, y_from, y_to) -# These constans xxx_EDGE are "outside-the-bounding-box"-flags + +# These constants xxx_EDGE are "outside-the-bounding-box"-flags LEFT_EDGE = 0x1 RIGHT_EDGE = 0x2 BOTTOM_EDGE = 0x4 TOP_EDGE = 0x8 -def encode(x, y, left, top, right, bottom): - '''returns a code that defines position with respect to a bounding box''' - # we use the fact that python interprets booleans (the inqualities) - # as 0/1, and then multiply them with the xxx_EDGE flags - return ((x < left) * LEFT_EDGE + - (x > right) * RIGHT_EDGE + - (y < top) * TOP_EDGE + - (y > bottom) * BOTTOM_EDGE) - -INSIDE = lambda a: not a -ACCEPT = lambda a, b: not (a or b) -REJECT = lambda a, b: a and b +def encode(pos, b_box): + """returns a code that defines position with respect to a bounding box""" + # we use the fact that python interprets booleans (the inequalities) + # as 0/1, and then multiply them with the xxx_EDGE flags + return ( + (pos[0] < b_box.left) * LEFT_EDGE + + (pos[0] > b_box.right) * RIGHT_EDGE + + (pos[1] < b_box.top) * TOP_EDGE + + (pos[1] > b_box.bottom) * BOTTOM_EDGE + ) -def clip_line(line, left, top, right, bottom, use_float=False): - '''Algorithm to calculate the clipped line. +def clip_line(line, b_box, use_float=False): + """Algorithm to calculate the clipped line. We calculate the coordinates of the part of the line segment within the bounding box (defined by left, top, right, bottom). The we write @@ -138,105 +135,113 @@ def clip_line(line, left, top, right, bottom, use_float=False): With `use_float` True, clip_line is usable for float-clipping. Returns: true if the line segment cuts the bounding box (false otherwise) - ''' + """ + + def inside(code): + return not code + + def accept(code_a, code_b): + return not (code_a or code_b) + + def reject(code_a, code_b): + return code_a and code_b + assert isinstance(line, list) - x1, y1, x2, y2 = line + x_1, y_1, x_2, y_2 = line dtype = float if use_float else int while True: # the coordinates are progressively modified with the codes, # until they are either rejected or correspond to the final result. - code1 = encode(x1, y1, left, top, right, bottom) - code2 = encode(x2, y2, left, top, right, bottom) + code1 = encode((x_1, y_1), b_box) + code2 = encode((x_2, y_2), b_box) - if ACCEPT(code1, code2): + if accept(code1, code2): # write coordinates into "line" ! - line[:] = x1, y1, x2, y2 + line[:] = x_1, y_1, x_2, y_2 return True - if REJECT(code1, code2): + if reject(code1, code2): return False - # We operate on the (x1, y1) point, and swap if it is inside the bbox: - if INSIDE(code1): - x1, x2 = x2, x1 - y1, y2 = y2, y1 + # We operate on the (x_1, y_1) point, + # and swap if it is inside the bbox: + if inside(code1): + x_1, x_2 = x_2, x_1 + y_1, y_2 = y_2, y_1 code1, code2 = code2, code1 - if (x2 != x1): - m = (y2 - y1) / float(x2 - x1) - else: - m = 1.0 + slope = (y_2 - y_1) / float(x_2 - x_1) if (x_2 != x_1) else 1.0 # Each case, if true, means that we are outside the border: - # calculate x1 and y1 to be the "first point" inside the bbox... + # calculate x_1 and y_1 to be the "first point" inside the bbox... if code1 & LEFT_EDGE: - y1 += dtype((left - x1) * m) - x1 = left + y_1 += dtype((b_box.left - x_1) * slope) + x_1 = b_box.left elif code1 & RIGHT_EDGE: - y1 += dtype((right - x1) * m) - x1 = right + y_1 += dtype((b_box.right - x_1) * slope) + x_1 = b_box.right elif code1 & BOTTOM_EDGE: - if x2 != x1: - x1 += dtype((bottom - y1) / m) - y1 = bottom + if x_2 != x_1: + x_1 += dtype((b_box.bottom - y_1) / slope) + y_1 = b_box.bottom elif code1 & TOP_EDGE: - if x2 != x1: - x1 += dtype((top - y1) / m) - y1 = top + if x_2 != x_1: + x_1 += dtype((b_box.top - y_1) / slope) + y_1 = b_box.top -def _draw_line(surf, color, x1, y1, x2, y2): - '''draw a non-horizontal line (without anti-aliasing).''' +def _draw_line(surf, color, start, end): + """draw a non-horizontal line (without anti-aliasing).""" # Variant of https://en.wikipedia.org/wiki/Bresenham's_line_algorithm # # This strongly differs from craw.c implementation, because we use a # "slope" variable (instead of delta_x and delta_y) and a "error" variable. # And we can not do pointer-arithmetic with "BytesPerPixel", like in # the C-algorithm. - if x1 == x2: + if start.x == end.x: # This case should not happen... raise ValueError - slope = abs((y2 - y1) / (x2 - x1)) + slope = abs((end.y - start.y) / (end.x - start.x)) error = 0.0 if slope < 1: # Here, it's a rather horizontal line # 1. check in which octants we are & set init values - if x2 < x1: - x1, x2 = x2, x1 - y1, y2 = y2, y1 - y = y1 - dy_sign = 1 if (y1 < y2) else -1 + if end.x < start.x: + start.x, end.x = end.x, start.x + start.y, end.y = end.y, start.y + line_y = start.y + dy_sign = 1 if (start.y < end.y) else -1 # 2. step along x coordinate - for x in range(x1, x2 + 1): - set_at(surf, x, y, color) + for line_x in range(start.x, end.x + 1): + set_at(surf, line_x, line_y, color) error += slope if error >= 0.5: - y += dy_sign + line_y += dy_sign error -= 1 else: # Case of a rather vertical line # 1. check in which octants we are & set init values - if y1 > y2: - x1, x2 = x2, x1 - y1, y2 = y2, y1 - x = x1 + if start.y > end.y: + start.x, end.x = end.x, start.x + start.y, end.y = end.y, start.y + line_x = start.x slope = 1 / slope - dx_sign = 1 if (x1 < x2) else -1 + dx_sign = 1 if (start.x < end.x) else -1 # 2. step along y coordinate - for y in range(y1, y2 + 1): - set_at(surf, x, y, color) + for line_y in range(start.y, end.y + 1): + set_at(surf, line_x, line_y, color) error += slope if error >= 0.5: - x += dx_sign + line_x += dx_sign error -= 1 -def _draw_aaline(surf, color, from_x, from_y, to_x, to_y, blend): - '''draw an anti-aliased line. +def _draw_aaline(surf, color, start, end, blend): + """draw an anti-aliased line. The algorithm yields identical results with _draw_line for horizontal, vertical or diagonal lines, and results changes smoothly when changing @@ -244,8 +249,8 @@ def _draw_aaline(surf, color, from_x, from_y, to_x, to_y, blend): Note that this yields strange results for very short lines, eg a line from (0, 0) to (0, 1) will draw 2 pixels, and a line from - (0, 0) to (0, 1.1) will blend 10 % on the pixel (0, 2). - ''' + (0, 0) to (0, 1.1) will blend 10 % on the pixel (0, 2). + """ # The different requirements that we have on an antialiasing algorithm # implies to make some compromises: # 1. We want smooth evolution wrt to the 4 endpoint coordinates @@ -258,113 +263,112 @@ def _draw_aaline(surf, color, from_x, from_y, to_x, to_y, blend): # # This implies to somehow make the line artificially 1 pixel longer # and to draw a full pixel when we have the endpoints are identical. - dx = to_x - from_x - dy = to_y - from_y + d_x = end.x - start.x + d_y = end.y - start.y - if dx == 0 and dy == 0: + if d_x == 0 and d_y == 0: # For smoothness reasons, we could also do some blending here, # but it seems overshoot... - set_at(surf, int(from_x), int(from_y), color) + set_at(surf, int(start.x), int(start.y), color) return - if abs(dx) >= abs(dy): - if from_x > to_x: - from_x, to_x = to_x, from_x - from_y, to_y = to_y, from_y - dx = -dx - dy = -dy - - slope = dy / dx - def draw_two_pixel(x, float_y, factor): - y = floor(float_y) - draw_pixel(surf, x, y, color, factor * inv_frac(float_y), blend) - draw_pixel(surf, x, y + 1, color, factor * frac(float_y), blend) - - # A and G are respectively left and right to the "from" point, but - # with integer-x-coordinate, (and only if from_x is not integer). - # Hence they appear in following order on the line in general case: - # A from-pt G . . . to-pt S - # |------*-------|--- . . . ---|-----*------|- - G_x = ceil(from_x) - G_y = from_y + (G_x - from_x) * slope - - # 1. Draw start of the segment if we have a non-integer-part - if from_x < G_x: - # this corresponds to the point "A" - draw_two_pixel(floor(from_x), G_y - slope, inv_frac(from_x)) - - # 2. Draw end of the segment: we add one pixel for homogenity reasons - rest = frac(to_x) - S_x = ceil(to_x) - if rest > 0: - # Again we draw only if we have a non-integer-part - S_y = from_y + slope * (dx + 1 - rest) - draw_two_pixel(S_x, S_y, rest) - else: - S_x += 1 + if start.x > end.x or start.y > end.y: + start.x, end.x = end.x, start.x + start.y, end.y = end.y, start.y + d_x = -d_x + d_y = -d_y - # 3. loop for other points - for x in range(G_x, S_x): - y = G_y + slope * (x - G_x) - draw_two_pixel(x, y, 1) + if abs(d_x) >= abs(d_y): + slope = d_y / d_x - else: - if from_y > to_y: - from_x, to_x = to_x, from_x - from_y, to_y = to_y, from_y - dx = -dx - dy = -dy - - slope = dx / dy - - def draw_two_pixel(float_x, y, factor): - x = floor(float_x) - draw_pixel(surf, x, y, color, factor * inv_frac(float_x), blend) - draw_pixel(surf, x + 1, y, color, factor * frac(float_x), blend) - - G_y = ceil(from_y) - G_x = from_x + (G_y - from_y) * slope - - # 1. Draw start of the segment - if from_y < G_y: - draw_two_pixel(G_x - slope, floor(from_y), inv_frac(from_y)) - - # 2. Draw end of the segment - rest = frac(to_y) - S_y = ceil(to_y) - if rest > 0: - S_x = from_x + slope * (dy + 1 - rest) - draw_two_pixel(S_x, S_y, rest) - else: - S_y += 1 + def draw_two_pixel(in_x, float_y, factor): + flr_y = floor(float_y) + draw_pixel(surf, (in_x, flr_y), color, factor * inv_frac(float_y), blend) + draw_pixel(surf, (in_x, flr_y + 1), color, factor * frac(float_y), blend) - # 3. loop for other points - for y in range(G_y, S_y): - x = G_x + slope * (y - G_y) - draw_two_pixel(x, y, 1) + _draw_aaline_dx(d_x, slope, end, start, draw_two_pixel) + else: + slope = d_x / d_y + + def draw_two_pixel(float_x, in_y, factor): + fl_x = floor(float_x) + draw_pixel(surf, (fl_x, in_y), color, factor * inv_frac(float_x), blend) + draw_pixel(surf, (fl_x + 1, in_y), color, factor * frac(float_x), blend) + + _draw_aaline_dy(d_y, slope, end, start, draw_two_pixel) + + +def _draw_aaline_dy(d_y, slope, end, start, draw_two_pixel): + g_y = ceil(start.y) + g_x = start.x + (g_y - start.y) * slope + # 1. Draw start of the segment + if start.y < g_y: + draw_two_pixel(g_x - slope, floor(start.y), inv_frac(start.y)) + # 2. Draw end of the segment + rest = frac(end.y) + s_y = ceil(end.y) + if rest > 0: + s_x = start.x + slope * (d_y + 1 - rest) + draw_two_pixel(s_x, s_y, rest) + else: + s_y += 1 + # 3. loop for other points + for line_y in range(g_y, s_y): + line_x = g_x + slope * (line_y - g_y) + draw_two_pixel(line_x, line_y, 1) + + +def _draw_aaline_dx(d_x, slope, end, start, draw_two_pixel): + # A and G are respectively left and right to the "from" point, but + # with integer-x-coordinate, (and only if from_x is not integer). + # Hence they appear in following order on the line in general case: + # A from-pt G . . . to-pt S + # |------*-------|--- . . . ---|-----*------|- + g_x = ceil(start.x) + g_y = start.y + (g_x - start.x) * slope + # 1. Draw start of the segment if we have a non-integer-part + if start.x < g_x: + # this corresponds to the point "A" + draw_two_pixel(floor(start.x), g_y - slope, inv_frac(start.x)) + # 2. Draw end of the segment: we add one pixel for homogeneity reasons + rest = frac(end.x) + s_x = ceil(end.x) + if rest > 0: + # Again we draw only if we have a non-integer-part + s_y = start.y + slope * (d_x + 1 - rest) + draw_two_pixel(s_x, s_y, rest) + else: + s_x += 1 + # 3. loop for other points + for line_x in range(g_x, s_x): + line_y = g_y + slope * (line_x - g_x) + draw_two_pixel(line_x, line_y, 1) # C L I P A N D D R A W L I N E F U N C T I O N S # + def _clip_and_draw_line(surf, rect, color, pts): - '''clip the line into the rectangle and draw if needed. + """clip the line into the rectangle and draw if needed. - Returns true if anything has been drawn, else false.''' + Returns true if anything has been drawn, else false.""" # "pts" is a list with the four coordinates of the two endpoints # of the line to be drawn : pts = x1, y1, x2, y2. # The data format is like that to stay closer to the C-algorithm. - if not clip_line(pts, rect.x, rect.y, rect.x + rect.w - 1, - rect.y + rect.h - 1): + if not clip_line( + pts, BoundingBox(rect.x, rect.y, rect.x + rect.w - 1, rect.y + rect.h - 1) + ): # The line segment defined by "pts" is not crossing the rectangle return 0 - if pts[1] == pts[3]: # eg y1 == y2 + if pts[1] == pts[3]: # eg y1 == y2 _drawhorzline(surf, color, pts[0], pts[1], pts[2]) - elif pts[0] == pts[2]: # eg x1 == x2 + elif pts[0] == pts[2]: # eg x1 == x2 _drawvertline(surf, color, pts[0], pts[1], pts[3]) else: - _draw_line(surf, color, pts[0], pts[1], pts[2], pts[3]) + _draw_line(surf, color, Point(pts[0], pts[1]), Point(pts[2], pts[3])) return 1 + def _clip_and_draw_line_width(surf, rect, color, line, width): yinc = xinc = 0 if abs(line[0] - line[2]) > abs(line[1] - line[3]): @@ -407,52 +411,64 @@ def _clip_and_draw_line_width(surf, rect, color, line, width): def _clip_and_draw_aaline(surf, rect, color, line, blend): - '''draw anti-aliased line between two endpoints.''' - if not clip_line(line, rect.x - 1, rect.y -1, rect.x + rect.w, - rect.y + rect.h, use_float=True): - return # TODO Rect(rect.x, rect.y, 0, 0) - _draw_aaline(surf, color, line[0], line[1], line[2], line[3], blend) - return # TODO Rect(-- affected area --) + """draw anti-aliased line between two endpoints.""" + if not clip_line( + line, + BoundingBox(rect.x - 1, rect.y - 1, rect.x + rect.w, rect.y + rect.h), + use_float=True, + ): + return # TODO Rect(rect.x, rect.y, 0, 0) + _draw_aaline(surf, color, Point(line[0], line[1]), Point(line[2], line[3]), blend) + return # TODO Rect(-- affected area --) # D R A W L I N E F U N C T I O N S # + def draw_aaline(surf, color, from_point, to_point, blend=True): - '''draw anti-aliased line between two endpoints.''' + """draw anti-aliased line between two endpoints.""" line = [from_point[0], from_point[1], to_point[0], to_point[1]] return _clip_and_draw_aaline(surf, surf.get_clip(), color, line, blend) def draw_line(surf, color, from_point, to_point, width=1): - '''draw anti-aliased line between two endpoints.''' + """draw anti-aliased line between two endpoints.""" line = [from_point[0], from_point[1], to_point[0], to_point[1]] return _clip_and_draw_line_width(surf, surf.get_clip(), color, line, width) # M U L T I L I N E F U N C T I O N S # -def _multi_lines(surf, color, closed, points, width=1, blend=False, aaline=False): - '''draw several lines, either anti-aliased or not.''' + +def _multi_lines( + surf, + color, + closed, # pylint: disable=too-many-arguments + points, + width=1, + blend=False, + aaline=False, +): + """draw several lines, either anti-aliased or not.""" # The code for anti-aliased or not is almost identical, so it's factorized - length = len(points) - if length <= 2: + if len(points) <= 2: raise TypeError line = [0] * 4 # store x1, y1 & x2, y2 of the lines to be drawn xlist = [pt[0] for pt in points] ylist = [pt[1] for pt in points] - left = right = line[0] = xlist[0] - top = bottom = line[1] = ylist[0] + line[0] = xlist[0] + line[1] = ylist[0] + b_box = BoundingBox(left=xlist[0], right=xlist[0], top=ylist[0], bottom=ylist[0]) - for x, y in points[1:]: - left = min(left, x) - right = max(right, x) - top = min(top, y) - bottom = max(right, x) + for line_x, line_y in points[1:]: + b_box.left = min(b_box.left, line_x) + b_box.right = max(b_box.right, line_x) + b_box.top = min(b_box.top, line_y) + b_box.bottom = max(b_box.bottom, line_y) rect = surf.get_clip() - for loop in range(1, length): - + for loop in range(1, len(points)): line[0] = xlist[loop - 1] line[1] = ylist[loop - 1] line[2] = xlist[loop] @@ -463,8 +479,8 @@ def _multi_lines(surf, color, closed, points, width=1, blend=False, aaline=False _clip_and_draw_line_width(surf, rect, color, line, width) if closed: - line[0] = xlist[length - 1] - line[1] = ylist[length - 1] + line[0] = xlist[len(points) - 1] + line[1] = ylist[len(points) - 1] line[2] = xlist[0] line[3] = ylist[0] if aaline: @@ -472,19 +488,21 @@ def _multi_lines(surf, color, closed, points, width=1, blend=False, aaline=False else: _clip_and_draw_line_width(surf, rect, color, line, width) - return # TODO Rect(...) + # TODO Rect(...) + def draw_lines(surf, color, closed, points, width=1): - '''draw several lines connected through the points.''' + """draw several lines connected through the points.""" return _multi_lines(surf, color, closed, points, width, aaline=False) def draw_aalines(surf, color, closed, points, blend=True): - '''draw several anti-aliased lines connected through the points.''' + """draw several anti-aliased lines connected through the points.""" return _multi_lines(surf, color, closed, points, blend=blend, aaline=True) def draw_polygon(surface, color, points, width): + """Draw a polygon""" if width: draw_lines(surface, color, 1, points, width) return # TODO Rect(...) @@ -498,42 +516,47 @@ def draw_polygon(surface, color, points, width): if miny == maxy: minx = min(point_x) maxx = max(point_x) - _clip_and_draw_horzline(surface, color, minx, miny, maxx) + _clip_and_draw_horizline(surface, color, minx, miny, maxx) return # TODO Rect(...) - for y in range(miny, maxy + 1): + for y_coord in range(miny, maxy + 1): x_intersect = [] for i in range(num_points): - i_prev = i - 1 if i else num_points - 1 - - y1 = point_y[i_prev] - y2 = point_y[i] - - if y1 < y2: - x1 = point_x[i_prev] - x2 = point_x[i] - elif y1 > y2: - y2 = point_y[i_prev] - y1 = point_y[i] - x2 = point_x[i_prev] - x1 = point_x[i] - else: # special case handled below - continue - - if ( ((y >= y1) and (y < y2)) or ((y == maxy) and (y <= y2))) : - x_sect = (y - y1) * (x2 - x1) // (y2 - y1) + x1 - x_intersect.append(x_sect) + _draw_polygon_inner_loop(i, point_x, point_y, y_coord, x_intersect) x_intersect.sort() for i in range(0, len(x_intersect), 2): - _clip_and_draw_horzline(surface, color, x_intersect[i], y, - x_intersect[i + 1]) + _clip_and_draw_horizline( + surface, color, x_intersect[i], y_coord, x_intersect[i + 1] + ) # special case : horizontal border lines for i in range(num_points): i_prev = i - 1 if i else num_points - 1 - y = point_y[i] - if miny < y == point_y[i_prev] < maxy: - _clip_and_draw_horzline(surface, color, point_x[i], y, point_x[i_prev]) + if miny < point_y[i] == point_y[i_prev] < maxy: + _clip_and_draw_horizline( + surface, color, point_x[i], point_y[i], point_x[i_prev] + ) return # TODO Rect(...) + + +def _draw_polygon_inner_loop(index, point_x, point_y, y_coord, x_intersect): + i_prev = index - 1 if index else len(point_x) - 1 + + y_1 = point_y[i_prev] + y_2 = point_y[index] + + if y_1 < y_2: + x_1 = point_x[i_prev] + x_2 = point_x[index] + elif y_1 > y_2: + y_2 = point_y[i_prev] + y_1 = point_y[index] + x_2 = point_x[i_prev] + x_1 = point_x[index] + else: # special case handled below + return + + if (y_2 > y_coord >= y_1) or ((y_coord == max(point_y)) and (y_coord <= y_2)): + x_intersect.append((y_coord - y_1) * (x_2 - x_1) // (y_2 - y_1) + x_1) diff --git a/venv/Lib/site-packages/pygame/event.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/event.cp37-win_amd64.pyd deleted file mode 100644 index a8fba4427cc07e6730d997fb0e2d410707cae2ea..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/event.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/examples/aacircle.py b/venv/Lib/site-packages/pygame/examples/aacircle.py index a37751ca0fd82cf74d698bd01f1e21004628bd9e..651df18b50e08c459c7d60d66a32969475f4c482 100644 --- a/venv/Lib/site-packages/pygame/examples/aacircle.py +++ b/venv/Lib/site-packages/pygame/examples/aacircle.py @@ -5,31 +5,37 @@ import pygame import pygame.gfxdraw + def main(): pygame.init() - screen = pygame.display.set_mode((500,500)) + screen = pygame.display.set_mode((500, 500)) screen.fill((255, 0, 0)) s = pygame.Surface(screen.get_size(), pygame.SRCALPHA, 32) - pygame.draw.line(s, (0,0,0), (250, 250), (250+200,250)) + pygame.draw.line(s, (0, 0, 0), (250, 250), (250 + 200, 250)) width = 1 for a_radius in range(width): radius = 200 - pygame.gfxdraw.aacircle(s, 250, 250, radius-a_radius, (0, 0, 0)) + pygame.gfxdraw.aacircle(s, 250, 250, radius - a_radius, (0, 0, 0)) screen.blit(s, (0, 0)) + + pygame.draw.circle(screen, "green", (50, 100), 10) + pygame.draw.circle(screen, "black", (50, 100), 10, 1) + pygame.display.flip() try: - while 1: + while True: event = pygame.event.wait() if event.type == pygame.QUIT: break if event.type == pygame.KEYDOWN: - if event.key == pygame.K_ESCAPE or event.unicode == 'q': + if event.key == pygame.K_ESCAPE or event.unicode == "q": break pygame.display.flip() finally: pygame.quit() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/aliens.py b/venv/Lib/site-packages/pygame/examples/aliens.py index f320588d7f15198671b8b469d73b4d4b1cb80d31..e0f5ed6a874d50f0beb5385c1bf42191f7f824e9 100644 --- a/venv/Lib/site-packages/pygame/examples/aliens.py +++ b/venv/Lib/site-packages/pygame/examples/aliens.py @@ -1,73 +1,92 @@ #!/usr/bin/env python +""" pygame.examples.aliens -import random, os.path +Shows a mini game where you have to defend against aliens. -#import basic pygame modules -import pygame -from pygame.locals import * +What does it show you about pygame? -#see if we can load more than standard BMP -if not pygame.image.get_extended(): +* pg.sprite, the difference between Sprite and Group. +* dirty rectangle optimization for processing for speed. +* music with pg.mixer.music, including fadeout +* sound effects with pg.Sound +* event processing, keyboard handling, QUIT handling. +* a main loop frame limited with a game clock from pg.time.Clock +* fullscreen switching. + + +Controls +-------- + +* Left and right arrows to move. +* Space bar to shoot +* f key to toggle between fullscreen. + +""" + +import os +import random +from typing import List + +# import basic pygame modules +import pygame as pg + +# see if we can load more than standard BMP +if not pg.image.get_extended(): raise SystemExit("Sorry, extended image module required") -#game constants -MAX_SHOTS = 2 #most player bullets onscreen -ALIEN_ODDS = 22 #chances a new alien appears -BOMB_ODDS = 60 #chances a new bomb will drop -ALIEN_RELOAD = 12 #frames between new aliens -SCREENRECT = Rect(0, 0, 640, 480) -SCORE = 0 +# game constants +MAX_SHOTS = 2 # most player bullets onscreen +ALIEN_ODDS = 22 # chances a new alien appears +BOMB_ODDS = 60 # chances a new bomb will drop +ALIEN_RELOAD = 12 # frames between new aliens +SCREENRECT = pg.Rect(0, 0, 640, 480) +SCORE = 0 main_dir = os.path.split(os.path.abspath(__file__))[0] + def load_image(file): - "loads an image, prepares it for play" - file = os.path.join(main_dir, 'data', file) + """loads an image, prepares it for play""" + file = os.path.join(main_dir, "data", file) try: - surface = pygame.image.load(file) - except pygame.error: - raise SystemExit('Could not load image "%s" %s'%(file, pygame.get_error())) + surface = pg.image.load(file) + except pg.error: + raise SystemExit(f'Could not load image "{file}" {pg.get_error()}') return surface.convert() -def load_images(*files): - imgs = [] - for file in files: - imgs.append(load_image(file)) - return imgs - - -class dummysound: - def play(self): pass def load_sound(file): - if not pygame.mixer: return dummysound() - file = os.path.join(main_dir, 'data', file) + """because pygame can be compiled without mixer.""" + if not pg.mixer: + return None + file = os.path.join(main_dir, "data", file) try: - sound = pygame.mixer.Sound(file) + sound = pg.mixer.Sound(file) return sound - except pygame.error: - print ('Warning, unable to load, %s' % file) - return dummysound() + except pg.error: + print(f"Warning, unable to load, {file}") + return None +# Each type of game object gets an init and an update function. +# The update function is called once per frame, and it is when each object should +# change its current position and state. +# +# The Player object actually gets a "move" function instead of update, +# since it is passed extra information about the keyboard. -# each type of game object gets an init and an -# update function. the update function is called -# once per frame, and it is when each object should -# change it's current position and state. the Player -# object actually gets a "move" function instead of -# update, since it is passed extra information about -# the keyboard +class Player(pg.sprite.Sprite): + """Representing the player as a moon buggy type car.""" -class Player(pygame.sprite.Sprite): speed = 10 bounce = 24 gun_offset = -11 - images = [] - def __init__(self): - pygame.sprite.Sprite.__init__(self, self.containers) + images: List[pg.Surface] = [] + + def __init__(self, *groups): + pg.sprite.Sprite.__init__(self, *groups) self.image = self.images[0] self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom) self.reloading = 0 @@ -75,29 +94,33 @@ class Player(pygame.sprite.Sprite): self.facing = -1 def move(self, direction): - if direction: self.facing = direction - self.rect.move_ip(direction*self.speed, 0) + if direction: + self.facing = direction + self.rect.move_ip(direction * self.speed, 0) self.rect = self.rect.clamp(SCREENRECT) if direction < 0: self.image = self.images[0] elif direction > 0: self.image = self.images[1] - self.rect.top = self.origtop - (self.rect.left//self.bounce%2) + self.rect.top = self.origtop - (self.rect.left // self.bounce % 2) def gunpos(self): - pos = self.facing*self.gun_offset + self.rect.centerx + pos = self.facing * self.gun_offset + self.rect.centerx return pos, self.rect.top -class Alien(pygame.sprite.Sprite): +class Alien(pg.sprite.Sprite): + """An alien space ship. That slowly moves down the screen.""" + speed = 13 animcycle = 12 - images = [] - def __init__(self): - pygame.sprite.Sprite.__init__(self, self.containers) + images: List[pg.Surface] = [] + + def __init__(self, *groups): + pg.sprite.Sprite.__init__(self, *groups) self.image = self.images[0] self.rect = self.image.get_rect() - self.facing = random.choice((-1,1)) * Alien.speed + self.facing = random.choice((-1, 1)) * Alien.speed self.frame = 0 if self.facing < 0: self.rect.right = SCREENRECT.right @@ -105,246 +128,268 @@ class Alien(pygame.sprite.Sprite): def update(self): self.rect.move_ip(self.facing, 0) if not SCREENRECT.contains(self.rect): - self.facing = -self.facing; + self.facing = -self.facing self.rect.top = self.rect.bottom + 1 self.rect = self.rect.clamp(SCREENRECT) self.frame = self.frame + 1 - self.image = self.images[self.frame//self.animcycle%3] + self.image = self.images[self.frame // self.animcycle % 3] + +class Explosion(pg.sprite.Sprite): + """An explosion. Hopefully the Alien and not the player!""" -class Explosion(pygame.sprite.Sprite): defaultlife = 12 animcycle = 3 - images = [] - def __init__(self, actor): - pygame.sprite.Sprite.__init__(self, self.containers) + images: List[pg.Surface] = [] + + def __init__(self, actor, *groups): + pg.sprite.Sprite.__init__(self, *groups) self.image = self.images[0] self.rect = self.image.get_rect(center=actor.rect.center) self.life = self.defaultlife def update(self): + """called every time around the game loop. + + Show the explosion surface for 'defaultlife'. + Every game tick(update), we decrease the 'life'. + + Also we animate the explosion. + """ self.life = self.life - 1 - self.image = self.images[self.life//self.animcycle%2] - if self.life <= 0: self.kill() + self.image = self.images[self.life // self.animcycle % 2] + if self.life <= 0: + self.kill() -class Shot(pygame.sprite.Sprite): +class Shot(pg.sprite.Sprite): + """a bullet the Player sprite fires.""" + speed = -11 - images = [] - def __init__(self, pos): - pygame.sprite.Sprite.__init__(self, self.containers) + images: List[pg.Surface] = [] + + def __init__(self, pos, *groups): + pg.sprite.Sprite.__init__(self, *groups) self.image = self.images[0] self.rect = self.image.get_rect(midbottom=pos) def update(self): + """called every time around the game loop. + + Every tick we move the shot upwards. + """ self.rect.move_ip(0, self.speed) if self.rect.top <= 0: self.kill() -class Bomb(pygame.sprite.Sprite): +class Bomb(pg.sprite.Sprite): + """A bomb the aliens drop.""" + speed = 9 - images = [] - def __init__(self, alien): - pygame.sprite.Sprite.__init__(self, self.containers) + images: List[pg.Surface] = [] + + def __init__(self, alien, explosion_group, *groups): + pg.sprite.Sprite.__init__(self, *groups) self.image = self.images[0] - self.rect = self.image.get_rect(midbottom= - alien.rect.move(0,5).midbottom) + self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom) + self.explosion_group = explosion_group def update(self): + """called every time around the game loop. + + Every frame we move the sprite 'rect' down. + When it reaches the bottom we: + + - make an explosion. + - remove the Bomb. + """ self.rect.move_ip(0, self.speed) if self.rect.bottom >= 470: - Explosion(self) + Explosion(self, self.explosion_group) self.kill() -class Score(pygame.sprite.Sprite): - def __init__(self): - pygame.sprite.Sprite.__init__(self) - self.font = pygame.font.Font(None, 20) +class Score(pg.sprite.Sprite): + """to keep track of the score.""" + + def __init__(self, *groups): + pg.sprite.Sprite.__init__(self, *groups) + self.font = pg.font.Font(None, 20) self.font.set_italic(1) - self.color = Color('white') + self.color = "white" self.lastscore = -1 self.update() self.rect = self.image.get_rect().move(10, 450) def update(self): + """We only update the score in update() when it has changed.""" if SCORE != self.lastscore: self.lastscore = SCORE - msg = "Score: %d" % SCORE + msg = f"Score: {SCORE}" self.image = self.font.render(msg, 0, self.color) - -def main(winstyle = 0): +def main(winstyle=0): # Initialize pygame - if pygame.get_sdl_version()[0] == 2: - pygame.mixer.pre_init(44100, 32, 2, 1024) - pygame.init() - if pygame.mixer and not pygame.mixer.get_init(): - print ('Warning, no sound') - pygame.mixer = None + if pg.get_sdl_version()[0] == 2: + pg.mixer.pre_init(44100, 32, 2, 1024) + pg.init() + if pg.mixer and not pg.mixer.get_init(): + print("Warning, no sound") + pg.mixer = None fullscreen = False # Set the display mode winstyle = 0 # |FULLSCREEN - bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32) - screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth) - - #Load images, assign to sprite classes - #(do this before the classes are used, after screen setup) - img = load_image('player1.gif') - Player.images = [img, pygame.transform.flip(img, 1, 0)] - img = load_image('explosion1.gif') - Explosion.images = [img, pygame.transform.flip(img, 1, 1)] - Alien.images = load_images('alien1.gif', 'alien2.gif', 'alien3.gif') - Bomb.images = [load_image('bomb.gif')] - Shot.images = [load_image('shot.gif')] - - #decorate the game window - icon = pygame.transform.scale(Alien.images[0], (32, 32)) - pygame.display.set_icon(icon) - pygame.display.set_caption('Pygame Aliens') - pygame.mouse.set_visible(0) - - #create the background, tile the bgd image - bgdtile = load_image('background.gif') - background = pygame.Surface(SCREENRECT.size) + bestdepth = pg.display.mode_ok(SCREENRECT.size, winstyle, 32) + screen = pg.display.set_mode(SCREENRECT.size, winstyle, bestdepth) + + # Load images, assign to sprite classes + # (do this before the classes are used, after screen setup) + img = load_image("player1.gif") + Player.images = [img, pg.transform.flip(img, 1, 0)] + img = load_image("explosion1.gif") + Explosion.images = [img, pg.transform.flip(img, 1, 1)] + Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")] + Bomb.images = [load_image("bomb.gif")] + Shot.images = [load_image("shot.gif")] + + # decorate the game window + icon = pg.transform.scale(Alien.images[0], (32, 32)) + pg.display.set_icon(icon) + pg.display.set_caption("Pygame Aliens") + pg.mouse.set_visible(0) + + # create the background, tile the bgd image + bgdtile = load_image("background.gif") + background = pg.Surface(SCREENRECT.size) for x in range(0, SCREENRECT.width, bgdtile.get_width()): background.blit(bgdtile, (x, 0)) - screen.blit(background, (0,0)) - pygame.display.flip() + screen.blit(background, (0, 0)) + pg.display.flip() - #load the sound effects - boom_sound = load_sound('boom.wav') - shoot_sound = load_sound('car_door.wav') - if pygame.mixer: - music = os.path.join(main_dir, 'data', 'house_lo.wav') - pygame.mixer.music.load(music) - pygame.mixer.music.play(-1) + # load the sound effects + boom_sound = load_sound("boom.wav") + shoot_sound = load_sound("car_door.wav") + if pg.mixer: + music = os.path.join(main_dir, "data", "house_lo.wav") + pg.mixer.music.load(music) + pg.mixer.music.play(-1) # Initialize Game Groups - aliens = pygame.sprite.Group() - shots = pygame.sprite.Group() - bombs = pygame.sprite.Group() - all = pygame.sprite.RenderUpdates() - lastalien = pygame.sprite.GroupSingle() - - #assign default groups to each sprite class - Player.containers = all - Alien.containers = aliens, all, lastalien - Shot.containers = shots, all - Bomb.containers = bombs, all - Explosion.containers = all - Score.containers = all - - #Create Some Starting Values - global score + aliens = pg.sprite.Group() + shots = pg.sprite.Group() + bombs = pg.sprite.Group() + all = pg.sprite.RenderUpdates() + lastalien = pg.sprite.GroupSingle() + + # Create Some Starting Values alienreload = ALIEN_RELOAD - kills = 0 - clock = pygame.time.Clock() + clock = pg.time.Clock() - #initialize our starting sprites + # initialize our starting sprites global SCORE - player = Player() - Alien() #note, this 'lives' because it goes into a sprite group - if pygame.font: - all.add(Score()) - - + player = Player(all) + Alien( + aliens, all, lastalien + ) # note, this 'lives' because it goes into a sprite group + if pg.font: + all.add(Score(all)) + + # Run our main loop whilst the player is alive. while player.alive(): - - #get input - for event in pygame.event.get(): - if event.type == QUIT or \ - (event.type == KEYDOWN and event.key == K_ESCAPE): - return - elif event.type == KEYDOWN: - if event.key == pygame.K_f: + # get input + for event in pg.event.get(): + if event.type == pg.QUIT: + return + if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: + return + if event.type == pg.KEYDOWN: + if event.key == pg.K_f: if not fullscreen: print("Changing to FULLSCREEN") screen_backup = screen.copy() - screen = pygame.display.set_mode( - SCREENRECT.size, - winstyle | FULLSCREEN, - bestdepth + screen = pg.display.set_mode( + SCREENRECT.size, winstyle | pg.FULLSCREEN, bestdepth ) screen.blit(screen_backup, (0, 0)) else: print("Changing to windowed mode") screen_backup = screen.copy() - screen = pygame.display.set_mode( - SCREENRECT.size, - winstyle, - bestdepth + screen = pg.display.set_mode( + SCREENRECT.size, winstyle, bestdepth ) screen.blit(screen_backup, (0, 0)) - # screen.fill((255, 0, 0)) - pygame.display.flip() + pg.display.flip() fullscreen = not fullscreen - - keystate = pygame.key.get_pressed() + keystate = pg.key.get_pressed() # clear/erase the last drawn sprites all.clear(screen, background) - #update all the sprites + # update all the sprites all.update() - #handle player input - direction = keystate[K_RIGHT] - keystate[K_LEFT] + # handle player input + direction = keystate[pg.K_RIGHT] - keystate[pg.K_LEFT] player.move(direction) - firing = keystate[K_SPACE] + firing = keystate[pg.K_SPACE] if not player.reloading and firing and len(shots) < MAX_SHOTS: - Shot(player.gunpos()) - shoot_sound.play() + Shot(player.gunpos(), shots, all) + if pg.mixer and shoot_sound is not None: + shoot_sound.play() player.reloading = firing # Create new alien if alienreload: alienreload = alienreload - 1 elif not int(random.random() * ALIEN_ODDS): - Alien() + Alien(aliens, all, lastalien) alienreload = ALIEN_RELOAD # Drop bombs if lastalien and not int(random.random() * BOMB_ODDS): - Bomb(lastalien.sprite) - - # Detect collisions - for alien in pygame.sprite.spritecollide(player, aliens, 1): - boom_sound.play() - Explosion(alien) - Explosion(player) + Bomb(lastalien.sprite, all, bombs, all) + + # Detect collisions between aliens and players. + for alien in pg.sprite.spritecollide(player, aliens, 1): + if pg.mixer and boom_sound is not None: + boom_sound.play() + Explosion(alien, all) + Explosion(player, all) SCORE = SCORE + 1 player.kill() - for alien in pygame.sprite.groupcollide(shots, aliens, 1, 1).keys(): - boom_sound.play() - Explosion(alien) + # See if shots hit the aliens. + for alien in pg.sprite.groupcollide(aliens, shots, 1, 1).keys(): + if pg.mixer and boom_sound is not None: + boom_sound.play() + Explosion(alien, all) SCORE = SCORE + 1 - for bomb in pygame.sprite.spritecollide(player, bombs, 1): - boom_sound.play() - Explosion(player) - Explosion(bomb) + # See if alien bombs hit the player. + for bomb in pg.sprite.spritecollide(player, bombs, 1): + if pg.mixer and boom_sound is not None: + boom_sound.play() + Explosion(player, all) + Explosion(bomb, all) player.kill() - #draw the scene + # draw the scene dirty = all.draw(screen) - pygame.display.update(dirty) + pg.display.update(dirty) - #cap the framerate + # cap the framerate at 40fps. Also called 40HZ or 40 times per second. clock.tick(40) - if pygame.mixer: - pygame.mixer.music.fadeout(1000) - pygame.time.wait(1000) - pygame.quit() - - + if pg.mixer: + pg.mixer.music.fadeout(1000) + pg.time.wait(1000) -#call the "main" function if running this script -if __name__ == '__main__': main() +# call the "main" function if running this script +if __name__ == "__main__": + main() + pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/arraydemo.py b/venv/Lib/site-packages/pygame/examples/arraydemo.py index 9341db6b3a1b74320813d594d1d4e7b2789cafd2..514722bfcfafd632d7bb2cf812f06336fd4af112 100644 --- a/venv/Lib/site-packages/pygame/examples/arraydemo.py +++ b/venv/Lib/site-packages/pygame/examples/arraydemo.py @@ -1,131 +1,120 @@ #!/usr/bin/env python +""" pygame.examples.arraydemo + +Welcome to the arraydemo! + +Use the numpy array package to manipulate pixels. + +This demo will show you a few things: + +* scale up, scale down, flip, +* cross fade +* soften +* put stripes on it! + +""" import os -import pygame +import pygame as pg from pygame import surfarray -from pygame.locals import * main_dir = os.path.split(os.path.abspath(__file__))[0] + def surfdemo_show(array_img, name): "displays a surface, waits for user to continue" - screen = pygame.display.set_mode(array_img.shape[:2], 0, 32) + screen = pg.display.set_mode(array_img.shape[:2], 0, 32) surfarray.blit_array(screen, array_img) - pygame.display.flip() - pygame.display.set_caption(name) - while 1: - e = pygame.event.wait() - if e.type == MOUSEBUTTONDOWN: break - elif e.type == KEYDOWN and e.key == K_s: - #pygame.image.save(screen, name+'.bmp') - #s = pygame.Surface(screen.get_size(), 0, 32) - #s = s.convert_alpha() - #s.fill((0,0,0,255)) - #s.blit(screen, (0,0)) - #s.fill((222,0,0,50), (0,0,40,40)) - #pygame.image.save_extended(s, name+'.png') - #pygame.image.save(s, name+'.png') - #pygame.image.save(screen, name+'_screen.png') - #pygame.image.save(s, name+'.tga') - pygame.image.save(screen, name+'.png') - elif e.type == QUIT: + pg.display.flip() + pg.display.set_caption(name) + while True: + e = pg.event.wait() + # Force application to only advance when main button is released + if e.type == pg.MOUSEBUTTONUP and e.button == pg.BUTTON_LEFT: + break + elif e.type == pg.KEYDOWN and e.key == pg.K_s: + pg.image.save(screen, name + ".png") + elif e.type == pg.QUIT: + pg.quit() raise SystemExit() -def main(arraytype=None): - """show various surfarray effects - - If arraytype is provided then use that array package. Valid - values are 'numeric' or 'numpy'. Otherwise default to NumPy, - or fall back on Numeric if NumPy is not installed. - - """ - if arraytype not in ('numpy', None): - raise ValueError('Array type not supported: %r' % arraytype) - import numpy as N - from numpy import int32, uint8, uint +def main(): + """show various surfarray effects""" + import numpy as np + from numpy import int32, uint - pygame.init() - print ('Using %s' % surfarray.get_arraytype().capitalize()) - print ('Press the mouse button to advance image.') - print ('Press the "s" key to save the current image.') + pg.init() - #allblack - allblack = N.zeros((128, 128), int32) - surfdemo_show(allblack, 'allblack') + print("Using Numpy") + print("Press the left mouse button to advance image.") + print('Press the "s" key to save the current image.') + # allblack + allblack = np.zeros((128, 128), int32) + surfdemo_show(allblack, "allblack") - #striped - #the element type is required for N.zeros in NumPy else - #an array of float is returned. - striped = N.zeros((128, 128, 3), int32) + # striped + # the element type is required for np.zeros in numpy else + # an array of float is returned. + striped = np.zeros((128, 128, 3), int32) striped[:] = (255, 0, 0) - striped[:,::3] = (0, 255, 255) - surfdemo_show(striped, 'striped') + striped[:, ::3] = (0, 255, 255) + surfdemo_show(striped, "striped") - - #rgbarray - imagename = os.path.join(main_dir, 'data', 'arraydemo.bmp') - imgsurface = pygame.image.load(imagename) + # rgbarray + imagename = os.path.join(main_dir, "data", "arraydemo.bmp") + imgsurface = pg.image.load(imagename) rgbarray = surfarray.array3d(imgsurface) - surfdemo_show(rgbarray, 'rgbarray') - - - #flipped - flipped = rgbarray[:,::-1] - surfdemo_show(flipped, 'flipped') - + surfdemo_show(rgbarray, "rgbarray") - #scaledown - scaledown = rgbarray[::2,::2] - surfdemo_show(scaledown, 'scaledown') + # flipped + flipped = rgbarray[:, ::-1] + surfdemo_show(flipped, "flipped") + # scaledown + scaledown = rgbarray[::2, ::2] + surfdemo_show(scaledown, "scaledown") - #scaleup - #the element type is required for N.zeros in NumPy else - #an #array of floats is returned. + # scaleup + # the element type is required for np.zeros in numpy else + # an #array of floats is returned. shape = rgbarray.shape - scaleup = N.zeros((shape[0]*2, shape[1]*2, shape[2]), int32) - scaleup[::2,::2,:] = rgbarray - scaleup[1::2,::2,:] = rgbarray - scaleup[:,1::2] = scaleup[:,::2] - surfdemo_show(scaleup, 'scaleup') - - - #redimg - redimg = N.array(rgbarray) - redimg[:,:,1:] = 0 - surfdemo_show(redimg, 'redimg') - - - #soften - #having factor as an array forces integer upgrade during multiplication - #of rgbarray, even for numpy. - factor = N.array((8,), int32) - soften = N.array(rgbarray, int32) - soften[1:,:] += rgbarray[:-1,:] * factor - soften[:-1,:] += rgbarray[1:,:] * factor - soften[:,1:] += rgbarray[:,:-1] * factor - soften[:,:-1] += rgbarray[:,1:] * factor + scaleup = np.zeros((shape[0] * 2, shape[1] * 2, shape[2]), int32) + scaleup[::2, ::2, :] = rgbarray + scaleup[1::2, ::2, :] = rgbarray + scaleup[:, 1::2] = scaleup[:, ::2] + surfdemo_show(scaleup, "scaleup") + + # redimg + redimg = np.array(rgbarray) + redimg[:, :, 1:] = 0 + surfdemo_show(redimg, "redimg") + + # soften + # having factor as an array forces integer upgrade during multiplication + # of rgbarray, even for numpy. + factor = np.array((8,), int32) + soften = np.array(rgbarray, int32) + soften[1:, :] += rgbarray[:-1, :] * factor + soften[:-1, :] += rgbarray[1:, :] * factor + soften[:, 1:] += rgbarray[:, :-1] * factor + soften[:, :-1] += rgbarray[:, 1:] * factor soften //= 33 - surfdemo_show(soften, 'soften') + surfdemo_show(soften, "soften") - - #crossfade (50%) - src = N.array(rgbarray) - dest = N.zeros(rgbarray.shape) # dest is float64 by default. + # crossfade (50%) + src = np.array(rgbarray) + dest = np.zeros(rgbarray.shape) # dest is float64 by default. dest[:] = 20, 50, 100 diff = (dest - src) * 0.50 xfade = src + diff.astype(uint) - surfdemo_show(xfade, 'xfade') + surfdemo_show(xfade, "xfade") + # all done + pg.quit() - #alldone - pygame.quit() -if __name__ == '__main__': +if __name__ == "__main__": main() - - - diff --git a/venv/Lib/site-packages/pygame/examples/audiocapture.py b/venv/Lib/site-packages/pygame/examples/audiocapture.py index c140e51a2d1f6653b4ad1a22b9fd9f8b8ffa8da4..714a8aa6fb1cf27ea2cfed31755dda236f5ecfb0 100644 --- a/venv/Lib/site-packages/pygame/examples/audiocapture.py +++ b/venv/Lib/site-packages/pygame/examples/audiocapture.py @@ -1,30 +1,36 @@ +#!/usr/bin/env python +""" pygame.examples.audiocapture + +A pygame 2 experiment. + +* record sound from a microphone +* play back the recorded sound +""" import pygame as pg import time -if pg.get_sdl_version()[0] < 2: - raise SystemExit('This example requires pygame 2 and SDL2.') - from pygame._sdl2 import ( - get_audio_device_name, - get_num_audio_devices, + get_audio_device_names, AudioDevice, AUDIO_F32, - AUDIO_ALLOW_FORMAT_CHANGE + AUDIO_ALLOW_FORMAT_CHANGE, ) +from pygame._sdl2.mixer import set_post_mix + pg.mixer.pre_init(44100, 32, 2, 512) pg.init() # init_subsystem(INIT_AUDIO) -names = [get_audio_device_name(x, 1) for x in range(get_num_audio_devices(1))] +names = get_audio_device_names(True) print(names) -iscapture = 1 sounds = [] sound_chunks = [] + def callback(audiodevice, audiomemoryview): - """ This is called in the sound thread. + """This is called in the sound thread. Note, that the frequency and such you request may not be what you get. """ @@ -33,9 +39,20 @@ def callback(audiodevice, audiomemoryview): sound_chunks.append(bytes(audiomemoryview)) +def postmix_callback(postmix, audiomemoryview): + """This is called in the sound thread. + + At the end of mixing we get this data. + """ + print(type(audiomemoryview), len(audiomemoryview)) + print(postmix) + + +set_post_mix(postmix_callback) + audio = AudioDevice( devicename=names[0], - iscapture=1, + iscapture=True, frequency=44100, audioformat=AUDIO_F32, numchannels=2, @@ -46,13 +63,16 @@ audio = AudioDevice( # start recording. audio.pause(0) -print('recording with :%s:' % names[0]) +print(audio) + +print(f"recording with '{names[0]}'") time.sleep(5) -print('Turning data into a pygame.mixer.Sound') -sound = pg.mixer.Sound(buffer=b''.join(sound_chunks)) +print("Turning data into a pg.mixer.Sound") +sound = pg.mixer.Sound(buffer=b"".join(sound_chunks)) -print('playing back recorded sound') +print("playing back recorded sound") sound.play() time.sleep(5) +pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/blend_fill.py b/venv/Lib/site-packages/pygame/examples/blend_fill.py index 822fa82661f4b39d29b65b4dd4e205b4f11908a8..473da0405db3c8dfd608e09d6be0f74ccd7a9eb8 100644 --- a/venv/Lib/site-packages/pygame/examples/blend_fill.py +++ b/venv/Lib/site-packages/pygame/examples/blend_fill.py @@ -1,17 +1,30 @@ #!/usr/bin/env python +""" pygame.examples.blend_fill + +BLEND_ing colors in different ways with Surface.fill(). + +Keyboard Controls: + +* Press R, G, B to increase the color channel values, +* 1-9 to set the step range for the increment, +* A - ADD, S- SUB, M- MULT, - MIN, + MAX to change the blend modes + +""" import os -import pygame -from pygame.locals import * +import pygame as pg +from pygame import K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9 -def usage (): - print ("Press R, G, B to increase the color channel values,") - print ("1-9 to set the step range for the increment,") - print ("A - ADD, S- SUB, M- MULT, - MIN, + MAX") - print (" to change the blend modes") + +def usage(): + print("Press R, G, B to increase the color channel values,") + print("1-9 to set the step range for the increment,") + print("A - ADD, S- SUB, M- MULT, - MIN, + MAX") + print(" to change the blend modes") main_dir = os.path.split(os.path.abspath(__file__))[0] -data_dir = os.path.join(main_dir, 'data') +data_dir = os.path.join(main_dir, "data") + def main(): color = [0, 0, 0] @@ -19,84 +32,83 @@ def main(): blendtype = 0 step = 5 - pygame.init () - screen = pygame.display.set_mode ((640, 480), 0, 32) - screen.fill ((100, 100, 100)) + pg.init() + screen = pg.display.set_mode((640, 480), 0, 32) + screen.fill((100, 100, 100)) - image = pygame.image.load (os.path.join (data_dir, "liquid.bmp")).convert() - blendimage = pygame.image.load (os.path.join (data_dir, "liquid.bmp")).convert() - screen.blit (image, (10, 10)) - screen.blit (blendimage, (200, 10)) + image = pg.image.load(os.path.join(data_dir, "liquid.bmp")).convert() + blendimage = pg.image.load(os.path.join(data_dir, "liquid.bmp")).convert() + screen.blit(image, (10, 10)) + screen.blit(blendimage, (200, 10)) - pygame.display.flip () - pygame.key.set_repeat (500, 30) + pg.display.flip() + pg.key.set_repeat(500, 30) usage() going = True while going: - for event in pygame.event.get (): - if event.type == QUIT: + for event in pg.event.get(): + if event.type == pg.QUIT: going = False - if event.type == KEYDOWN: - usage () + if event.type == pg.KEYDOWN: + usage() - if event.key == K_ESCAPE: + if event.key == pg.K_ESCAPE: going = False - if event.key == K_r: + if event.key == pg.K_r: color[0] += step if color[0] > 255: color[0] = 0 changed = True - elif event.key == K_g: + elif event.key == pg.K_g: color[1] += step if color[1] > 255: color[1] = 0 changed = True - elif event.key == K_b: + elif event.key == pg.K_b: color[2] += step if color[2] > 255: color[2] = 0 changed = True - elif event.key == K_a: - blendtype = BLEND_ADD + elif event.key == pg.K_a: + blendtype = pg.BLEND_ADD changed = True - elif event.key == K_s: - blendtype = BLEND_SUB + elif event.key == pg.K_s: + blendtype = pg.BLEND_SUB changed = True - elif event.key == K_m: - blendtype = BLEND_MULT + elif event.key == pg.K_m: + blendtype = pg.BLEND_MULT changed = True - elif event.key == K_PLUS: - blendtype = BLEND_MAX + elif event.key == pg.K_PLUS: + blendtype = pg.BLEND_MAX changed = True - elif event.key == K_MINUS: - blendtype = BLEND_MIN + elif event.key == pg.K_MINUS: + blendtype = pg.BLEND_MIN changed = True elif event.key in (K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9): - step = int (event.unicode) + step = int(event.unicode) if changed: - screen.fill ((100, 100, 100)) - screen.blit (image, (10, 10)) - blendimage.blit (image, (0, 0)) - #blendimage.fill (color, (0, 0, 20, 20), blendtype) - blendimage.fill (color, None, blendtype) - screen.blit (blendimage, (200, 10)) - print ("Color: %s, Pixel (0,0): %s" % - (tuple(color), - [blendimage.get_at ((0, 0))])) + screen.fill((100, 100, 100)) + screen.blit(image, (10, 10)) + blendimage.blit(image, (0, 0)) + # blendimage.fill (color, (0, 0, 20, 20), blendtype) + blendimage.fill(color, None, blendtype) + screen.blit(blendimage, (200, 10)) + print( + f"Color: {tuple(color)}, Pixel (0,0): {[blendimage.get_at((0, 0))]}" + ) changed = False - pygame.display.flip () - + pg.display.flip() - pygame.quit() + pg.quit() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/blit_blends.py b/venv/Lib/site-packages/pygame/examples/blit_blends.py index 33fe214ba6bd306f1d6e2f103a57baced11de154..e5f64a2c60c404f2dee157345720b221bb418548 100644 --- a/venv/Lib/site-packages/pygame/examples/blit_blends.py +++ b/venv/Lib/site-packages/pygame/examples/blit_blends.py @@ -1,125 +1,132 @@ #!/usr/bin/env python +""" pygame.examples.blit_blends -# fake additive blending. Using NumPy. it doesn't clamp. -# press r,g,b +Blending colors in different ways with different blend modes. -import os, pygame -from pygame.locals import * +It also shows some tricks with the surfarray. +Including how to do additive blending. + + +Keyboard Controls +----------------- + +* R, G, B - add a bit of Red, Green, or Blue. +* A - Add blend mode +* S - Subtractive blend mode +* M - Multiply blend mode +* = key BLEND_MAX blend mode. +* - key BLEND_MIN blend mode. +* 1, 2, 3, 4 - use different images. + +""" +import os +import pygame as pg +import time + +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") try: import pygame.surfarray import numpy -except: - print ("no surfarray for you! install numpy") +except ImportError: + print("no surfarray for you! install numpy") -import time - -main_dir = os.path.split(os.path.abspath(__file__))[0] -data_dir = os.path.join(main_dir, 'data') def main(): - pygame.init() - pygame.mixer.quit() # remove ALSA underflow messages for Debian squeeze - screen = pygame.display.set_mode((640, 480)) + pg.init() + pg.mixer.quit() # remove ALSA underflow messages for Debian squeeze + screen = pg.display.set_mode((640, 480)) - im1= pygame.Surface(screen.get_size()) - #im1= im1.convert() + im1 = pg.Surface(screen.get_size()) + # im1= im1.convert() im1.fill((100, 0, 0)) - - - im2= pygame.Surface(screen.get_size()) + im2 = pg.Surface(screen.get_size()) im2.fill((0, 50, 0)) # we make a srcalpha copy of it. - #im3= im2.convert(SRCALPHA) + # im3= im2.convert(SRCALPHA) im3 = im2 im3.set_alpha(127) images = {} - images[K_1] = im2 - images[K_2] = pygame.image.load(os.path.join(data_dir, "chimp.bmp")) - images[K_3] = pygame.image.load(os.path.join(data_dir, "alien3.gif")) - images[K_4] = pygame.image.load(os.path.join(data_dir, "liquid.bmp")) + images[pg.K_1] = im2 + images[pg.K_2] = pg.image.load(os.path.join(data_dir, "chimp.png")) + images[pg.K_3] = pg.image.load(os.path.join(data_dir, "alien3.gif")) + images[pg.K_4] = pg.image.load(os.path.join(data_dir, "liquid.bmp")) img_to_blit = im2.convert() iaa = img_to_blit.convert_alpha() - - blits = {} - blits[K_a] = BLEND_ADD - blits[K_s] = BLEND_SUB - blits[K_m] = BLEND_MULT - blits[K_EQUALS] = BLEND_MAX - blits[K_MINUS] = BLEND_MIN + blits[pg.K_a] = pg.BLEND_ADD + blits[pg.K_s] = pg.BLEND_SUB + blits[pg.K_m] = pg.BLEND_MULT + blits[pg.K_EQUALS] = pg.BLEND_MAX + blits[pg.K_MINUS] = pg.BLEND_MIN blitsn = {} - blitsn[K_a] = "BLEND_ADD" - blitsn[K_s] = "BLEND_SUB" - blitsn[K_m] = "BLEND_MULT" - blitsn[K_EQUALS] = "BLEND_MAX" - blitsn[K_MINUS] = "BLEND_MIN" - + blitsn[pg.K_a] = "BLEND_ADD" + blitsn[pg.K_s] = "BLEND_SUB" + blitsn[pg.K_m] = "BLEND_MULT" + blitsn[pg.K_EQUALS] = "BLEND_MAX" + blitsn[pg.K_MINUS] = "BLEND_MIN" screen.blit(im1, (0, 0)) - pygame.display.flip() - clock = pygame.time.Clock() - print ("one pixel is:%s:" % [im1.get_at((0,0))]) + pg.display.flip() + clock = pg.time.Clock() + print("one pixel is:%s:" % [im1.get_at((0, 0))]) going = True while going: clock.tick(60) - for event in pygame.event.get(): - if event.type == QUIT: + for event in pg.event.get(): + if event.type == pg.QUIT: going = False - if event.type == KEYDOWN: + if event.type == pg.KEYDOWN: usage() - if event.type == KEYDOWN and event.key == K_ESCAPE: + if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: going = False - elif event.type == KEYDOWN and event.key in images.keys(): + elif event.type == pg.KEYDOWN and event.key in images.keys(): img_to_blit = images[event.key] iaa = img_to_blit.convert_alpha() - elif event.type == KEYDOWN and event.key in blits.keys(): + elif event.type == pg.KEYDOWN and event.key in blits.keys(): t1 = time.time() # blits is a dict keyed with key -> blit flag. eg BLEND_ADD. - im1.blit(img_to_blit, (0,0), None, blits[event.key]) + im1.blit(img_to_blit, (0, 0), None, blits[event.key]) t2 = time.time() - print ("one pixel is:%s:" % [im1.get_at((0,0))]) - print ("time to do:%s:" % (t2-t1)) - - - elif event.type == KEYDOWN and event.key in [K_t]: + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") + elif event.type == pg.KEYDOWN and event.key in [pg.K_t]: for bkey in blits.keys(): t1 = time.time() for x in range(300): - im1.blit(img_to_blit, (0,0), None, blits[bkey]) + im1.blit(img_to_blit, (0, 0), None, blits[bkey]) t2 = time.time() # show which key we're doing... onedoing = blitsn[bkey] - print ("time to do :%s: is :%s:" % (onedoing, t2-t1)) + print(f"time to do :{onedoing}: is :{t2 - t1}:") - - elif event.type == KEYDOWN and event.key in [K_o]: + elif event.type == pg.KEYDOWN and event.key in [pg.K_o]: t1 = time.time() # blits is a dict keyed with key -> blit flag. eg BLEND_ADD. - im1.blit(iaa, (0,0)) + im1.blit(iaa, (0, 0)) t2 = time.time() - print ("one pixel is:%s:" % [im1.get_at((0,0))]) - print ("time to do:%s:" % (t2-t1)) - + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") - elif event.type == KEYDOWN and event.key == K_SPACE: + elif event.type == pg.KEYDOWN and event.key == pg.K_SPACE: # this additive blend without clamp two surfaces. - #im1.set_alpha(127) - #im1.blit(im1, (0,0)) - #im1.set_alpha(255) + # im1.set_alpha(127) + # im1.blit(im1, (0,0)) + # im1.set_alpha(255) t1 = time.time() im1p = pygame.surfarray.pixels2d(im1) @@ -128,10 +135,10 @@ def main(): del im1p del im2p t2 = time.time() - print ("one pixel is:%s:" % [im1.get_at((0,0))]) - print ("time to do:%s:" % (t2-t1)) + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") - elif event.type == KEYDOWN and event.key in [K_z]: + elif event.type == pg.KEYDOWN and event.key in [pg.K_z]: t1 = time.time() im1p = pygame.surfarray.pixels3d(im1) im2p = pygame.surfarray.pixels3d(im2) @@ -144,50 +151,47 @@ def main(): del im1p del im2p t2 = time.time() - print ("one pixel is:%s:" % [im1.get_at((0,0))]) - print ("time to do:%s:" % (t2-t1)) + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") - elif event.type == KEYDOWN and event.key in [K_r, K_g, K_b]: + elif event.type == pg.KEYDOWN and event.key in [pg.K_r, pg.K_g, pg.K_b]: # this adds one to each pixel. - colmap={} - colmap[K_r] = 0x10000 - colmap[K_g] = 0x00100 - colmap[K_b] = 0x00001 + colmap = {} + colmap[pg.K_r] = 0x10000 + colmap[pg.K_g] = 0x00100 + colmap[pg.K_b] = 0x00001 im1p = pygame.surfarray.pixels2d(im1) im1p += colmap[event.key] del im1p - print ("one pixel is:%s:" % [im1.get_at((0,0))]) - - elif event.type == KEYDOWN and event.key == K_p: - print ("one pixel is:%s:" % [im1.get_at((0,0))]) + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + elif event.type == pg.KEYDOWN and event.key == pg.K_p: + print("one pixel is:%s:" % [im1.get_at((0, 0))]) - - - - elif event.type == KEYDOWN and event.key == K_f: + elif event.type == pg.KEYDOWN and event.key == pg.K_f: # this additive blend without clamp two surfaces. t1 = time.time() im1.set_alpha(127) - im1.blit(im2, (0,0)) + im1.blit(im2, (0, 0)) im1.set_alpha(255) t2 = time.time() - print ("one pixel is:%s:" % [im1.get_at((0,0))]) - print ("time to do:%s:" % (t2-t1)) - + print("one pixel is:%s:" % [im1.get_at((0, 0))]) + print(f"time to do:{t2 - t1}:") screen.blit(im1, (0, 0)) - pygame.display.flip() + pg.display.flip() + + pg.quit() - pygame.quit() def usage(): - print ("press keys 1-5 to change image to blit.") - print ("A - ADD, S- SUB, M- MULT, - MIN, + MAX") - print ("T - timing test for special blend modes.") + print("press keys 1-5 to change image to blit.") + print("A - ADD, S- SUB, M- MULT, - MIN, + MAX") + print("T - timing test for special blend modes.") + -if __name__ == '__main__': +if __name__ == "__main__": usage() main() diff --git a/venv/Lib/site-packages/pygame/examples/camera.py b/venv/Lib/site-packages/pygame/examples/camera.py index 9967d81df880f8449cf26da11119aa836ba5efff..cfb91d2b2fb81991af7a15527256bfa47b957307 100644 --- a/venv/Lib/site-packages/pygame/examples/camera.py +++ b/venv/Lib/site-packages/pygame/examples/camera.py @@ -1,32 +1,40 @@ #!/usr/bin/env python +""" pygame.examples.camera -# 1. Basic image capturing and displaying using the camera module +Basic image capturing and display using pygame.camera -import pygame +Keyboard controls +----------------- + +- 0, start camera 0. +- 1, start camera 1. +- 9, start camera 9. +- 10, start camera... wait a minute! There's not 10 key! +""" +import pygame as pg import pygame.camera -from pygame.locals import * -class VideoCapturePlayer(object): +class VideoCapturePlayer: + size = (640, 480) - size = ( 640, 480 ) def __init__(self, **argd): self.__dict__.update(**argd) - super(VideoCapturePlayer, self).__init__(**argd) + super().__init__(**argd) # create a display surface. standard pygame stuff - self.display = pygame.display.set_mode( self.size, 0 ) + self.display = pg.display.set_mode(self.size) self.init_cams(0) def init_cams(self, which_cam_idx): - # gets a list of available cameras. self.clist = pygame.camera.list_cameras() - print (self.clist) + # ensure a camera exists if not self.clist: raise ValueError("Sorry, no cameras detected.") + # check to see if the camera id exists. If not, default to the first one in the list. try: cam_id = self.clist[which_cam_idx] except IndexError: @@ -38,55 +46,76 @@ class VideoCapturePlayer(object): # starts the camera self.camera.start() - self.clock = pygame.time.Clock() + self.clock = pg.time.Clock() # create a surface to capture to. for performance purposes, you want the # bit depth to be the same as that of the display surface. - self.snapshot = pygame.surface.Surface(self.size, 0, self.display) + self.snapshot = pg.surface.Surface(self.size, 0, self.display) + # return the name of the camera being used, to be included in the window name + return cam_id def get_and_flip(self): # if you don't want to tie the framerate to the camera, you can check and # see if the camera has an image ready. note that while this works # on most cameras, some will never return true. - if 0 and self.camera.query_image(): - # capture an image - self.snapshot = self.camera.get_image(self.snapshot) + self.snapshot = self.camera.get_image(self.display) + + # if 0 and self.camera.query_image(): + # # capture an image - if 0: - self.snapshot = self.camera.get_image(self.snapshot) - #self.snapshot = self.camera.get_image() + # self.snapshot = self.camera.get_image(self.snapshot) - # blit it to the display surface. simple! - self.display.blit(self.snapshot, (0,0)) - else: - self.snapshot = self.camera.get_image(self.display) - #self.display.blit(self.snapshot, (0,0)) + # if 0: + # self.snapshot = self.camera.get_image(self.snapshot) + # # self.snapshot = self.camera.get_image() + # # blit it to the display surface. simple! + # self.display.blit(self.snapshot, (0, 0)) + # else: - pygame.display.flip() + # self.snapshot = self.camera.get_image(self.display) + # # self.display.blit(self.snapshot, (0,0)) + + pg.display.flip() def main(self): + # get the camera list. If there are no cameras, raise a value error. + clist = pygame.camera.list_cameras() + if not clist: + raise ValueError("Sorry, no cameras detected.") + # get the first camera, as this is the default. We want the display to contain the name of the camera. + camera = clist[0] + + # create a list of options for the user to easily understand. + print( + "\nPress the associated number for the desired camera to see that display!" + ) + print("(Selecting a camera that does not exist will default to camera 0)") + for index, cam in enumerate(clist): + print(f"[{index}]: {cam}") + going = True while going: - events = pygame.event.get() + events = pg.event.get() for e in events: - if e.type == QUIT or (e.type == KEYDOWN and e.key == K_ESCAPE): + if e.type == pg.QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE): going = False - if e.type == KEYDOWN: - if e.key in range(K_0, K_0+10) : - self.init_cams(e.key - K_0) - + if e.type == pg.KEYDOWN: + if e.key in range(pg.K_0, pg.K_0 + 10): + camera = self.init_cams(e.key - pg.K_0) self.get_and_flip() self.clock.tick() - print (self.clock.get_fps()) + pygame.display.set_caption(f"{camera} ({self.clock.get_fps():.2f} FPS)") + def main(): - pygame.init() + pg.init() pygame.camera.init() VideoCapturePlayer().main() - pygame.quit() + pg.quit() + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/chimp.py b/venv/Lib/site-packages/pygame/examples/chimp.py index 2e703cb863a9a62cc4cedb5b9dc8d406a74e3ea9..b3373ecd6ea99d7877c80614f0072c2659543559 100644 --- a/venv/Lib/site-packages/pygame/examples/chimp.py +++ b/venv/Lib/site-packages/pygame/examples/chimp.py @@ -1,5 +1,6 @@ #!/usr/bin/env python -""" +""" pygame.examples.chimp + This simple example is used for the line-by-line tutorial that comes with pygame. It is based on a 'popular' web banner. Note there are comments here, but for the full explanation, @@ -8,85 +9,91 @@ follow along in the tutorial. # Import Modules -import os, pygame -from pygame.locals import * -from pygame.compat import geterror +import os +import pygame as pg -if not pygame.font: print('Warning, fonts disabled') -if not pygame.mixer: print('Warning, sound disabled') +if not pg.font: + print("Warning, fonts disabled") +if not pg.mixer: + print("Warning, sound disabled") main_dir = os.path.split(os.path.abspath(__file__))[0] -data_dir = os.path.join(main_dir, 'data') +data_dir = os.path.join(main_dir, "data") # functions to create our resources -def load_image(name, colorkey=None): +def load_image(name, colorkey=None, scale=1): fullname = os.path.join(data_dir, name) - try: - image = pygame.image.load(fullname) - except pygame.error: - print('Cannot load image:', fullname) - raise SystemExit(str(geterror())) + image = pg.image.load(fullname) image = image.convert() + + size = image.get_size() + size = (size[0] * scale, size[1] * scale) + image = pg.transform.scale(image, size) + if colorkey is not None: - if colorkey is -1: + if colorkey == -1: colorkey = image.get_at((0, 0)) - image.set_colorkey(colorkey, RLEACCEL) + image.set_colorkey(colorkey, pg.RLEACCEL) return image, image.get_rect() def load_sound(name): class NoneSound: - def play(self): pass - if not pygame.mixer or not pygame.mixer.get_init(): + def play(self): + pass + + if not pg.mixer or not pg.mixer.get_init(): return NoneSound() + fullname = os.path.join(data_dir, name) - try: - sound = pygame.mixer.Sound(fullname) - except pygame.error: - print('Cannot load sound: %s' % fullname) - raise SystemExit(str(geterror())) + sound = pg.mixer.Sound(fullname) + return sound # classes for our game objects -class Fist(pygame.sprite.Sprite): +class Fist(pg.sprite.Sprite): """moves a clenched fist on the screen, following the mouse""" + def __init__(self): - pygame.sprite.Sprite.__init__(self) #call Sprite initializer - self.image, self.rect = load_image('fist.bmp', -1) - self.punching = 0 + pg.sprite.Sprite.__init__(self) # call Sprite initializer + self.image, self.rect = load_image("fist.png", -1) + self.fist_offset = (-235, -80) + self.punching = False def update(self): """move the fist based on the mouse position""" - pos = pygame.mouse.get_pos() - self.rect.midtop = pos + pos = pg.mouse.get_pos() + self.rect.topleft = pos + self.rect.move_ip(self.fist_offset) if self.punching: - self.rect.move_ip(5, 10) + self.rect.move_ip(15, 25) def punch(self, target): """returns true if the fist collides with the target""" if not self.punching: - self.punching = 1 + self.punching = True hitbox = self.rect.inflate(-5, -5) return hitbox.colliderect(target.rect) def unpunch(self): """called to pull the fist back""" - self.punching = 0 + self.punching = False -class Chimp(pygame.sprite.Sprite): +class Chimp(pg.sprite.Sprite): """moves a monkey critter across the screen. it can spin the - monkey when it is punched.""" + monkey when it is punched.""" + def __init__(self): - pygame.sprite.Sprite.__init__(self) # call Sprite intializer - self.image, self.rect = load_image('chimp.bmp', -1) - screen = pygame.display.get_surface() + pg.sprite.Sprite.__init__(self) # call Sprite initializer + self.image, self.rect = load_image("chimp.png", -1, 4) + screen = pg.display.get_surface() self.area = screen.get_rect() - self.rect.topleft = 10, 10 - self.move = 9 - self.dizzy = 0 + self.rect.topleft = 10, 90 + self.move = 18 + self.dizzy = False def update(self): """walk or spin, depending on the monkeys state""" @@ -99,65 +106,64 @@ class Chimp(pygame.sprite.Sprite): """move the monkey across the screen, and turn at the ends""" newpos = self.rect.move((self.move, 0)) if not self.area.contains(newpos): - if self.rect.left < self.area.left or \ - self.rect.right > self.area.right: + if self.rect.left < self.area.left or self.rect.right > self.area.right: self.move = -self.move newpos = self.rect.move((self.move, 0)) - self.image = pygame.transform.flip(self.image, 1, 0) - self.rect = newpos + self.image = pg.transform.flip(self.image, True, False) + self.rect = newpos def _spin(self): """spin the monkey image""" center = self.rect.center self.dizzy = self.dizzy + 12 if self.dizzy >= 360: - self.dizzy = 0 + self.dizzy = False self.image = self.original else: - rotate = pygame.transform.rotate + rotate = pg.transform.rotate self.image = rotate(self.original, self.dizzy) self.rect = self.image.get_rect(center=center) def punched(self): """this will cause the monkey to start spinning""" if not self.dizzy: - self.dizzy = 1 + self.dizzy = True self.original = self.image def main(): """this function is called when the program starts. - it initializes everything it needs, then runs in - a loop until the function returns.""" + it initializes everything it needs, then runs in + a loop until the function returns.""" # Initialize Everything - pygame.init() - screen = pygame.display.set_mode((468, 60)) - pygame.display.set_caption('Monkey Fever') - pygame.mouse.set_visible(0) + pg.init() + screen = pg.display.set_mode((1280, 480), pg.SCALED) + pg.display.set_caption("Monkey Fever") + pg.mouse.set_visible(False) - # Create The Backgound - background = pygame.Surface(screen.get_size()) + # Create The Background + background = pg.Surface(screen.get_size()) background = background.convert() - background.fill((250, 250, 250)) + background.fill((170, 238, 187)) # Put Text On The Background, Centered - if pygame.font: - font = pygame.font.Font(None, 36) - text = font.render("Pummel The Chimp, And Win $$$", 1, (10, 10, 10)) - textpos = text.get_rect(centerx=background.get_width()/2) + if pg.font: + font = pg.font.Font(None, 64) + text = font.render("Pummel The Chimp, And Win $$$", True, (10, 10, 10)) + textpos = text.get_rect(centerx=background.get_width() / 2, y=10) background.blit(text, textpos) # Display The Background screen.blit(background, (0, 0)) - pygame.display.flip() + pg.display.flip() # Prepare Game Objects - clock = pygame.time.Clock() - whiff_sound = load_sound('whiff.wav') - punch_sound = load_sound('punch.wav') + whiff_sound = load_sound("whiff.wav") + punch_sound = load_sound("punch.wav") chimp = Chimp() fist = Fist() - allsprites = pygame.sprite.RenderPlain((fist, chimp)) + allsprites = pg.sprite.RenderPlain((chimp, fist)) + clock = pg.time.Clock() # Main Loop going = True @@ -165,18 +171,18 @@ def main(): clock.tick(60) # Handle Input Events - for event in pygame.event.get(): - if event.type == QUIT: + for event in pg.event.get(): + if event.type == pg.QUIT: going = False - elif event.type == KEYDOWN and event.key == K_ESCAPE: + elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: going = False - elif event.type == MOUSEBUTTONDOWN: + elif event.type == pg.MOUSEBUTTONDOWN: if fist.punch(chimp): punch_sound.play() # punch chimp.punched() else: whiff_sound.play() # miss - elif event.type == MOUSEBUTTONUP: + elif event.type == pg.MOUSEBUTTONUP: fist.unpunch() allsprites.update() @@ -184,13 +190,14 @@ def main(): # Draw Everything screen.blit(background, (0, 0)) allsprites.draw(screen) - pygame.display.flip() + pg.display.flip() + + pg.quit() - pygame.quit() # Game Over # this calls the 'main' function when this script is executed -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/cursors.py b/venv/Lib/site-packages/pygame/examples/cursors.py index 2bcc9e4606521d53d19333d0af3d22b2b688672b..5778c5bb9f479b04fc6e10030d4ccd2cdd890bb5 100644 --- a/venv/Lib/site-packages/pygame/examples/cursors.py +++ b/venv/Lib/site-packages/pygame/examples/cursors.py @@ -1,99 +1,259 @@ #!/usr/bin/env python +""" pygame.examples.cursors +Click a button and the cursor will change. +This example will show you: +*The different types of cursors that exist +*How to create a cursor +*How to set a cursor +*How to make a simple button +""" -import pygame - - -arrow = ( "xX ", - "X.X ", - "X..X ", - "X...X ", - "X....X ", - "X.....X ", - "X......X ", - "X.......X ", - "X........X ", - "X.........X ", - "X......XXXXX ", - "X...X..X ", - "X..XX..X ", - "X.X XX..X ", - "XX X..X ", - "X X..X ", - " X..X ", - " X..X ", - " X..X ", - " XX ", - " ", - " ", - " ", - " ") - - -no = (" ", - " ", - " XXXXXX ", - " XX......XX ", - " X..........X ", - " X....XXXX....X ", - " X...XX XX...X ", - " X.....X X...X ", - " X..X...X X..X ", - " X...XX...X X...X ", - " X..X X...X X..X ", - " X..X X...X X..X ", - " X..X X.,.X X..X ", - " X..X X...X X..X ", - " X...X X...XX...X ", - " X..X X...X..X ", - " X...X X.....X ", - " X...XX X...X ", - " X....XXXXX...X ", - " X..........X ", - " XX......XX ", - " XXXXXX ", - " ", - " ", - ) +import pygame as pg +import os + + +# Create a system cursor + +system_cursor1 = pg.SYSTEM_CURSOR_CROSSHAIR +system_cursor2 = pg.SYSTEM_CURSOR_HAND +system_cursor3 = pg.SYSTEM_CURSOR_IBEAM + + +# Create a color cursor + +surf = pg.Surface((40, 40)) +surf.fill((120, 50, 50)) +color_cursor = pg.cursors.Cursor((20, 20), surf) + + +# Create a color cursor with an image surface + +main_dir = os.path.split(os.path.abspath(__file__))[0] +image_name = os.path.join(main_dir, "data", "cursor.png") +image = pg.image.load(image_name) +image_cursor = pg.cursors.Cursor( + (image.get_width() // 2, image.get_height() // 2), image +) + + +# Create a bitmap cursor from simple strings + +# sized 24x24 +thickarrow_strings = ( + "XX ", + "XXX ", + "XXXX ", + "XX.XX ", + "XX..XX ", + "XX...XX ", + "XX....XX ", + "XX.....XX ", + "XX......XX ", + "XX.......XX ", + "XX........XX ", + "XX........XXX ", + "XX......XXXXX ", + "XX.XXX..XX ", + "XXXX XX..XX ", + "XX XX..XX ", + " XX..XX ", + " XX..XX ", + " XX..XX ", + " XXXX ", + " XX ", + " ", + " ", + " ", +) + +bitmap_cursor1 = pg.cursors.Cursor( + (24, 24), + (0, 0), + *pg.cursors.compile(thickarrow_strings, black="X", white=".", xor="o"), +) + + +# Create a bitmap cursor from premade simple strings + +bitmap_cursor2 = pg.cursors.diamond + + +# Calculate if mouse position is inside circle +def check_circle(mouse_pos_x, mouse_pos_y, center_x, center_y, radius): + return (mouse_pos_x - center_x) ** 2 + (mouse_pos_y - center_y) ** 2 < radius**2 -def TestCursor(arrow): - hotspot = None - for y in range(len(arrow)): - for x in range(len(arrow[y])): - if arrow[y][x] in ['x', ',', 'O']: - hotspot = x,y - break - if hotspot != None: - break - if hotspot == None: - raise Exception("No hotspot specified for cursor '%s'!" % -cursorname) - s2 = [] - for line in arrow: - s2.append(line.replace('x', 'X').replace(',', '.').replace('O', -'o')) - cursor, mask = pygame.cursors.compile(s2, 'X', '.', 'o') - size = len(arrow[0]), len(arrow) - pygame.mouse.set_cursor(size, hotspot, cursor, mask) def main(): - pygame.init() - pygame.font.init() - font = pygame.font.Font(None, 24) - bg = pygame.display.set_mode((800, 600), 0, 24) - bg.fill((255,255,255)) - bg.blit(font.render("Click to advance", 1, (0, 0, 0)), (0, 0)) - pygame.display.update() - for cursor in [no, arrow]: - TestCursor(cursor) - going = True - while going: - pygame.event.pump() - for e in pygame.event.get(): - if e.type == pygame.MOUSEBUTTONDOWN: - going = False - pygame.quit() - - -if __name__ == '__main__': - main() + pg.init() + pg.display.set_caption("Cursors Example") + + pg.font.init() + font = pg.font.Font(None, 30) + font1 = pg.font.Font(None, 24) + + bg = pg.display.set_mode((500, 400)) + bg.fill((183, 201, 226)) + + # Initialize circles + radius1 = 40 + radius2 = 40 + radius3 = 40 + radius4 = 40 + radius5 = 40 + radius6 = 40 + radius7 = 40 + + pos_x1 = 82 + pos_x2 = 138 + pos_x3 = 194 + pos_x4 = 250 + pos_x5 = 306 + pos_x6 = 362 + pos_x7 = 418 + + pos_y1 = 140 + pos_y2 = 220 + pos_y3 = 140 + pos_y4 = 220 + pos_y5 = 140 + pos_y6 = 220 + pos_y7 = 140 + + circle1 = pg.draw.circle(bg, (255, 255, 255), (pos_x1, pos_y1), radius1) + circle2 = pg.draw.circle(bg, (255, 255, 255), (pos_x2, pos_y2), radius2) + circle3 = pg.draw.circle(bg, (255, 255, 255), (pos_x3, pos_y3), radius3) + circle4 = pg.draw.circle(bg, (255, 255, 255), (pos_x4, pos_y4), radius4) + circle5 = pg.draw.circle(bg, (255, 255, 255), (pos_x5, pos_y5), radius5) + circle6 = pg.draw.circle(bg, (255, 255, 255), (pos_x6, pos_y6), radius6) + circle7 = pg.draw.circle(bg, (255, 255, 255), (pos_x7, pos_y7), radius7) + # Initialize button + button_text = font1.render("Click here to change cursor", True, (0, 0, 0)) + button = pg.draw.rect( + bg, + (180, 180, 180), + (139, 300, button_text.get_width() + 5, button_text.get_height() + 50), + ) + button_text_rect = button_text.get_rect(center=button.center) + bg.blit(button_text, button_text_rect) + + pg.display.update() + + cursors = [ + system_cursor1, + color_cursor, + system_cursor2, + image_cursor, + system_cursor3, + bitmap_cursor1, + bitmap_cursor2, + ] + + index = 0 + pg.mouse.set_cursor(cursors[index]) + + pressed = False + clock = pg.time.Clock() + + while True: + clock.tick(50) + + mouse_x, mouse_y = pg.mouse.get_pos() + + # Check if mouse is inside a circle to change its color + if check_circle(mouse_x, mouse_y, circle1.centerx, circle1.centery, radius1): + circle1 = pg.draw.circle(bg, (255, 0, 0), (pos_x1, pos_y1), radius1) + else: + circle1 = pg.draw.circle(bg, (255, 255, 255), (pos_x1, pos_y1), radius1) + + if check_circle(mouse_x, mouse_y, circle2.centerx, circle2.centery, radius2): + circle2 = pg.draw.circle(bg, (255, 127, 0), (pos_x2, pos_y2), radius2) + else: + circle2 = pg.draw.circle(bg, (255, 255, 255), (pos_x2, pos_y2), radius2) + + if check_circle(mouse_x, mouse_y, circle3.centerx, circle3.centery, radius3): + circle3 = pg.draw.circle(bg, (255, 255, 0), (pos_x3, pos_y3), radius3) + else: + circle3 = pg.draw.circle(bg, (255, 255, 255), (pos_x3, pos_y3), radius3) + + if check_circle(mouse_x, mouse_y, circle4.centerx, circle4.centery, radius3): + circle4 = pg.draw.circle(bg, (0, 255, 0), (pos_x4, pos_y4), radius4) + else: + circle4 = pg.draw.circle(bg, (255, 255, 255), (pos_x4, pos_y4), radius4) + + if check_circle(mouse_x, mouse_y, circle5.centerx, circle5.centery, radius4): + circle5 = pg.draw.circle(bg, (0, 0, 255), (pos_x5, pos_y5), radius5) + else: + circle5 = pg.draw.circle(bg, (255, 255, 255), (pos_x5, pos_y5), radius5) + + if check_circle(mouse_x, mouse_y, circle6.centerx, circle6.centery, radius6): + circle6 = pg.draw.circle(bg, (75, 0, 130), (pos_x6, pos_y6), radius6) + else: + circle6 = pg.draw.circle(bg, (255, 255, 255), (pos_x6, pos_y6), radius6) + + if check_circle(mouse_x, mouse_y, circle7.centerx, circle7.centery, radius7): + circle7 = pg.draw.circle(bg, (148, 0, 211), (pos_x7, pos_y7), radius7) + else: + circle7 = pg.draw.circle(bg, (255, 255, 255), (pos_x7, pos_y7), radius7) + + bg.fill((183, 201, 226), (0, 15, bg.get_width(), 50)) + text1 = font.render( + (f"This is a {pg.mouse.get_cursor().type} cursor"), True, (0, 0, 0) + ) + text_rect1 = text1.get_rect(center=(bg.get_width() / 2, 40)) + bg.blit(text1, text_rect1) + + button = pg.draw.rect( + bg, + (100, 149, 240), + (139, 300, button_text.get_width() + 5, button_text.get_height() + 50), + ) + bg.blit(button_text, button_text_rect) + + # Check if button was clicked and change cursor + if button.collidepoint(mouse_x, mouse_y): + button = pg.draw.rect( + bg, + (60, 100, 255), + ( + 139, + 300, + button_text.get_width() + 5, + button_text.get_height() + 50, + ), + ) + bg.blit(button_text, button_text_rect) + + if pg.mouse.get_pressed()[0] == 1 and pressed == False: + button = pg.draw.rect( + bg, + (0, 0, 139), + ( + 139, + 300, + button_text.get_width() + 5, + button_text.get_height() + 50, + ), + ) + bg.blit(button_text, button_text_rect) + index += 1 + index %= len(cursors) + pg.mouse.set_cursor(cursors[index]) + pg.display.update() + pg.time.delay(40) + + if pg.mouse.get_pressed()[0] == 1: + pressed = True + elif pg.mouse.get_pressed()[0] == 0: + pressed = False + + for event in pg.event.get(): + if event.type == pg.QUIT: + pg.quit() + raise SystemExit + + pg.display.update() + + +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/pygame/examples/data/bomb.gif b/venv/Lib/site-packages/pygame/examples/data/bomb.gif index 02271c38f105f143d36f74921761d68b018b2c74..f885cbb7feabda955f2cea9eaea65d38829ace06 100644 Binary files a/venv/Lib/site-packages/pygame/examples/data/bomb.gif and b/venv/Lib/site-packages/pygame/examples/data/bomb.gif differ diff --git a/venv/Lib/site-packages/pygame/examples/data/chimp.bmp b/venv/Lib/site-packages/pygame/examples/data/chimp.bmp deleted file mode 100644 index ec5f88a4dc86bae54d94350c384bf8c33320b71d..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/examples/data/chimp.bmp and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/examples/data/fist.bmp b/venv/Lib/site-packages/pygame/examples/data/fist.bmp deleted file mode 100644 index a75f12e656b5a7eeb73363135b581c4e5c1742ec..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/examples/data/fist.bmp and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/examples/data/oldplayer.gif b/venv/Lib/site-packages/pygame/examples/data/oldplayer.gif deleted file mode 100644 index 93906abad21b07a50dc287744f0dd7c53eba60b3..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/examples/data/oldplayer.gif and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/examples/data/yuv_1.pgm b/venv/Lib/site-packages/pygame/examples/data/yuv_1.pgm deleted file mode 100644 index a59b3839cd1c60f2f2ebf0b92b4be032b11d9209..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/data/yuv_1.pgm +++ /dev/null @@ -1,6 +0,0 @@ -P5 -752 864 -255 -                                        -                                                                            >Zideigcgffedcbbeeeeeeeedddddddd_adglortsuy~xwyy{|w}~ytvtvrxuw{vwvvwwusrtutnnoligdgkkhhlnnoopppoononkjjd_crxqnrqqlihmnkkmklmnnnmlkghiiigfegggghhhhkgeedbdgeeeeeeeecegfeddeefilpsuwuw||xvvxwwwz{{y~~|{yxwv{{{{{{{{vvuvvvvvxvuuwwvsstwyyuvwsoorsoquuttrponmusrmhlojflppnlifgmpqrlhioqrokhikstsniegisqmigiospqkcckqqnkd`hrnbQJC3,.,-OU>0444.&0>FO[T<3FRPMTZ[MKILQLDDMLIHHGDCFXZEBY_IAYeO8@Wa]][ZZZ[\^XRK<,*00259=98666=<>=97EW<%$#.@DSgkVKsdC{[_eQ<555512369;=?BEABDFHIIIHIJKLLLLPPQQRSSTTTUUVWWXZ[\]_`bbcccddeeegggffeeegghhiijjmmkjiiiikkllnnopnooppqqrpqrqpnljja`aWUcrvnmpxqN25=<<==?79521/--0**+++,,,)07;:89;CCDDGIKLNNNNNMMMRRRRRRRR=ctkhmjekkjigfddddddddddcccbbbbaeeddddcceeddddccbcdgilmnnoqvz{ǰjrxtrrtvvnu}}}wx{wz{~{yxurtzspmmonifflnjglpqqrrsrrqponihjd`gnypntsrjgfkmhjononmlklmnkklmlkihjjkkklllokggfegkiiiiiiiigffgiigeggffeeddfecdefedhjloruwx~}}}}||||{{zzyxxx|{zz||{z|yvvxxurtwyzxwxzuppuursxuututttttstoinqidiorojfdgloqqjehorsohefimoolfdgksqmhehotqsmaakqomlc]fso`RKD2+1-0NY<0940(&1?GOaX60HUNIR[[ZVTVXSNPKIQQDDLJ?JPHFW\NL\d^M?DV]_\XZ\ZYbWNH9))41258;=>>?><:89;><9;?:5?QA''% /=CXacWJofIv`[hT;534;89;>ABCDGJGHIJJIIIJKLNOPPPRRSTVWXXZZ[\]__`abbccddeccddeeffhhhhggggeffhijkkkkjjklmnoooooppplkkjiihh`_\ZVRONH??A;A]vrptswrM//5//.,0*.,*+0588579;>ACDDIOQONOQTSRRQRRRVVWXYZ[[`abcdefgBgvlkpnmqqponmlliiiiiiiiiihhggffhggggfffffghiijjiiiiiiiieffedcddcd_ahecknprtwz|}ϼ[emhioomgdyrxvywxqr||vuvwyslopgilkgipspuuuuutssrogeg`\expntqsnjeilhmtrponmlmnnmmoonljimmmmnnnnplkkkkmplllllllljihjkkihllllllllonmllljigghijklljhebbcfhhimqw|~}|~|z{|}{y}yyzzuqu|urrtutuxuuvvwwxxpptrmrtkfhnsogcfjmnnmgflnpqmhdegmookecgkppokhinrqsnfekolmnhafqpfQJH5/601PV;/61-(&4CHR`T12HTMGQ[^TILSOPWXY[b^QMTWKOPE?LSHGZ_SJGHLN[^XY[[Z_SJE9**302479::9<==<;;=>=77;95?OC*'##187BAAILMLIHJLMMMMMLKJPQSUVWWWWWXYZZ[[^^_``abbeeddcbbbcccdeeffffghiijjiijjklmmllkkllmnkjihfedcZYVSOLIHHGEBAABCB@LMJPPQVTOY`XSdeNBPQ>>Wc]Y[^bXSPI9((12358998748<>=:9999<=73@SD#() 2@CVgdSLqfG}b_fY??=9ADNOQSQMKLOKMNQRTTTSTVXYZ[[\]]]]^^^````aaaabbbbbcccccddefggdefhjlmnjjjkkkllgec`][YYUTROLJHFDDCBA@@?=>?AABAAFEIH@Ldojp{svvL)'.6R[QVVTgwvqqrqtttsssrrtojilnljmmlmmopqssrqponnlmnopqqrGnxptuqsxusttsrroqsuurrwtrssqsurtqqsuwuqrqqsrppsqpsusmjkppqrolmphmnkhhjjmigihghliffhgdbcdeeeddegc_cgccgecbcfjors|pgghgjo\PKNPNJC<<=>?CFENQQLLJ5.B^`[\\`^TPI8)*0/14789:;<;9:<:9<9=<=;8DU@'&%!.:FTefSLqcEza]cVHB=:@GOTTTUTSSTRQSWZZYYWUZ_]\^^]]^_^^`cba`abcefdccdedegeeghhmojjihijhebfb][ZXSOOMJHHHGFBBAAA??DC@ACCDEDJKKMOPRSXX[ZTVesopurwuQ01:I_hghc]ntqstsvvwwwy{{z|xpkjjkmrnlnppprppqponpsqrqpopqrGmyrvxutqtvtssttvxwtttrrutrrtwusstvvtttuurrtussusttrqpoorqstspquusqqpnnpsolllknrljjnqnjhnljiiihggjmjdgigfedcbcdedddddcddcdec^``[YVUVUSOKOKMNMX^UG@IVXEAb}ZOplKdzvzvszpfiw~}yttsswxusqxwvwxxuqwtqqtspmqtqruttvrssrtvvtprsrqrrrquvwxut{vuuvsqtyvqsvtrsuvvvwvuuuwtqqsusqpqqpqsrnqoooommmjiikkjknhgeddfgigghmnnrw|~w{~y{zz|yx||vpmkknleipsqoidgoqpomfdjrrplfdioprphdhmnpoibdlonlke_enj^UNK8-411IWA141.)'7DGVaM67MRNIM[W64MJ?ORLJV?1HJ79SD;745996;78;?@816IW]]XZ`TLE3(+213566777:=<8789;;:8=<4@UA%%&#,9GWhjXPtdEzb]cVJIHHNRW\ZY[[ZYYYYXYZ]^^[\[\]^^^`a``^^^__`a`_beggfgiihghggif``b^`\VRPNMKLJHHHHFD??@CEDCDCCCEGFGJNMORSTVWXZ]`aa_^c`_\VXgvvppqywS39JXfklnjhqvy{{{~wljomknolmqspnoroorqoostrqrqprvDn~wwvtwxwvxyyxvyxvwxutwxytrvvsrsuvtropruqortrqquvtpqvxwuttutqqtvtstvvtrpoqttrqqpnnrtspnrpnlllkinmmnnljgklkjhgijkfeggefhdefc``a_ec``c_[]^[\[UQPLQOQNONIOVPL]WHhzyyzup~x\Yv_Yq|}zjpuvtttsxwponqotvwwxwsurrrrsttssssssqsusvvxzvsvywwxwtuwxtvxvz{uvvwxwuwztvxwwwuqswxuuvrlwurprtsptpnqtrnmsqpooprsolklnmmnljjklijmqmoqt{~uy{~u“rrxyylonlmheoropiekopoonhflqopmeelpqtpgdflrsphaclrrmnh`frn_WMK9/312IWA141-)(6DJY_I4:NPNNS]V53KB:RTMUR46[Q7FWKLQNKLHDFHHGKPJ906IZ^Z\^QJC3(+0025677889:98:<96=;9<<6@SC$$'$+7HXggSLrdEza]cXOQSW\\\a^\\]]]]Z\]ZZ[\[\^[Y]^\\aa`^_bca`figdddc`__`^ZWVTQQOMLJDFEDDEEDCCCCBBAAAFGIJKKLLNOOSWWWZVY__YX`hcdfgiijie\WUUZemrlnpwrT?AB@@@@BBCBBEGHJJKLNQUWVUYZX]]]`dcabhgihb_beggeddefgbUKMQ[ekqmpqtoS?J]]dnmnngceijjhdfghiijlnlmoomnompoooonmlpnlmqspknqrrstsqF{w~i^gnkquwxz||}|z{zz|~{w{{{}xxwrpsvurqurqruuuuyvvxxvvwwwvtuvwvsttttuuuuvvtrrstsstsssuwnstpnorutsrrsssrlpqpnnoplmliiknogejh`bif_`^\^\Z\[\ZY`^Y]XY`[\^[ckpppnmrk\UUUQU\YWY_[X`_UTWYZZYTNNQ[ua_bagjjlkknqrrrstvwxyz{{~~yywz|yz|~~|{{}{~{wvx{~}{yz|ys{{{yyxxxw{{xx{}|}xvvvuvxvwz{{xvtuvvtssuutomoqqpqqnossqokroquxyzyodadaacbafijmllqst|tm|zoknmiijmopnnjhhoomnledlprqkfchqpqqkehqsqrlcagnpqme`gpnfWMK;1302NW>/64.%(;GJ[`E.9LRNHT`M7HVPQ>)F\B>ZdMEYWIFU^OEKGNbV;EO6,32/5?LW\NIB1)./13678899789;=:9;=74<=5?SA''%",9EUegUPv_F\\h`WWWZa_\^`a_`a_Y\]^__^_a^]__][\[XVVVVRNKKKIECEFECAAA@>>?=@BA@?BFGHJLMNPPTVYZZ[^_]`ddcba`cbacfdabgdegecb`cddeffggcWNLP[myuoqpxuV/64-$)>HIZ_C.ATWRMW_K/;>8<2*EMFBFLOKEFNLHJIFKDN[N>KK4162112CXZMIC0*///136789:9<;89:::;85;<7ASA)'%!-9DVggTMt_F[\h`XXUXacaaca^_bb^[[]_^[XYVRRRKJLMJGEFEDDFA@?>>>>>=<=@CDFIIKLNPOPTXY[\\]^`bccb```a`^_cb^]`]]]_cbadfcceefhhghhhhgffcWONR^nytpppxxX<[{mcnmljhkonknnilmlkmpoljpnknmjonoonnpqpopqqppppppprrsrr<^ihrsmnsvshacjmlijpvutw}~¿~~|z{|ysstspqw{{vxxvuvuswvttuuutuqtyxuusssstttttwrosvtpotrpqtoknlonnpmhfedcbacdb`agh`]ccgeabfa]blponprql_[^bb_`acaddaacfbaced_^`ba^^_^\Za]^`^^b__bb_]]]\\^]ZY[\\[\\`hjlqtsuy~y}}|||~y|~}}}}z|~|yy}{zzxvw{yyzzzyxxzzyvwzzv|xuuvuvxvyzwsrstns{|tsy{pollnlikplospmoqurkhjnk_ajqqpkffkprolgdfnpppnfgsqqokfdhnspolggmrqoicempomkfbirncTMK8.410LYA141,'->GJ[\@/EXTRX\]QC?=<8655:9416>=5=FA7;CDBAHLBAH@1.7/,6DU]XLJC/)//.036789:6;=;9878;<9;;9CRC(&&"+8DVeeQJpaF}^]f^YYTV`ccgfd_XTVXTRPNMLKJDCBA@ACDBBBA@?@@EBADDDGLMLNQSSUW\VOQZ^]`___^^_cf]^^^]]]^_\\_a`bdabcehhimogcefggehhgghijkaUMOV_krsruqusW=Y{i_rrnjjjjhikkhgklkjkmnmrnilonoqppqpoooprqopsspmopprtsq<[d\]bcb````dfc^abcdhjjllrttwy|}zvvxyttutsstvwuxxtx|yprttttuvwxwuuvursxvqrsqqlmlmnjilmigeabee]__bcacffijfhf^boxqoqqsl][ckfacfgjbdiedfhcaeea`bb^`b`^^`^c^^`aeb^``^_bb_acdbabba]_ba`bddcdfhmqrqqkmsrtxyxy{~~|wx~{z{~|{z{}~{zww{ztux||xuuvxvuwwsonrxxty~zqooqstronsqnrusqopoldjsfajqpmihjlsqmmhdimoookbdppopmdbjptvqhejnptupfcinpmme]dqpcXPI1'244IYE22-+)0>EJ\Z>1ATJK]]WT\ZSRPLOMOKHECBBB?:9;845;87726=3/153CX[[[YLH@+'0224677888:868;:77<;8<;6@RD&%(#*6FQdgVPucEza]c^ZZTS]a`cee^K;9@EECAABA>BDA?BC@@ADEEEIKLKKOUUQSZVVX\]\[\`\WZba]^[^`a`^__`abccdefcfeceiieefeddbcggfhjfddeihhgghij_VQSV_ltsrurvvZCcmapljmoooonlkkonmnmkmqpnpqpqqmprpllrutoqsqnnqttrpqqppq:]hcgdcjeadeadf^_db_cc_`cffghecffhnx͸~zyywz|vuywuvutvyusuvvywvrvutrmqurnnppooieeefgbbcbbfgfgjkecijg^epsoppqthabcegecddjnhchkhfgededaaa`bb]_cca``begfd_`aaabceedbcfdbedda`cc``a^`eeabhghgfgggklmkknllqhja]ڻ|wx}{w}~~}{z{}~|yw|||}zsnuzyvvxzpsusprtrutuwvssukrytjlogckoopmijiptqojdgpproifillophabjnqppmfhnnrsmcckpqole`emleVQM;*.20IWD166,$,FHI^X;0JWOHPVWUSWYUPQSUUSRSUUUUUVOLRPIIGBGFAA8-16JYa`Y[YOK=+(-/-25589:;988::9:;;;=>;5>RI)&+$)9BVbhVPzdFza^c`XZZZ]^afeg_E25=?@BDDCB@CEGGGHKNNOQRSTTUWVXZ[XWX^]\Z\_`__^^`_]^baabcddb`fffeeeefdgiihghjffgggfedfiheeiiffghhggggcUPTV^ksxttnszbDet`rrlmkmnmlmmmopqpnnpqqqonooonlqsoorrotqprrqqttrsuurpp=`jfigcfdadd`dheghdbdcbcdfedecabdcbcefedkiimnnpsyz}vz||}zwyzwwvxxvstzusrrusoroknolpnbkecjied]afiigcclgjlmjjcenqppsvsk`behgeeihgjllkihbdedcb``_ac`_egabbcdddddbdda`dghhgghhfghffccffccic`dfecddcegedffefdcdefg`ca_x}{{}{wvy}~|x{|{{|yup{~|}wzsrutuwuxxwvvvussvwpjqulfmqtvphgjnqpqnghpoqrlegpnonhbcjmrpkedhloqpjechnspmfbgongUQP=*,22IWD166,%4EHQeX:5K_]UX[YSFOVTONOPQPQUVUVYYWV\^XQOVVWVZWB19V_Z\[Y]YOK=,*.013349;87:<<::<<:<::<86BUD%"(")9CUbiWOxdH}_^g`XYZZ_adhcgeN98?GHIJLMNOTTTRPRUXXWUUWXYYXWVVY\^^\^_]\^``acdcabcecdeeddgigeeigcdjgeccdfghefghgfeehhhigdfidefgghijcXQQS_mstrtrxx]Fb|mdomlmnmmppnnqlkoroopmoonooqqqtpnqsqpprootvtqpssssrssr?hpejlhgjhhgddfghdadhifddfecdedccbbceffeheeffdegfefgfcdfdeffghkmswsnv˔z~uvz{xvwsttvwsmielhecbfd`chjgehigkkhdd`dsxrptrokgbdmoheijkoljlikfefgfeff`efa_dgbdfgecaabafhfdffdhgiifjnmijklomihlihikjjjljklkkookkjhfhhfonifedgf]sÿx}}}wyyupsxusx~vy{ywv{z{zqiosmcjqqmljdinsqpmhlpssplfgopnongcgmrroiefkorojdejpsqngbgong[QJ;/33-IWE166-'2CGPcW90I`bYY[\WRX\YVUVUSRUXWSTX[TV\ZSSWRUPLYZD9Pc\X`]\]YNJ<,*/004558989789;<<;;878:73=NH)%)$)8BVcjVNvfH}__i`WYZ[`cfgdgaQFGITUVVXXYYWXXWVUWY\YWWY[[Z[]\YZ`dd[_dddddbaeggfgfceffffeddfffffeefeffeddgiegihgefffgihggikffhggghheXQOR_ntuuuqyx^N^fahpjoqlnonnnnnmlnnnoonooppqponpoqsrmmpppqtsqqsttrpqtusAmvlqpjorpoppnmosmjlnonmmmkijjiglkihgfddhfegfefheffgeeegjkkkhgedfigfgfdgjjjiijlnmryzy}o}¡}[p{r}piozrcbbfiljhjjhmlefih_ipouqsnebbgijkkolpmlqnomiggfffdffgedfiifhiifffgjfgmldchkhkonkkmmmlmnonmmnnnnqqqmponoooqrpppnookopknnlrsllkjmpmgmmmnprtvwy}ÿu{}zuqttw|{wy||{||}{y{z|xqtvpijmoonkiiosqojflruqnlgeloopmfdksnoqmfgoqoqlcblsroleafnmf[QM>/12/JXF256.).>FRbV9-L^`[[\_\\][YYZVQVWX[YVTUWVY^YPPWZ\TLTWMN^^X[\WY^WMH;*+/0.36689:<779<<978;;:<;4;OI,&*%*7BWciVMvhGya_g_WZ]]bceffe^QPTUZZZZYYXXWYZ[ZZ[\_^^_bcb`befecdfgcdcbdgfdeecbcffeffggfeb`bbbbfihedfhheefidgiigffgfeeffghhefggghijaYSQSanqswvoyz`Q^YXkrmprmpqolmllptpnpnjmomlmnopqlnqrqoprpopstsrrrqruwvrpDnynrutxsrqrusrttqqqonpsvsrrrqpooopoonnnpmmnnmnpmnnkigggjhgefghhffffhddkgeccdefge`cigcdfgjhiokgnofbgkkllnirqoiStz~iol`r^etsmpnftroxqplb`ejjhlpooplntpmnhfghgfejhfgjkjiggghihhglihkkihiloooqqnmonnnopppnopoqsqmtwvqrsstpnpttttqpususptrrpnnpqpnmmkjjklljhgfghihhhhhhkorkpw~selokxw||}|y|z{yrimlntqiijmppplehsqooodbkprrkcdkppmmicgmnkqoc^hsrpmfafnmf[QM?001.JYG256/+*+-9E:+,CMQVZZ\\\^_`dd`YZ[\Z[ZWSTY[[\SNRSUVRUWW_]Y^\VX\`VLG8)*/.02249;988:;99:978:8;=4:QC'"&$)7DVdkWLsfFx_^e^X]_`bbbgdfcWQW[\[ZYZZ\]^^^^_`bd__``aaaababdca`aedcabcb_dcbbbbdfffffeefgfbaegfedfeeegggfegjihfghlhgjjeeihhhggghiaZTORbpprurmyx]QaXXktklomllmmjijkpomqnmrqonorrsuppqqqpswpoprssrrusqrssstDjzusuutssqrrqqrrrrtuuuvvssutsssstttrrrttqpqpnnqpstsrtutsqmlkllljllmoljopnlkjjigijiiljghhkgcffcehfccefgjddhhcioifhkhiiablrpptsv~wits`gTĜ`Qsztvrnx~|||~xkotru{tloojdfifbcgjjjkkjkgfegikigfjljlomhmrokosrpppqsrsrpmqrrrtrnuvvtrvywsqswxwvutuuwxutsvuuuspqsppqqqpnnqqqrqrqqjmprrponjjigeegiejmklrwxmkmqlfm{wyx||}rjmomjfgkpqqkeimnoroeckqonkebhnpqngcdinoold`irqrohchpoh]PJ>210,JYG3560-+'#&,*(../.6;:==AFIKMQSRWZZZ\a_Z^b][`_Z\XX]]ZXX]]Z^]\`^cVLE6'*.-/3336765987679:;6869<5:PE*#'%*6DTenYKrbFz\]e_X\__babfdfcWTZ^_____```_`_``aaaccb`__`afa_beecc]befddca_bgieabfhdacgieaefggffedeghihhgfegijhhghjjjjhfgjkkjhffgheWQSU_mtusqq{v\T^YYjrjioomkmopnllmopmmnpqporromnorrrpqrsoprrqprtsttrqrtvChxttrsyvuuuwxxuxxuswxvwutvvtssrvwwurpqrvssssqqtpsussuwvstvyzvplpttqsrnmoqrrpoopptpikmjfinnhfhjiigggffhjdgjhghhefbbdfgfhfcbcec^bbciib_gmigZhoq|[T{iGan|wvxqqqsbw{dbo~torpllmjimjhghlmkhmlnqpmnppnostpnqtqrrpqtsnrutuvwurrvxwvwuwwuvxxutzuttwyxyxvvyyuttuuvvvussqsuvvutssrrqqpooopqponoppqqomllkknpoqsngjqxzwrqrt{{{|kmnjvxhqqkjiflrsqidimnpqmggkpmlkedkskppjefmttnkfciqqrohchpohWPOB.*-0JZH3561/)--**,+)-+&()&(&'*+)*/477:=>CHKJSWVTY\[[__ddbb_ba_`a`\\bWLF6'*.,.3655667:767879<<:9:==DRI.&('*5CVdkWNu`H{]_d`WZ[\acfhff`UU]```bcb`_]``abba`_eca`````da_`bdddadecceghcegfdcfigc`ciic\_fifeiigdhlligghfgiijihhchiedgihiiihhhjldTPWZ^jsusuw~v]U^YZjrlkqmmmlnqplkkprooqnronopnmooopqsrokppqrrsrptrrssrtwI{vxwxz}}uw~}xxvtvtuyyvwxutvwwutuwwttvvuvytwxtrstrsrrsvvusswsnrxwuotxxtqsuuprusqrrsopssrsuomqsmmqqnklljlkehgfgecedcbfcdfcfc`bc_^aa[[\[]]Y]ZZZZZWTWWXUTU[ZapnllomkuNu\BOֻ}{ywrolqspmmoolptsqvwrxuutrswvssrtx{yvyz{}~xuyyzvtxyvvvx~{yytuzusvxwy|}|zwvuuuwxyywvuusstuvusprsutsrrronmopponoopqonqtnrwxxwtrl`]db\]bdbejjgkttx}yt|}wroqrkgijmrtskcftmmqmfglntuldglmnmnkfjrtonng_dorqmfbgongYOMA/+,-JZH356201/.-*')/%((,,++%)*(%$&&$#$&%%&)*,04559?ACGMNRVTW]^[__]a^XMF6'*/,03449;986899:=>>DADIKPX\C*"%&*5D[cfUR|bHya`a`WWXYaekogebWRY`cccccba`aa`aabbab`_`bba_`deb`adegfcaaccaffedfiheceffffhiefggfedchgfffggggghijjihiiihhijijjjihijldZSOQaptuuwswt`WbZ[jsnlpnmnpommoonnooqrrpmlopoorpmovumlqrrronortrmox{vrsUy|}tkimqrttvwwwwvwx||vvzvuzxuspsuqptwvuvvxzwqswuqx|xttuuxvwtrxynssttttuurqswywuvwtutqsvsprssroptvsppqnnrpmonjmqmkjmmfehheggdege_a_``]]^[\Z\ZWZ[TWUVSRSRYlvuomoqhWRWZZZVVceaWP\wɽ|xuvuqpsspnquwwtrqx|ysxxy{}yx~|}{}xyxr~wwwy||ywz{zyyyxwxxxyyxvt}xtsuwwvwttvvttvupsqqvtsrvwtruvolt{zwuuskjkljjkijkkjjkkjkfgiedaW[nsrqjhhknrurjfioqommjhkprpkhgioromicelpknldaipnqqfadkpi[QN@0,.1CVI799425332//0,-/.)(+..&)*((*)&'))&%''$(%$')'&'()+-0468??@CHLMLFA92-,-.34579:<93.+("),('0DXdiVNtfNuc_ibWZ][addggheZT[da``a`_`bbabb``a^bcddddeggbaeeddebdffeffcgdcfgeefghfeghggfigfgihfhghijjhghhefkkggiigghiiijjjjihij`TRXZ`lrtttszt^Y^\Zktmmojnnlnonnpmmnnonkpqnntspqqsqoooorqqqrutrqvwussqqs[¹wc[diejijjmnonvuutwz~{yuvxxxywvuuttsssvvvspouzwqtyxrrttwywuuwxvvvuwwurxtyvstsutpqtrpppplklmkkmfiligkldccegha\^_[YVXWT\ormkouvlXQTVUWUVZZZYZ[_a]_]\a_ZZ\^^``VVe]^cdbehekmry~~yyy{}|zxxyy{}}{yy{~~~zxwy{sux{}|zxwwwx{|yuztwzxwstvwurqsuwttvxyx{snnpmmopompuuolmlpphejkfmmnstmjiioqomiggqssqmeemsrpjcdkpsspieejpqrlefknonldcinpfXQN>,,/-GYI3689;9458423311475/-./..//-,-*(''('()'++'%&'%'(+-..,,--.,+,15149?EKOR[\^_^\YVNLHB;4/,***)(%$"&!'*)*3GWfkVOxhJqa]i`W\_^cgikgfd\W\aaaaa`^_bcabca`a`fcabdccddbddbacdfgecefghfdceffefgfggeegggghijhffghjihgggkhijgiljfikhefhhggilljkmdVQST_nuussqys_Y`]\lvopqtpnnopommllnqqpnrorsoqtsmqsrsporqoortutqotvuusru?n~~wqqqqlc^cillmmmmoqrsvyyxz~}xwwxuqsyvssxvqoquuwwtstvyrrwwyzwppzvpwvsstsqnpssnlkoolkmqllmighgbedef`Z^b[USYXSZosomoqodZRUUUWWYTUVY[\ZYWXZYY]_]^`_bfc_ccgiebehfddefffghcegfhklkmqokksurtƱȳ~~{yy{}}|{z{|}z|~}{{}z~~{yzzy~vuxvux|zw{{utuwmtyywwz{qmosrqrssrrtspopsurkfglnnossmhhgkmosqkhmnmkmojgnupmhcfmootrkgekupnmhdhlkmkcekpofXRP?+.1-GYJ67;;><57<946869842331-,-12/.0/-,/.+*,,,*))+)&,,,-./0125:?BFJLVY]a`]YVPNJE>70-))*))'&%%%%&&&&&%#$#(3F[gjUNxgIs_\i_X]a`filjhhe[V[bebaaa`_`cbbceb`cdccddcdfeddddedddfdcfffheddeffefedfgeefdgigghhghijjjgfffjhklffhehilkfhmnggikkklocWQQUborvtsrws^Xga[kvrstpjnrlmrpjsrpsolssqlloqppqoruqpqrpquupptupswxssvw>^gacdee^aefdaabcdedcdgikiknqpruvwz}¿vsyzvuvurprwxuswuyuqsrqsvwuutsrtnnrsmlorjjkigkmekicda^`a_ZWZVRajnkjmoqj[TXZYZXYZ[\\\[\\[Z^_[\^]b`_bhfa`aggbadggjjihggfelklmkhikjjkkljjibgifeggdgeeghkws|z~ɹü|yxxxyz}}xx|zy}|{zxzuwzusqvywwyzxsprutsuuxrrwxstzwxvleiqtqjkonmidimonmjiklmmnmffonmooihihoqmhhilromjebgmmpmgiprrjZRR@,/4.DYM:<==??89?<8:<;<:535766323424784221-.030.00.,,57;@EKORTVZ^`]XSROKE@952.,*'&%%%)*))'&$#(''&&%%%% $%%+4DZbeTMtfLza`l`Z]_`cdgfilgYRZeecbccbbbcbbdeb`dacddddedcddbdedcaddeggegfccghddgeeggfhiefiifhjihjiihhhgggjhglkgijffhhjljkllihiklcUPUZdnpvrsqwr_Ye`\lwpppnkpqmqunrtnmutnpttrrssqrtnptspsutnnqswvrqsxxtsvv:`kbdjkgcfgecefecefecdehgcbeeccfabfgny}v}~~|{uwyu|{wuvy{vursvtpotqlnsvqjolkiehkihmlfcbaa`]WUZUQbornnoopiYT[_^]YXW[]^[[^aZX\`_^`a_`aeggffeiigjlmpklkkmpommkloollpnkknmllomifimmkifjjedhhdb]fdafdgtup{y}}{{|x{{{vy~srw|~|zsqqrruxxxvvyxwvwsxxlbmyuojpsnheghoqoljhgllikmiflppplechkoolhfgjosqg_bimpomgjmook\RRC-/4/AYO>?@?@@;=A?=?@><<=;78<;99997788533358;77;BDEIOUWY[[[YXUQLID?7242.+(''(+)%"!!#$&''&%#"!&%$$$%%&&!"!#+6EYbgYRucL}``ja[\_chfhiijfZT[dcbcddcdgd`becabbabbbcedaaefdcedeedddffgjfdeggedffggeeijggggjkhgjihghijihcgffjihkjghkjjiijlkhfjlleYQRWeqrtprswubZca^owonournnpsropqqonorupqwvonrroootwtrwsrtvwxxvostuwvuw9cm`affccghedgfcccdeffgfffggfdfigdfeddadbbdffegjiimprx{yƜ{oys|uy}|yux}{plmkjlmkkmjgikib__^`[TRZUPbvunnqqqj_WY[Z\\]d^YX\`a_ba`ab`^``cfedfgfkmjkpnknnonmotrnpooppppprpooooqsonooljntqrpkijmormoolllijdfhhgdecddcbcfhljmpomu~|ttwxttutsy|z|}wuxwsy{}rfsykhnrqmghglppnhdelsuqkdfqnomjihhjlmnjeelrssjadjlkmkfgihki]OPD--4/AYQ?AAACA>@B@ACB@?ACA==@8:<=>>=;>@BCEHJITUWWWVXZRQNJD=841-)%$$%&%'+--*'$$#""#$'(&&'''&%$(&%###$$#!,7F\gkZRwbHz_^gb[[_hmjkmjif^Y]ccccdcbcfdabdbaaabdeeddeffcbehdbdfdefffgheggffffdikjhijjkkgfijfgmihghjjjighmnighgdgjjihikfghhijmncZVTVertsostyucZhc_nwprurqonnmovrqrqnosspsqpsrqtqsssusruutssvurpuvssvwvw?hsijigfkjhffgggigefhgdadefgfggglfffghehgghifcdhhggedhidib^bffdefmmmqqqxz|ѿzmŤwxysqolrtkddc^_[X[TSjuqstun^VY[Z\[\X[_`^]]_ab^]bb`caefcagiclkhltpkpuvtrtxvpprqprvvrqsrqqstsmoqtsrsurjkvxolqppqsonspqlwtijmurollmoonsjehhc_`fdbabcccadinrw{~sxz}y{~|zyx{}~~~{{zqwuiinoqqgchmppnjghlrrolggnprnhedfmppqmeelqlmjcblqnnmhhgfkk]MOD,,2/BYQ@ABBEA@BCACECAEECACCB@CDDFLONPV[]]]XRTRME@=71,,*('&%%%&&%##$'%%%$$$$$$$$$$$$$%%&%$"! $#"!!#$%$"(+09AL[hjSKtfHyb`hd[Z^gkeellmi]W]fgdcccbaaddccdb`cfdcddccdehgfffdegdegeeffffghgedeehihhfehhjjhiihikjiiiiihiijkjiihhlkijjikffhkjijmdXVYY`oytptvyubXea_r}urqosusnoqroqrstttuooprqstsprutqsvtturrwwtuzvuvwxxtEhrospnqpsromjlroliknnlnjijjjijmhglngfhe_dhhffghgfddffdcehkiecfieeffgghhdfhhggikehmqolmrtvx|snsy{{{yق|ɒst\e]wtqzurv]XYWX\YY[W^\^_Wb^fe\`ecgejhcefgiiknorsrpuqqutprxttssuvusxsquvssvtuwwuvvronquursvnqrrsrqsppttpmopklnpqqrrrnggopljlmmmmnmlhjiffijhghijjihgjoprx{yploqryx~~}yyvpmmrqiigpolmiekpsnkmgbgopolgcgoromjeelrolgcdjnklpkbcghg]UQE/+32>SQBDGCDABCDEEDDCFJMNOQRWY\^^][YUQKGC>950***'((#"$&'&%%%%$##$#"!%#$'%"#'&% !&"&##$%%$$#''(*+-27<>BCNTLJZghYNzfG{c_ebZZ^gmihghkh[TYbkhbaffb_cddcbbcfddcbbeghcadgdcffgefiifefhffggghgeefggfgieghijhhjdiljgilogikiikllmmmlmlllhjkjhinr`WRU\fptwwwrvvd]he`n|utxsqsvsrsrrwwvvspsutvuprwwtuutttsptvuuwwvxwuvzzvuwAo|ruzwsstttutrqsqprurprvplotvtrlloommppoomkijlmiklkihikjkkjihhieeeeeeeefeeeefeehecdgjifffeeefghgghfceihhffhgfhlklrvrmpw{u|u~xu}kT̎urtwsrnRHq\gstttnid]`]]bfe`febdhfdhmmosssuyrqstsqtyutstwxxvsvwtsuvttopvtpqvwvtssuvutuvutrppsqqstvwvwutuuuroqsppsropmpstsqnmlorqonoomnonmmnojkllkjkjlorpmlmlnrutu{~xxmjorx|mmnkecgnnnojehqrokjgionknnfekoponlghmsokgffjmlpmdbilhd^TOD0-30;NOILJDGCDFIKMMNUTTX\\WRSQNID@<://-+)'%%%"#&%'(#&'''&%$# !"#$%$#" "! $!!$! &#$%),1578=??BIOQSZ^WWYNIXgj[MwhK]^j`XY_gmjlljkh[SYdgdegfffbcecbddcfgcadeeddebdgdcdcgfeeefggdeggehidfiiffiiegilifkmgghjljhiljjjkjiikmknplkmkgjlkhgilf\VTV_ltuutputc]jhcpzuuvrpruttutwtqquwtrtyxqovyutstvwttusuwyyxyxvwwvvwy{Enyqtutxwwyxvuwvwutwzxtuuvxyxutvuwustuwzwuuttsssqstsrqstrqpoponmooooonnnqnllnnliikjhjlidijjjjiiiiilkgfhghgghiiiicedceedecbdddhkhhs~~eeb{iZxpmyxvwj55~η]oĸ{xxlzupvxxyupomptsqptxtuusrstuwuuvxyxwrwyusvyxqtvvxyyxy{yutwxuttvwwwuszxwvxywuyustxxtouyyxxurtstvusqrtnqturnpsrollnppoqlmnlnspoqrpmlmnls{ytt{}og`_aadhfigfhgiq{~qxqnssmggklnppkghrqpmjgjntmljddlppmliefnsqpkeelqmnhbfppga_RMC1.2.ADHMMMNPSX[^\baYV\^[[fk^XWMJYhk[LvgH{d`e`[^afjimkjkh\RW_efcbddcefaeiddhhabdefeefccefbcgjehhffhighgghhihgikjggjifiihjlhgllmljjklkkmmllnnmmmkjkkihljgggjklf[TUYdoussvtzwd]hfervswvsstsqquwtruusuxwywtuxwvzwussy}ztwwyxuuwwxwy{zxxz]~|zsnottpwzxvy|{{zvvy}}{y{~zx{yux|zzzyuwzxvuvxxwvuuvvuuuvtttuuvvvtuwvutuvyvstttuvstuvwwxxqqsspoqqonoqpmkmrqqpnlkikkjlnljmifhllhghhfmdffdebfbb_Z^\\[WWYRR\koonknqdRMRNPdbK`uvsuwkʳyBA~xw~|ww{zz|{wuvytuxvxytu{xtxzxxuvwwwvutwywuvvuyuuwzyvvzytruvuuwoswuroqtrrpossqrqstsrrpmlnuyxw|~zlhligjlnjjlkjkjjlof^egY]hqnooiemknqniijmplijgfmqnljhehopoojegmqpkheehmnnnf_elmmZRPE0+1/;KMBBA92/..,+(&%"&)'%#%'$#####$$"!!"%%&% "###! #""!!!! "#$%',2678:??AHMNRUVTUWZW`ea]\]]^`daY\b```____``ahg]Zac^dfeZYZPL\ikZMwhH|faec[\`hmjkkkkf]Z`eedghffgfecaeiebfeddfedfihhfeghhffgijiggighjhhjkfjmmjjmmkhkkmplimljkmnnmmmkkmonnoomsvpkmmkkigfgkmgYPT_jpovuww|wc]nkfr{tstsvvsssrswwvuvutusvywvywqtwwuuxzywxxxyzz|yzzwx{yuUwz}}kalspsxtsy}xvz}{y{||||zttwx~|y|yuruyyustwxxwtsvxzyvwywwwutwzyu{swztx|tuvwvsuwtxuqquututpnrxqrlloopgbhceZSVWPPYjklqpnmdRPSRRUUWSPQYUT\[\W\\Y]acbgfdgjloklmnnlheijihhijkmggoroosqqppt{}{~xzxuuxzxuwxvssvzwuwwrswvrxytswunptyxtv{|yojotrqsvsorwtptpnqmdhplfimnpmhjlnmmomiglnookggjmllmmihjkmnkfhmnqokfekqqslcclojg]SOF4/3-#$$&'%$(##$%%%$$"'('$#"!!"""### "%&&'*-7;::>BFNNSVTPPV\VZ_^ZWUVQ\db\\^^`bgbY\b_`_]\\^acblk^[eh`cjj\Z[PIZjm\LteH}bdng^]ahmhhhgih_[_eggccfgefdggffhgehgfeffedghgefhgfgijjihjkkhlnikpmmmmklnmknkmomlmnpnllnqpmooonorqoomptrjiokhfhlonlaYTUXbnuvtvv{ub\kkdo{vttyxrpvyvtrtsswwvxtqtywvwvvwxxusvzwuxzxyzwuvwxyz{{8_oggf_^^]]]_bdfdghghlljonnqtvvuqv|}}~|{}{zyxwuvvxzzyx}|{zyy{|{uwvqtzxrquvsnp}xrqtrprvqommkjkf`_^ZXOKYloppntveUNQQSYWSS[WQYa\UYY\__`eldgiihhknponorsrqrolmrtspppmijnnjkmlhffebeegmrsmgmpux{þ~~z{{{zywvry{|qsu~yxprpnruuvmprrssroqrwobkwpflsrmkkiiklnnjhilqrmigimnrojhfhmlmokeflmpmjcakrmmohdgnrkZRNH:-)) &(#"! ""$$! !!!$$"$  %&&)+/51AABFMRUUSTVWUX\^[VVY[[]^[WY^Z\bb\[][ZbicY]cbfklbXZ`^dd]]^^eechf][cgebifZ\[NMYijXKydH|^`na\`bfkinkkni]ZdkiigikifhjgimlhhjiffjihhhfghijiihggfgihgjlljimomjkllknqpknqonolkpqjinponlomnonlmnmnnoppnmqkggfjmkcXRU[dovvuuvxu`^nkfrxqswssrsvutuzvstutuwuqquvwwutxzwuxzw}yzzutx{zxvy}zx}<`j^_cadabcdca`^c__bb^^a\[[\_``_^^____``dbbcdekqlosuwy}~Ԭ}{wyy|vuutzwtyvqtpqxsomghga[XYTNZkqmkmmniSQWWTWX[ZYXUVZ\Y]]_ccbdhhjllmortvtssuutrutrrrssspoosutrrorrpqstrusqoopqqtqoortsrmllmooom`gfchifgje`_cghgmoqtx|Ԯx{vt{}yxzzxyxvvy|zwxvytimuphlopomkiiloonigjmonjigilrrolhehorqkfhjmrsohcdimmmngehmqk_UVK/#&$! "%&$""%(***/56@@CJKQTVYWTW\Y\baZW[Z\bdaZXZ]]_`\XZ_\afd]_a]]ekd[]dbcgicZ^dbcb[[\^fgeki\Y`c^fnl][XJJ\klYKwbH~]`ob\`bgjinjknj^\ekjjhhkighggigeilffhiilkiggikkjihigjifjmlikoollkkmrljlnnmmnpnmonmportpmnpnmmmnnmmojnpnlkkjpnlihhklf[UW\dntxzyy~ybajihu{ruxtsrtutsussuxwttvtrtwvuwyxvxzyxz|{yxwtvzz{{ywzzy|9_mcdd_`cdefeecc`__bbabda``acdcb__^^__^^a`aec^\]abca^]]^_]^bdcdghjlorvxyȦ||vjtwttihgb]YZSKUmnlmlmrnZQSTUYXW\WVYXZ[W][\_``cghkosttrrxwuvxywvvvuutuvwxsqtvttwuxwutwvtuusrqrtvvsqpqqqpuspqqsrqlnpnnqrntpkijkjijhfghigd\]`cegghkmnptzݯ|wtxuz~~xrv}xqoonnmgimoolfdhnpomjhginmmmjfhoopkggjklppjeeikmmnfehlpk[SSK1!% '3AJQOG=97?GHGLNTWXYVSVWZ[^a]Z]_YZbaYUYZ]ad`[Z]^_aa^Z\a^bgf__a^ainf[]bacfid^afehha``_dddmk_\egaflh\]\NL]mmYJvbI}_and\\ahnjklgjkb^elihjlhikkgfhjihfcfjigjkhhijkjhhhihhhjkllmjmmmmnnpopopqrplnonmooopmrsnlopmmrpmqqoqsqnmmpppikjhggild\UTYgswswuruwc_olgt{ttuusstussuuvxxvstwvwvuxzxxusuxyyxvvxwvxyyzz|~~z{zu8`qjlmgggggfghjkdffefghfb`_`bcb``___aaa`Z_b_[Z]`\]__^^_a_[\ab_`d`_`__^_^[Y[_a`behhhijkklpuwx{shhnpsu}~t̴yKFT[[RQLMKEEFJ]ojloilujWMPUX[WUXTVZ[\`ca]\^abeiginrvvusvttuwxwvxxyxxy{|{uswyxy{uwwtuvvtrsuuuuvwrsssrsuwvtttusqoqqsqosvqrppprrqookijmomkpppnmjhghfedccehfecbbcefkknryqv}z{ѣ}tqlwxjrphhigknnlgfiplkoohgnomlkfcgnrnmlhgjmloleehlonmeehkol`UNMC2+0>Qkvw|k\MP\[UVUXY\b`[Y[[Z]a\Y^]YY^^YWZ[\`a_[]b``bc_[]b``eg`\^_`hnf\]cadehf`bfefgcdb`b`ipna]bb\^^WNWZKEYikXKxdI|abkd[]`imjkpiii`]emghlmkkliljhiljjljjiikhgkiijjjjjkhgiklkmojlnllklooonllmnnpponoooonoooommomrplnnlmqnmnoonmkkjjnnllf]VTZfrvvvvstwg`smfpyuuuuttutttvvwvttuussxwtvzxtuuuuuyzvvvw{{uu}zvwxxx{|>eskmojllkjjjmoqilkgfhgdhgefghfebcdedeffdgd^\ab``ab`][[\^_^\[[ZXYYYYYYYY][Z]\ZY\YYYYYYYYZ`_XW[[TXXWX[^\XY[]\ZZ\_]]]afihfkjfhotuw|xdbd41LQD784/.//,=drjlqhnud=5;@DINTgtpbeu~xsppponputrsuvvuxwvwxywvyxvvvxxxvtuy{{yxvyyxx{{yrsuxyywvuwxwuuwzwvtvwyyxtuusquwtttuuutrqzupnprrqoppqqqoooopplilqkkmmmkihkjhgghijinslozvqrx~|z}}vyjfeevwhtshfijmoomifiopppjdfpomlkffkqpmljdejlnpnfaflonleehjnlbYOPRKLYoxy|x`U^`ZYYYVY``ZY^\Z_c^[_[Z[^]ZY[]^ac`^`f`acc`\^cb`eha[]_]ekdZ\b`jikjdcecce``^XXTRUQG??=953./HYOJ[kkYKxdI{ccia\`cfkinolmi]\dikmkjlmiifkihkljiklkkkjikjjlllkkjhjkjgijijigjmnlmmlnpollnnnnljjjjnooopnnqqopqomookmprokikjjkllllkbXRV\fqxyuvxvud_omgrzuuuutuustvvuwwuuwwttuxywwwuwuwxwvy{wuwzyxzzxywwz{{}In{qrtprponmljjjpppnmllkonllmmljfhjiggikefd`bhf`cdeca_`a^^^^_____^][ZXWW[XWYXTTVTTTTTTTSX[[VUYXTPRSQRRPKKMOPPNMLJKLMNNMKOLMNNQTSPQ^bSQXOQVWal]R^YECPSGBGD97;9/9\hcehfnr^2)+,+4BQuü{z{{ywz{zyyyvrxzzwwyywywvwywsnxxwvvvwxxvutvwwvwzwstwxwwwwwwwxyuvwxxvsprstttsrqsqrusnmprssrqnkipppooooosqqkntomqtttxxttte_`^\dkecbdfhnsxvzktysjjstkedjlmmlgdfhqtohehnooqohfikopnhehmollmidfmnnkdehjnla[VUY^l{}x}|nkv|rYYa^Z\^\\a`ZY^[[bha[__`dgd^]`^_ceb^_c`acc`\^cbcgha^_^gnsj^_daea`]SNMIGG@=70/+%'&# #=TNJ]mmYKvbJ~bcjb\`bgjinmkmi^]dhmokhklkkljhggefiikmkikljjlmmkjjjojknlijljlnmlijnkmmkknpnoqqolmnnmnlkpqonqmormmpnnnnnonmlljlkginodYSU[cnuxvwwyvc`pojsxrsrtuvtsvxwuvussuvuxstyzyyxstyzwvwvxwwvx~~vy|ytx}}|Bhwnqtprpqstutsrtqqttqqtusrrrrqonqtrnlorjopkfhmnjkljhfghhbagf__e``______]ZY\[XXZ\[ZXVTRQRRRQQQQPOQQONOPOLKJJLMNNHJLNMLKKMIMOIJLHIJLIHHDEGHJJHHFCFMGCIKIIFFDADB::7/*(%$$ "!!%,' >TOLZjkXKx`K`cld\]ahnjkogfg`]emjkmliloliklifkokjgikhhklhjlkihjmjhikkmnlnnmnnlknqoopponnmoqomnqqprrqpkkpoopoklmjmoqqppmjidgjhkmic[SRWdpttywtyyeasofntrvwtuwtrwzwtsrtwywtuxvsuz{|wwxwvwyxx|zwy{yvxw{3JRLORSVSSUVXZ[\^]\^`bbaccdfhijkgijlmmmmllmnopqqoqsuuutsqppoonnnkkjhgeddgfdba_]\^^]]]\\\[ZYXVUTSTTTTTTTTLLLLLLMLNMLLKJIIJJJJKKKKHHIIIIIIGGGGGGGGGFEEEDB@F>?AA<9EV]Z^^^g\F=FGBFDAEIQZbimowx~¾z{~~xz~|y{wqqrtw{}{upwwwwwxxxursvwtsvutsqqrtutuuronqtosvtqopsprrttvwlmlv~vrw{tifkkiihgjlkkklkhjle^cg`^hopomhflpnjljfgqmmnkhhjlpqlfekpmkjigimnpnlhffjmrpe]dnolh`X`nppzykusejp{t]X`Y^`^`iia`d]]fid___]diaXZ_adikhda`bcefaYUVQOJD=73032-$!  !"##&(# (+)*-1%:VPJ]mjVLxdI}bbje[cecmqnnpmg_^glklliijlmlmiinnjkkkjjkmmlkljgkpoijljhlnllnjlppoolkmopnlkkpmmpqnmnpmopmmqtonqromnlnsrnopnmjeglkjlng[WXZanuuuupuvf_rlct{vsttuuwyvsvuuuuuuuusvz{{vuxwvuwzyvwyzxuuuwyw|xt+AF===;=>>>??@@ACBACEFFECCDEFGHIIJKLMMMMPQQRSTUUWY[^_``_ccdefghhgghijkklonnmmllkkkjigfeeccbba`__\\\]\\\\VVUTSRQQRRQPOONNLLKJIHGGIHGGEEDDFFFFFFFFDCCDEFDCA>>??:9HfkcgigleP>BEAFFD@ACEFFFEFFGJIHINRSW[^acdompy}~ʳų~|xutvz|urqrsqrvtuwxxwvtruwxvuttvtrsuvspuysnrvqhft|xtuxyunptpmnnrpmlmmlkmmkc[enjelommkgfjnooogcisojmogcjnnmjffimpopmiilnkmlggknlnogagnmjpfYd|{mmsmvwfdkm{gbf]]_]`hg`^_\_gha\]b`gnia_^]^^\UNIGB@?>7/**&%$" !!!!"#$&&'**)(&%$$%%!$,,)+,0$8UNH[ljWN{dI|cbje]`dgklqpooma^hkmnlkkljgjjhgjmjghjighkkhmhgkmkkmlmkilooqojjmnoomkmmmnqpnqnmqqnmpmooqtropqoopnmnnrqnmnmklnkkljjmme[WY[erxvtvw{uddrlct|xuvuutuvttvsvyxvttuutvyxxvtvxxy{yutwz{yvvx{upcXV&@???>==<<:98:<==;::;;<<<<9:;<<;;:9::;<<==;<<==<;:DDDEEEFFHIJKMNPPSTTUVVWWUUVWYZ[[^^^^^^^^________``_^^\\[XXXWVUUUTTSRQPPOONNLLJJIJJKJKJJJGFFFFECA??>;<65HflacjeggV<=@AAABCCBAAEDEEC?@DAAABCDFGGFFFFEEFNNOOQTXZZcigjqsow{zrqzɵͽ}zwuyxwutqmjsrrrrqqqtwtszwoniuvruvtwuopqmmpnnoprsromlori_grmgjmmjjkiiljhlkghoqpnkedmomljgehlpoomijnpmmlhcchojmjfjmkioibj}shmou}xf_ik{tee^[]]ahga_b_agf^WVXTUUNGA:6773/*('!  !!"#%&&'))',++*)(('(((''&&&%$#%)&&++/#7SMG\mkXP|dI|cbke^_cjjhrsmml^[hkkkkklnmkigikigikjgghihhijmmihlkglmlklmnpnmoqommmjknpnjloooomnppnrrpostqprqppqpoqqomoqnllmkjihjnmd[XWYdquytvw{ufgqjarzwvwvttuutuursuuttuwwwyywvvttvwwyzyxxw{{wy|ndb_]^_$8=79<9:==<<;::9;:9:<=<;<<;;;;;;<==>=<;;;;<<===>@@?>=<::99999999:;::9:997889:;;;???@@@AA?@@AABBCFFFGFGFFHIIJJJJKNNMMLLKKKLLLMMNNMNMNNNNNMMMMMMMMMLKJJHFDFFB=>85G]jb`hfglY=9D@<@D@@@@@@@?@?@CA?AECB@??@ACEFEDCDCBDEEDBCEHABCEEDCDDGHIGHIKIORXfkb[[dkvz¶|}}{yxy{xwz|{wmlu~}tpmlmsvtostqoopppswn`htqbfqtljjbjqqnnkggnoqojhjmknqne`hsrolifhlmlmkedimnimkgjljjikpomxzjjmrwreajozugebaa`aa^WSRMJKH@851-,*$#""% !#$$&'''(()))++)(()++((''&&&&&&%%$$###&''((+.,1$7TMG_olYNzcH|ccke^_cgjipolmm`\fimnmkiklmlkmnjfhmlhhllhimijjjjlljjmnnnmmnmmnonnopkjlqqonqosqlmssnqomlkproqqooqpnpqqpqrqnlkjiihkmnb[WVXervwutryxhdpkdtzvtwvtvxvwwuvtrruvvtvxvuxywvvxwwwxyzxyww~~ul_[adhs&:=69;7788889899:878:;:988877666:;;<;;:9<<<=====:::999::6789:<==A@@>=<;:<<<<<;<;9999::;;7889:;<<99:9:9996677889988777766566788999999:9::>>?>?>?>BBAACB@>?>9573/@DBA@>>?@A>?@?ABCB?>>>>????>?@BAAAFCBBA>?C?DDBCCBBADBDB?GLGDKLOX]bz¿y{zx~}zznusoswvwsuurqrrppovqditofiopkijhjnopqjfjnmopjegnolkkigiopmlighlmllgabimmkmjfikjllowshpwmgiqumeehiu|~q[USKHEB=60+)&$#! ! !%##$%$#$')/)%&)(()++*)(''&(&%%$$$&##$$&&''##%&()*+,0348>:0/2&9UNH]mkVMxcH|cclf\bcbjmkmookc_hllmmjjkmmkomimolknmlmlkkmkhhmomlnonljikorqommoqpnnpokklljjjiefffdhghfeinmtsqoqonqtsrqommkllkkmlmnf^ZXZesvsturyyg`qokz}utvsqsttuvsvutvxywuuwvtuvuuxwwxwwxyzyv{|njc^n~}{(;?:@DA@;;<==???=;:;==<;==<<<<<<99:;;;:98899999965433334333333440000000033210/../012345623344556::::::::;;::9:99;;;;;;::==<<<<;;;;:9876655555555320100-+.+*)*)'.GdldfgfkmN018789:::;=?AB?>@CA?AEHGGGFGHHEEDEEDCDECABBB@>>B@;;:=<:;?9;====>>@@=AA=BCCCGB<>>EDJIFHHINNNRZhv}wtty丆|{{~tvyxvvuqxxjmxvqnmqtmfgmolkmicdjpqnhbdnlmmkfcholmnmiimosoicbeknmmgeikklpopruwupdhqtlfeeejws>%)&###"#"%&$(*&&)(+++)&%&(*&$&&&&)%%%%%%%%!!#'(%$$)**,-./0:;>BEIKMPPQSY^N504&9UNHYjhVLybG{ddlf\cd`jnilpmia`hmmnlkklkhjolimnklpnmnliillmmmnomkonopmkkjikjhhgeebfgebdefjjiijlkjnpmkkjhhmnopqpoqqopqnkjkijjkmllnaYVX[guzutvuyvfdnlgw{wx~utsstwxvtuwvuuwystwwsuxxvstwwx{|yqy~pig`^vu&8;59=87667899::=;:;<=<:==>>>>>>:;<>>>>>=========<:87778<<;976545566778833210//.00/.,+**//000000000000005444444488888888;;;;;;;;>==;;:98:9:9:9:9;986630.)&,,(((,>ZjgiifjoZ4(.++-210../010025536:78899887=::>?;<@AAABCDCBDGE@BHG@BACEEA>=>==>>>?>@@=BA;<;>9:9?C=>CC>>A@?AEGHGCCDEFFFFKHHLOOSWY\bhnrtvുzpʾxrsqztt{iosleffproolggkqpkigfknookfdhmmnomigijmniackmholfekljkpnjn{zpgorojdadgcmy<#'"" " '&%%'*-.,$#(&$$$)*+)'%&'(&&''$&*"#$&(*+,//4:=;;>>>========<<::;<>@>>=<<;::========;;;:9998777666556666555511111111.//12344222223330123456756666666555555556544431/+(22(')+4MbfijehnfA03.0675310011101320151234420/5006612713454445;758;<<;::=AB@=;FCCCABEF@BAGF@A?CBCACD>@:@BA?96==<;=?@?=B@>;;<>@?=>BCAACA@???ACDEHJIHJOTTY^`bgqymVXX[ddrt~|zteͽީ|spköeoqlkgknmnqkfhmnrqjfjpprrlfehllkjiffggjkjedinookefmnjiqqnktklxrhhb^gedctx^LHUKE;-()%(&(4GVZW@0),+(()%'((&$$$$%(++),1568;>ACDEBBED@@B\\\[ZZYYYZZ[\]^^`ZY[[\R?.2$7RKE_pmYOzaG{ddmf^_bggenplnna]hklnmjhikkkjjkijjjijkkjkmnnmmmidbcfaacbbdcdda`gjgeghhgffhigcdkliilmdahqvvqikqtpnnnppponqpjkolfhllleZVZ\es{wtutywe`rmfv|vrsuwtruvsrvvvuuuvvvusuwustvvwxxywr|qryqt{̷&:@::;;>==>==<::9;;::==;===<<<;;;><;=:79=;99;;97<::<<::<988:967;<:89;<;9;;::9988987666667655557777777777766799758998544555656565887766657996688535899642022//.*%+BelggeiqfD,48103467876586568987;88;:88:876667895555666643333333332124422235668<;<>?@AAADEFEDEGIEEGIHFFIDEEDBABD@A@>>@?>?==?>;<@@=<=@A?<==<;>AA?=?@?><<<;;=:79:7ACDEEBBBA?;8621.-.&&=VZ[]YVYWW]]UKEFLY^ZZ_ZL9*2*6SOEVplVLx]Hbcnc_dd`ciqpnqpb\gmlnonllmnmkikolhjljmolkljkliddeef`acefhihgcdjllmnps{{wtrmlppkmuoqqopqmgpkhjljijfXSSZhryzzvxxtefmofsyutwtuwyxuvywvvvvxxvtuwwvuuuwqyxxysxukǠۀ!3:6764645678888;77<<99=;==::<<9;><;=;8:>>===>=<;<;;:;:::;<;::;;9888999899::;;<<;;:999::98776789666666668886558:5677667966666666877766654664355245676532455322.)'AakijhdphI./5211233443334555556655665567899987644567899888999::8::99;<;;9997457;9754689986557:=88888:<=:>??=<::>?><=:;;83550:UjhbgnooW?>>=====<;::;==>:<<:;<<9<:88:;:9;879:9:<<:;==:;>@AA?BFEADEEDCCCBB>?DFCCEBBCEJMF6:VkibekmiN<:<:84:987764224/WqoXLx`Gfdji]^_chijmkmne^gkomllnnmkkjjiiknnllmkikopk`]baXTWg~¾ȷ}nprrprnnqrnllkmlikopmfXUU[gpvwytuyzicttjuzsqtuvvttuwvrqsvvvvsuvwwwxz{{{y{|vwzy|ʹʩ퀀%48676268887643247644774799768867889998835775579688779977::979998:;;989;:::998887777789:::9:9:::;;;;;;;;:99;<<:79:;;99:;::9:9:::777666666766565565332334232001.*06Jhldjkin\;)063432223562344334658866997;:876677899::;;;99::;<<=<>=:89:9667888:=999:9::;?>=>@A@>?@@@BCB?EDCCCCBA>?AB@>?B@>>>=;=?=<::;><9::::;;9798:>?=>CAADJRWH11RkmjkmlhYQTUMFEHB?CDBBGE?9@\Y@Onlkopkedkkhkokfjmlllhbelpomkedhonkjjfcgnmlhcaglklnjeji\Z`^^XJED>4@NV[][XYY\bdaWPMEFPZepwwikpxa:$#&%&'''%%&''&&%+)'&%&'(((&'+%&?USJMRKOZN3).)&)(*':W[XQ<)1)0MKB\rkVLzbIeend]accimsmjmpc]fjikllkklnmkjhglokhklkloniefjfUSnÿԾ{trptpnossplkklkjknnmg\YXZfoxwxuuyxgcrtjvzutywvuwwvwyuvwutwvqvwwvtsrrxvzyqrxx˪퀀&594665966766543623562254223310243566554458765797679:9:;8899967:79;:879;8877665598889:;;99999999;;;;;;;;><:9;<=<;<==;;<=<<<<<<<<99::::::87777666764223452320120,05Ihleljhpb?-17365433568765689878;;9:==:<;:998:::::;<<==;;;;;;;;9::9<;;;:9:=;;;::::::86679:9=:99;;<<:<>??=<<>BDCBBDEDDEFECBCBBCA@????>>??>=<;;<;97898:;:@G?+3WpoihjniVKMPLIGDCBBFJMMPNPM[aLIQconomhbfjmmmfdkpnopkefjknpme`emnjijjgjnomidbgkjjjecojUMRNMPOF=:INXekkhfnlntvroneZVZ_ehdkx~{unp|e?"''"$,+$''(((('&('%$$%&'*)((+$%>\SHKNMQQ?35<6+))+&,EZ]P8'1+1NOJ^rkVKv`Jcepb]cdbfmtrokje_ellkjkmmmkmmmjgklgikjimpkbaghZUr˼wrwsrqomoqqnljjkkikmeWTTZfouttrtuq``tsht|xvytsuwwuvxxwwvuvvspswyzyxx{xztzxxٹΨꀀ2FLFEGGJEEEDCBA@==<=<<;<:6599448546755645666566753377669:89;:78<::;;:9:;7778899:887778997889998899999999:<<<::;=9;<<<<=?<<<<<<<<<<<<<===<;:<;:9;<;98899:67644752+8Sjjkmdhq_>/45254444566:878:<:89::::<=<;<=>>><<==<<<<;;<<<<<;;;=<:9:=<:<;;<;9;=<<;<;<<=<<;;<<<<=;:<;:9::;<;99:;;<;89<<;;;<=<<<====>>??@=>?@?=>@==<=@CDCCILNU[J.0SjkkmmphPEF@=<688646<=<<;>DZ^B/?^qolmkgjpqqnfcjolnojdfkmoolgegjmnlhdgikpmjechkjloe`szaNMKJPUPOZedefdchorljotuw{sup^VemcY`kw|xuypB(")$$)$&&())*))%&%&')**''%'*$&>YWRTURL=/457;8-'+)$8WbO3(2,2PRM\snZJr]Iddne^ab`ehmqnllc]ekmnonkkkmlnnifhkilmkjmlf_f\\gyҽounoqpopsqmljiljhingVPS^msvyzx{{veeyufs|zuuquwvtvvvwvvutwwtwxyyyyz{tx|qryxѸש뀀6Q\SRTTTUUUUTRQPORRNNQQMNKKNMJJLHFHIFEGCA@??@@?>:999:99988998789876778764455667788766677889::9888888888869;<:8898:;;::;<;;;;;;;;99::;;;;=;:<<99;;;:9999977645774);^lhmmbjoX6/4423455665588889999878::::<:;<>>>>=::;<=>>????@@@@@>?><=??>@???><=?><:99;>@?ABCBA@@?@BCCBBB>??@????>=<<=<:7??><;;<=;:99;=<:89:;:9;>:88;>@ACKLJISZF%1RgilmiifTNPKEDB@=:9<@DFKD^WKMRI9+'+,,2=:-%)&2N[P9-2)3SRJ[ro]NtbKdfqjbccdhjnolqrc]hlpnlkmlkhglmkjjjjiklnnhcefZo¾¿ſϢyponoqrtspljjkkjjlohZWZblprvxvxzwebzvgs{xttswzwttvwwwxwvxxtwwvuttvxp{}rpt׷Ӥ怀1Q`WTWUSSTUWXYYYZXWYXVVWXXXXWWVWWTVWSSTPVTRRTTRPNPPLKMLIGJKHGIJICA@ACCB@A@@@?>>><<;:999:89:;;:988888888897557887:;;976669999999955666777854763363333221144312541-<<<<<===;<<=>??@=@@?>?AB>==>=<=@AAA@?=<;>@@?>=?@ACCA?@AAEDCDDDB@CA@AA??@>@A@@???=@@=<=>=?====<=?;99==;>DCHJJSYE$+Qhhfgipl^NKNF>A>>;8;CGFFHBI\^I3@aurnlhgonjlplgkmkopgaekhnqlgillklkfdfikmkheejnlplcdshI?BDO\bcefbdfhjmnmmmmnqtvwwy||iaX]dfrc;-*)&!''())(('&'()('%$((&'*#$=\YQVT9(*(#,+ /?4&*!(F_V812%4TPE]qm^SzbIgfmf^abbgjnllllc]dlpnmmnnlkdijjmmjkommmi`bn_Z}ĽʷwvsomqrqpqgkmkjmpqfZWVXdoyuwuvzzgavvjtyutxwxyxtpsxuprxyvuwruxzzzyy{|uty|ִͥ倀-GNGJLIKKKLLMMNNRQPPPQSTRTUSPPSVVYYVVYYVUXWUUYZXWYZXX[\ZXXXXXXXXSSTTUUUVSSRRQPPPOONMLKKJDIKHEEECFCBCB>=?DA?>??>=<;=>=?AA?=<<==;;=<;??<>A><;:<:;>><;<9;=>?>=?AAABEAADFDBABBAACDC@BCCA>=>@A@@A?==?GBFLPSD'(Ihjeeejj]OPMA@@;7349?BBA=9AZ`C,:arnqndclommmieiljmngchmkoqkdchklmkfeikkpkgecgmoloedgTBALW_dd^^gd_`fjpsnnkmstrtzw{z|zi[\ckvpQ9.& (')+)%&,#')(''('%(('*%&?:<@=;;;::::987623553346,/Qllkkjfm^B56786679<=<:9;;88;;9;;;;<<<<>>===<<<>==<;:98;:9:<=<;<:9;>?><>=;:<::=<8:<;;<=>>;77:=<9:;<;;99AA?=;;<=;;<=><<><97:<=<==<;;<=<9>??=;;>=<>B@DKMQ[N-)Hkrkkjml_QROEDD=;9:Y\^X?((+#(++4;8<4(%%9ZS6-6+5QLE^qkZPy_Hgfli\^caflrpomld_gnikmnnlllmjjmmkjlgknkbiug[Žþά~mvrprrommpmlnopopdZWZ^frx}ttu{yifrpgt{yxwsuvwtuwxvvwwwwxx|uotzxtuvzrq{ٹťꀀ7QXQUVSUUTSRPONNMNOQQPONLKJJKKKLLJIIIIJLGHJLLIGFHFFHHFEHJKKKJJKKMMMMNNNNMNOPRSTUUUVWYYZ[\YX[]][Y[[[[\^^\]^_]Z[]`_]^__[YZ^[ZZYWVXWWXVUSTTTOQRPOQQKKKKKIHGFFGECABB34SnmggkemnW;362333577647898899788888999:::99988<<<<<;;;<::;<=<;===<<;==>=::;:9=;8:<;;<==>=::=>=<<<<<<<<=;:<>>;8=<<<;;>A@=;<====<;;;<>=;;=?><999;:<>?>@DEJOLN[R4$=dogfijobSQMA?>:999;>DHEC@E[^C.:[ponmhclonllgchpoopkcckmmomfcinnmiddgjjliihegigjgcs|eU\fdca_bffcgd^ajqrrolmswuquuxyz{{ywoc_qr[3%$*)'*'&*+'&()(#&'&(#$><<<;;;::;;;;<<<<;=>=;:;=8:;<::;><<:;=;9=:8:><;;:878<<:9:::9::;<=<<;<>>=;99:;;9:<;;;=>>=>??=;<<<;;<=::=@@@ADGINMOWM1';aoegmoqdUSNECB;::;=AEHEECI\`E29]spmkgfiljkmjgjkpqnjeekllmkdchmmoniefjnlqkabhkloiap|m_b[_ggbdgcbb]^cflwsnmswuqotx{{|{lYXn{vyf='*(&()")+(&))'%&%%*%%:X`XE/&*(*),3?;,,<0*&5XZ9%0&6URM_rkZPyhKeeoi_cd`ekoqsqmdaimlmljknmihlmjhilmkqp`cqjbǿľǡ}usrssqnlnlmonkmrd\YZ^htyxy|wyvgf|vgrwuuvtrsuuwvsssuwxwuryzxwyxvv~qlyѱͨꀀ6PXQUUPQRRSTUVVWSSRRRSTURUWVTRTVTTVZZVTTYVUWWTTVUXXUUXXUUUUUUTUUTTTSTSTSTTTSSRRRQQQOONMMPNOPNJKOLQSOLMOPLKKMOONMNLLNPPRUSSSRRTUUVX[ZYX[]_\]\Z\_]``aaa`_^```abbccdWT[hpjejfgfefhdfeca`_``Z[\^^\[ZYYYXXWWWQQQPPPPOOONMLKJJGHIGECDEA@@BDCA>>?>?@=:<:7:=;998:7687669::989:;<8;?>;:<;:;;:=ABCHHKVL/*=cqijprqeUQMDCA<:8:@DGGLJHL_`E2:atonkegjkijnkgikmlkjfdimnnicchkjjifeeghinh`djjipj^h}zk_X_ikedeabeb^dintqnmoponosxzz|x^GRk{{{l=&,+&+*$#()$$()(&&-*(;X`T=-)*#',79:920:3*%6[^8(2(6SOJ`rlZOx_E}bdqh`ed`eloqpnkc`gkmlkjlmkiojilonkigliZjtgyźżŽȫwurpqqpnnlmnnknrc]ZZ]iuxvy{tyyifxpdu|vsuxxwvwvuuzwtrsuwwuxxwxvssymm|ٶĦ耀6QYTWYTUTTTUUUUVTSRQQSUVTTTUUUTSVUTTTTUVVTSSSRSUXVUXWTTWWTQSVXURVVUVUUUUVVVVWWWWWXWXWXWWZZZYVTUXSWYVRQSTVSQQTVVURQQSROMNPQPNMNOMNOOOMMMNPOPPMQSPSTTUUUTSUTSUVWVUWSQSbngbabehe_\_`bddcbbbfdeggedfccdeffggddddcccba```__^^`^\\]][ZZZZ[ZYVTSSRSSOLNPLLLIFGFEFC?=?@?@@>=<<<<;<>??>==A=;;<;;AFILILZR3&>bpjkllodSMHA?<<97:@FGFGEBH\`C/;/**/63'%<]\6'2(6TPK`slZOx]Fcdnb^egbhnprpmleagmpnllkjijmnppnlkkqme`pnjľľÿļʲwvroorqomljjlnmlb]ZY\iuwvxytywjozobu|uru~|uruvsuwwvvvuttrtwzzuu|{lpسŦ怀3NXTYZVWVVVVVVVVTTTTTUUVWWWXYXWVZYVSSVYZUVWVWXWVZWVXWTSUWUTTVWVTWWWVVVUUUUVVWXXYXXXXYYZZ^^][Z[\\[YXYYWVWZ[\]^]\Z^\]``]\]\]\YY[[Y[ZZZ[[ZYWX[ZX[]XTTUUUUTTWUSSTSQOUOFDWgc_imsrcPMTRTWWVUUVXUTVVTUXZZ[\\]^^a```____bbbcccddfdcdffedbdggedeggfdcebbehdcd`^^]_`_\[\[YWVUSRQQPTPLKLMKIIGGGFDBBBCB@?@A@>><9899998655677668;;:;=BFMLMVK-(DfolmkklbRLIDDA<;;>CGIJDA>E^bG1<^oknmeclomlmieijmpmgeimnnmiddilgkmjecioklojcinjongr~f]`djnmkljhheabionrqljnqotzzy|~}~rZTeyU*")%)+)'3A=-$''&)$%<[cQ7+*+$*8>/&))*63$(E_W4&0'6TQLaslZOxbLhgme`ec^gpspnmld_gnmllnoljipmhhmrpjoma`mgr¿¼ĿѺvvtpqsrmnpnkmrrma]YW[iuvzwur{yjnscrytsvyvrswxusyywvuvwx{wtx{xuy|lsδɦ倀7R[VYZUVVVVWWXXYUVWXYXWVSVYYVUUWSWYXXYWSSWZYY[ZVTVWUUXXVVY\ZWVX[XXXWWWVVWWWXXXXXYYYYYYYY[[YWX]]Y^XUY\[XWYZ[ZXXY[_\[\[Z[^[^^[\`a_^]\^acb`Y[^^\_`Z_`aaaa```_]]__]\`_XQ\iimplpqcVW]WXXVSSUWXTSUTSTXQQQQQQQQSSSSRRRRTTTSRQQQTVWXWXY[\[YZ[\\[b`]]``bhb`dhgfgfiggiighjjjjihgffdddcaaabca`_\ZZ]Z\\ZXWVTWWTQOOONLLKJHGEDGEDCA?@CHHNQPSJ5,IgkkmkmkaPIE@@<78:<>BEHGB>E]bD-:[nlkkgckppnlgekqlmoiehlomkicbgklkjihilomihgegkkqg]pk^_^aggipvssndclrlmmmlklmq{~{zz{{r_\eyrO3)*/',L`N.&,.,,%)C\eR6)(,)5:5+,+%-95#-N`T5*4)5RNHaslZOxbJkkpebhf`fmnppome`ipqnlmnmjgjijlnlkjon^gtezſ¿Կtvusstqkiopjinpm`\YWZhtux{|vzvfk{rcrzxwxvtvwuvwuuvwwvuvwyyvsuvvymixyç怀9TUOTUUTTUWTXXRVURRVWUTURWXUSVYYUUVVVVVVXVUVXYXUYYXXWWVVVVWWXYZZ]WUVWUUWXVVXXVW[ZVY[Y[\WTWYYWVWX[^_ZVY[VY[\[XWXYZ^_\ZZYV[\]]]]\[`_]]][_ha`_]]]]][afe`]`dZ[]^`bcd`i_RZfikjmnkgeeg``````aaaaa`_^^^^^^^^^^^[[[ZZZZZ[TQRTUUSQQQSUUSQQRRSRRQPSRQQQQRRUUUUUUUUYZZ[\]]^aaaaaaaaedcbbcdejjiihggglllkkkjjiihhggffeedcbbaa_^][YXWVZVTY_^VM@MfmjolimaVPLFBA=>@BDFHHHICFY`K0:Wmolmjcmprsphdfnmmlhfhlrokhhhgflnkffhkoiffc_dlmtk^oxiebadhjksutohfkpkkmpnhjprz{vw{wojjze7'',*"4YcF 'AC0 "?]bN8.)+,;<.#*)&.;1$6X`O715)4TTL\poZP}aJgipibfeaiprrqokc]cnkomknqmhkimpmllgplZlznƼľÿбsqppqqpnmmkimstqeWW]WWktv{wxvijyqew{xyuwywvyzuvvustwwvutquurz~rn}s~耀Bjwplgin_\`]WTQYWYZZXVW[XWUTUXWUXWWWVVVVWVVWXYYXVVVUUUTTWWWWVVVUTUUWXZXU]YVWY[]_ZWWXUY__]YUUVXXWY_a]XZ[YZZ[[Z[[\Y]^\[]\Y_````__^Z[\]][[]\[[[[\]][_ba]Z[]baaa``__[c^Yaknrkhfec`adccccccccccccbbbbaabcdeef\\]_`bcdba`ce`^edcbbbba`cb`_]\\\WXZ\[[YXTTTTTTTTRRRRRRQQSSSSSSSSXWVUUVWXUVWXYZ[\[\]^`abcfffeedddddeffghhggghijjkjigffgffZWfnmmjmqg__`_^^XXWVVUTTYWMJX`O;AUlrlklhiiijkhdfponnlhgilnlgdfiigkmhchnlkjhecgklokbrnicbdinpkorqmlossrsvrljltyzwy~|~ohp|zG'#) ;[Z@, )E@*$)9XdX?*'4;=0#%,+*0@,(CZ]Q715)4USK]qpZP}bIghqg`ed`gnpopqmcbilomjjnonookkkhknmpi]otmͶwusrrqomonlhjnnkcXX[V]p}sx{w|vfj~vhw|vuxtvvuutsruvvwxxwvvvswwt{mh}u退Zstxspmnsqmkmpolfgigc`chdcba``abccba^\\\^^^^]]]]YYXVUTSRWXVRUZ[XSUVUSUWYRS[]XVXVWWXZ\[XUV[\ZZ\^_[[[[^^^^[^_]^`_\___^^]]\^___``_\^^_`abbc]^```^^^aa`^]\[[\_[Yafinsnkkhb`a________\\]]]]^^`aabbccc``abdeff^ea]ega_`abccbccacefgfedccccdeffffffffffgffedcba````````]\[ZZ[\]YYYXXXXXQQRTUWXXZZZYYXXWTUVVWXYYXYZ[]_``ba`][\_b[Wellnnsnf`bfgghhggggfffdh`V[cd`\^jqljkelnnpqlhhnnmjhhjjmlkidbfmolifchlkllhdfikmijeoyohghklgmuxvssssrtxzwutvxyyz}~}viis`<))/=5)'*))06:&2U\XR904)5TSJ]qpZP|bHfhrg`ec_gnpspqlbdmnomiilllnmjkkjlonoi^nrrĿ˻tsrrttsrrqnkknnlb[YWVcu{uxxwydj|sgw~xuwsvuuwurtvuvwxurpwxuxxu|njꀀZtt~{{z{x{}||z|{yy|zywvvx{|}~}{{{{zzzzxxvvtsrrlmmllnkgdfgfipvxnt~wpnjghhfbabec`VQUYXY[YXYZ\ZYY\[ZZ]ZVXXYZ[]_`a_^]\ab\``abbbaa[\]_aba_______^_e`[^ekmpmopnhb^]aaaa```__``aabbb``__^]]\ccba``__X_^]__]a`bddddegeeedca_]`_^^_acdbbbbbbbbgffeedddgggggggghgfeefghffedcbaabbaaa`````__^^]][ZYYXWVVVVVWWWWWVUSSTUTSCMbebjkkh_YZ[ZXXZZ[\]^__[b_X\dgheagpmjgchmoolhdejlnmiegjqmjjgdfkqoifhiilomhbbfiighcgzvilf_[[]_`hmqutuvywusvyyvsyxy{|||~s`fyva<*4GW^V@3+CP6%%(@\a[H21>:*+-+&*14.,D`\VS;/4)4TRI]qp[P|bHfgrhafdahoqtpqofcilmpomoqomjjkmmnnnmj\kwz¿ͺwusrsrpnonmlmoqqe]XWXgwyzwvwzeizpcu|xwvwwwxvst}ywxxvuuuxuxwu{|po耀]mt{vz}z}~|{}}z{~}|{}xltuomponmmlkjhhgdefb\a`_^^_ab]Z`d`ab[``aa`][Y\[[\]\ZX^^^^^^^^b\[`gjjfhnqjb__^``__^]]]bbbbcccc``______aa`_^]\\_ab```bbccc`]\\^_acdc`[YXY[]^]\[___________``aab````````cba``abcabcdeffghggggfffiiihhggfihhgfeeddddccbaaa_^^````KUjlhljhd\VWXVSSVVVVUUUTTTOO\aWLSZgpqkggimnkkhgjpnprkcckmomhfiifjomhghkmmkhc_ekijhcdmjXF?<::Z_]P916/(*('+-.06CT^ZZU;.3)5TRH^rp[P|bIfhqhaed`hoqrqpmgdglnqqpqpljkkkkmnopli^oy~¹¿¿ƿǿ϶usqqqqomkjjjkmorj]XXZgvw|www|wgktds|xvwuuwwvvvswttwxwutswuvvtzyipľ䀀\kp{uw{{y{|{x|~{z}zyvvy~~|xuxyywsolklkjihgfeabimjljcknmicaacddcba``____^^]]]]]^^_`aa]]^^^__`ba_]_hkfbdeda]ZXTVY\`cde]]]^]^]]]]]]]]]]ZZ[\]^__________a`_^^_`a^^____``[[\^_`abccbbaaa`bbbddeffaabccdeeffecbdgj_\jsqqnqleadhgeedddcbbaaa^VVaf]UV^gpsngflonllkhgmoolhfgihlmighihkjkjfgkjihheaiolnifgf[G81/,**(&#.5@MXaehwyyy|}z{{{{}}}yeep~{a<5_dA"*68WY7!%>Z^\WE50*''&*+).=P[\XX^V:-2(5TQG_rq[P|aJhipf_db^fmonsqhdfjnnmlopkilillmoolnmecvu{úĿϵrpopqrqonmlmlknrl]WY[gtxzw{xxshm}uhvzttxtsuvtvxutssvywtqrwvvutyvdrųÿ Xps~{~}~|}~~{~|ywz׼¾uihaidk~~}}|vutrqonnjjihgfedaabbbbcc]]`fg`\_fgijkiebd`ZVW\dhba_]\\^^^^^^^^^^^^______________dcbaabcdba`_^]\\[\]^`abb___^^]]]]]^_`aab[\]_`bcda```abcc[Vdkkmmrib_beca`ddefghhijjd_chjldaempmhcmqpnomifekokhiigjkmlhdgmnhhkjiihlkidcjnjnhgh_M>72.+)(('%*(')2?LS`hpswz{wy}~zz{~t\_m}l9(FcY7$9<-N];!'AV^`_P714*',/),AT`aZWY^T9,1)6TPF_sq[P{_Kikof_cb^fmokqslefjklkloqnmrjnnnqoklrfcwszķ¿Ǽͺwurrqqnlqooomknsj\WX[gvywy{vxtfkysiwzstyxvuutttuywtsuwvsryxwvvzvfvԽ瀀^px|{~~~}yz|{~{wux~ؿμ¿Ϫ|owwkcyÿrba_^dliggilmnmliea^[Zffffdb`_^^^^^^^^^^]\[ZZY________`_^]]^_`^^^_``aa`____^^^\\\[[ZZZa``_^]\\^^_`abcdea`fmng^V]nmhoqrslfggc^\]]^_`bcc`b\Y]`_``ZbmliieioolkllmjjnpkdfonoolgfimlmhehjlonmgacjlkleefV@67/+&$%')))&#"%)-/4DU`hpspxyyz~~~rd^k}~~s=-LcQ/#?7&G^E%#@\\W^\E772+./*=X_`ZVZZYQ9+1(6TPF_sq[P{^Ljlng`ec`gnpqptodbjmoqpoolijoqlilllqwi_ru{ʽ½;̿utrrstrpmllmlimrg\WWYhwyw{ys|xeh|tgtxsuzwussurqwxwuuwzxts{yyww{whxݾã쀀bouzx~~~y~z{{y}Խü¹¿ĽȨvvzuj~۩lZbbdggffimlloonkhfdccYeywmx|wzz|qhtljj_\cad```__^^]^^^^^^^^\\\\\\\\\]]]]\]^Z`c_bb\^fchkgqsZ]flqmlvty|qlg_\Z_^X\gh_cib`b`aaZXgnkjghikifhjiinmlkiffhkooifhjhjnkeehhjlhgebgmkpmh]J;52**)((())''(('%$##.9FUcmpuz}}~vd_r{wN8LeH%-B0!IcF'&=Z^UV\WK>46516IWT[^YSY\P?03-2RMGatqZNzcIhipi]bd_aisrqsocalsplknpmigknlkmlkmqe]u|}¸Ļ¾¿Ŀƽuyururnqklllmmnnd][Z[ivutzywytclviuyvvuvvvussvyyyyzxutuvxwxuv{thrܹĤꀀ^st~~}}}|||zԽʻ¼ʽzlիvW]c`afgjijlnmopnligfeeg\v޽Աͻw}yuqnmkkjiggeeeb``bb`]]_`]]aaadiilokd\eqsqiemntxtnnlea[]_``chlcljie_cd\_iloqjffloonhdelmnnjfgjnolgcfijqkghiffkhhjidgllnleWF:40*)(((()*(((('&$#$!)9EUgw}~~{rgl}|bET`<-AF)'K_G($C]\VWW[\M:8997=BBOV][Z_V?.1,3TOI^qoYO|`Kgfjg^df`cjqqpspc_hprnlmpolilnlknmkmud]y}ƸǾȾ¿þtwtqsqmpoonmlkkkd\XWYiy{tzzz|wcktduzuvxxvuvxwutyvtuvvvvswx{xwzqiw޻ƾ退cuq~zz~}||zy~վɲ¿Ⱦ֤bV`b]bffhlkikmllqomkihggo`[ෲƼⷞ~vjjgfmrnikojhknuqxysmold`^_`acfkomlgjjee_XbmlmojhjlmnmhgljknnkggjqqojgegiljjheejnkklidejjlkaOA:3-*)(((()*))))(&$#!1ARar}~{~zg^pjOXX33PR#+SaE!B]][\ZYVC:3582.49:EW\Y`X=-0+3UPK`sqZN{_Lfjqjbeb\dnsrqure`hoqssqnoppmomlonlntc\x|źſþ¿ûſľtusqrpnooonmmlkkc\\[]jvvw{yx|xfm~shy{suzruursxyuttuuuvz~uxwxvv{tlz־瀀h{r}}}}~|zȳǽ͚tUZdc^`aedfeflonnqpnmkjjjmsdw述亘ȴw~}zq]dffdyrcbfgmvysrzwoogfhptpkfcjlkmnkhjkkkkigfhjkkjgfhjkllheilhjiifbeiikl_J@<4,)(('()*+***)(&$#'5Nhy{iUhr_ZM6BXS24W`?$%CQXUZ^RC6-*1:3)*/.6FRX_S8.2,3UPKbuqZNz`Lhntebie`hnopptpc`gnjopmijlmmonmonlotiayǷƿſŻtussrqppkllmnnoog_[ZZivwy{wv{yfn~wkvwtwysrsvvssvwvvwxyyzwxvwtv|unyĴƫ怀dx~~~×ųû˔b]``^ce]^cifdfmmkkqpommlllfunaм½库ɓ|nnpkbƏhhi`ozjonijgknnlmkfenlkjhfgiiiigfehknkkiegklmllhghkklm]HC@5,((''()++***)'%#"" '6PfrTXsuf]OCOQ90CbU2!(C\a\[T?40,+4:3)(*-05CW_O804.3TOI^qoYO|_Ljjjjeha[gqspptnb`iooponnpolmonmonlosg`wƺ½ĻƽƿŻvtutsrsqjklmnpqqd\ZY[jz{vyww|yelwgtzxwtvssvwuuwwvtstvxxswx{xwzrjvؾ˪退d´z{y}~||νÈa[X[^]ed_egigdhnolmqponmmmnipxinͿԿẔŒyyiٜojg^uʸfssjkgkpojhhhjponlgeglmmlieeghjiihcbipmmjfdfhkmlZIHB3,('''(*+,)))(&$! !(:ew~_M_z~e`]HBG?>Zb>'$&?]^\V>,.-/.371/12-/.7Q`S>25.4TNI`spZO{]Iljjfcie_hooqqtpcajqooonnnmkmomlonlnq`Xv}{żÿþſusturrsqmmnnnnnoc]\\]kxwvzwvzxfnvgy}vstrvvrrwywoswxwwvuuxwyww{sdwջʣ뀀hŵı}~|~|}~z|ùùɺɻſӷcZW]bYZbbafgiihilkjlpoonmnnooclsg~̽ٿ㹗Ŕì˚rje`xáºeqrgjkjnomljikooomifimkmlfdehhijigffghlkheehkmliWJLB0+''''(*+-((('%" +Rw~vYZo}o^R:.AY][G'(/+=Y\VG0&.+-+3;:9::@ED@K^X?04.4UPJburZNz^Gjlricgb]forrosre^fonoonkijmlnlknmkmsb[x}ȻĿżÒsprsoproppoonmmmi`\YZixyx{xuywgrwjxyrtzsuwxusstyxwuwxxuwyvwtv|ufyóˣ뀀jȸȺ~ƿ˾ڿYXb]WZW]]_ecaaghgikloponnmnoohlnrflⴖػΙrnh]tƶgruiggoooqqlghnmlljhikrrmeafklknlhggjqmlhgikjikfTJNA,)''''(*,-(('&$!#&BpeV\vvS5(%0EI:2)(%":YbS@7,%&&&''())*'((&!2\zu`byw{p_l`2#($(*$*,'9Z\EIhnhljjllmmmmknoYCV^A,4- 6SQK_trZP~eLjjngaec^fnruqrrf_hopnoomnnmjkmnnnmlsg]tƶüþɿ̿utsssrqprnmopnkkh_[[^kvvx|zvxyhmweqxuuwzwuuvvwwwuuvxwwwvyyzwx|uiyҺɨ怀nŲ˾x}zz|}||}~}yyyx}}}}ͻѺuRac[aic^_\YXX[^`dghikomijsqlmkd`ik\TUOO\Sҿܮ}͚ond`u̗ùgqwokggoomnhcghknnjhillihhegjjnmkggjkknoheggjng[I<3*%*%%&'())*))('#0_~~}d\r}xtyr\bQ0(&(#+&)1.:W]KPnodgeda^ZWTSRLMA8R\=.4."7RPJ`vsXL{_Jkkogaec^fnrvsstg`jqonopnopoppppomkjsg]vǹ“wurrttropkjnommqj`[Y[hvywwvzyfn~uhu{xvttuwxtrtyuvsqt{|xwwtwwy{phzҹ˨瀀mɷđz~{}~~}|zz|y}}zxyzzvvw~ÿзž׳iMcbZdlf\[[ZYYZ\]ceedhlmjiolb[RNR^ZNLRNOVMVܽڰɒ}ƾϛomd`v̗fouljehnlknjcekjijljihllnlgfjlpkgehlkholfffflogXC3.(%($%%'())**(''$ 4h~~kVhzypqzyr[?,2D>/(@IFLED[`G>@A>:AAAWY8/4.$9QOJ`vsXL{]Jhkqhbfd_gosrqrqd\hqrqqqooonmnnooonnrh^vÿ’wurrttrolmmlkmopf\WX\ix}zxtx~wdrudxzururqtwwuuttuvxwwvxwtwxy{qh{׶Ъ怀fŷ̿{~}zy{y{y~{{}~zvtyÜҽզ\Lba[`caWVWXYZ[^`defgilkimh`ZQIN\SEHNMWZMSJk۱ʑžԝnj`^wĕȻdmrigempkimjegknonidejlnlihfgknlighjkkkihidemm\O:..-%!#$%&')***'&'$8luW_qyonywr[?>ACFJLNMMPR[ZV`Y;.4.&:ROJ_trZP~aMgiqicge`hptsrtsd]ispnoommmlkkklmmnnsi^užü¿Ŕutsssrqpnoppmjjle[X[_jwyz|wuzucosbrxvwuutrtvwwwyyyzwvx}wzz{xy}vh}׸Ҫ瀀Y|~}{{~|{|~z}}{{||}}~y{ӽƶ¿ĺ١VL\\^cdf]UVWWXZ_cecdgjjkkgZRSUVWVRHFJW[T\SJX೒ѷ֟oj`^vԿcmqfefkplilhdhrmijmjhkookghhhkikkiddinjghgahrjOJ<00/&##$&')**-)')(" !!# Cq|~|`Xerliv}wlXPc\DXkLOgnl_IT`ZUY\[SORUVTOJFRZ_\X\R=-2-'AHOMKHQbkmpxonihhchqbUü׭ھgu{u}z{Ľԙmhb]xýɾcmskkknpmlmkghjoomifihgkpmdflnjiihghllmkefhijeidL=B:% &+-(%*'&&''%" !>w}{~x\UmyvmWVf`[lM&LqaYi\, <[_[WYVOLPMJKNNJERZ]WY`U=,50$9VSK`tr[P{`Jglsg`ca_iruwqttg^gsrpnmnpomopommpoktmav¹üƿurtpornqqmqrljordZVW\l{|wwy{{ufqufw|urrvuuutvvrtyxtrvwuvxvwuw{tn׷˩ꀀU}~}}|}~~||v|~}}}}~~y}¿ʻ\FW][[gmjh]VW\]Y\bj^H;@FEDPY`juwsprlmlllhji[Wئٽkz}їnic]wɾgorhhfnpmlmkghjllljgijigkkgfijjiihfgjklnjgeet\:=M7#+0,(**+++*'$!!#T~~|}~~x]RjgBKibXpO%NsSShY)>[``^`]UONUOHLMHISW\\\^O5+51%;VSJ_sq[P|^Jektidga]hpqtrtpd`ktmopononmmpqnlnomqiaxøƼſqpvttsmlpkjopmmpk^YZ^jwzwxz|zsequfv|vvxvuussvxvxttxzwuvyxuwwy|sl|ӻϩꀀY}{{|}zx}{wy{Ǽưb`YYY\ZMGOXUXaimhabXW`^XZaaOHNQSUU\hpqrrpqsrttqpon`TZئnz~{Иpld]wŹepvnnmjooljfeiljimjcfjllomdbirlkjjghkknnjgaewmH14:5&'01*(*)'&&&#0h}}|~~{{bOhuL(Ko^Ig`A\hBRt`.=W[\\]\VPCNMGHIIMU\`_aaSA-60$9USK]qp[P}aKhmugaeb]hqtsvyp`_jpnprqonnonnoqnklqngayǿ̾ļÕppuutslloqpllnrvk^Z]ajuxy{vv|t`nwdtzwvwzwvwuuvxuwxxuvy~zyuwx{}shxԾϨ耀T}}||yz|yv{}z}}~¿´ý\[[WWTROHKXXQUaili`b\\]Z\aaTLIJOYcdinnmqsrqttwyurqj_Q`ԯƊs֜qja\{ó`nvkiijooljfeikmoqj`bhmmomheilkjjjhjmnkjhgbar]1$%)@?##,.+(*)&$$&%!!Q~||{}cG_}_2#Tr\8NcZ^M6E\P, <^^][Z[XRTXZVRONONTNEEB9418/ 6STM`sr[P{bKjnsh`dd_fpvtqute]gtvqnpqonpnmopokmqskbw¸ýǼƾľǿŔvrsonqorospihknph\X[_iuxyztu}wbowjvtms|vstutrttwvusvyzyzyuwx{}sj{Ъ瀀`~~zz~}{{{yy~ƸĸĻd\g`]]WUXVTXWQR\ellg_Z\]VVWSKJDH[jjgfknptsnkonpwvwvh[H]ɼֲo{}~ľ֝slb]{Ŷdsvhegnpmlmkghjoonidgiflqogcejljjjghkljkhhd_fqS$IP*"""&))++-+'')(%7m}{|~d@OfD#(CNA-1=@A0/.14-+@VSPLHIIC>=:6433311-+*'&)39/4SSNdwt\OybI}ilqjafe^cmusloti_ixtoorrmlolopmmopnvk`r}ĵĿ¿ÿɿȖurspnqnqrpnmmooni]XY]jwzyvux{whrscu{wuvtuvvvxyvtuuwvuuuyxuwwy|sl}©Үꀀ_~}}~~}}~}}~{}}~ŸĿçq\ee[Z[WV\]Z\YYW[eigcd[ZXNIJF9AJXgkjkiorturqvxtqtsvzlSB_Ǹܮٺs}|}қtmc\zƹŹakpfilnpmlmkghjmjhgfiimnmkihjmnlkiffiilkfdfd_R2! U[2$%!#&(+*,+(()& N{{|~hEIS;+30/37328;@FEHNQSO610,(+,%(5628=?D@-.8.%(*290 5SSMcvt\Oy`Ihlre`fd^fqxvqsre`jupopspllpmnnlnqqmrg_t|Ĵÿʖqpusssmmokmmghorg]XY^n|}ywwyzugsvhx|ustuuuuvxxtwxuqtyxqvxvwuw{tj}®ү쀀a|}~}~}~ǿǿhXbcZ[^[VUTU\QXY]imgdc]WLCMWQAK^ihhkkgjkorqs{qrswrqteOEmȷڰؼt~}ԛqi^Yzɾfnskkhjooljfeihkjjeadfkllmljjmmkkjggjjikhb`mkO(6hW,$#!$(()--+*-.&8mzz}}|dILSKKOLOUYTYaY\TWc]_cJ+(((%*,%8[fahhdmU./?,((080"7TRK^rq[P|`Kgmvfcic\fptvsvrc^jwtpmmnnprplknqonopfd}ŹýɿƑrqusrrmnqlnoikojg_[Z]kvtz|vuzsaothwxpsxwttxyzzywwwvvwxxuxwxtuztj~İϭ뀀_~~~~~~~|||~{Żǽºq]]`^ZXY\\XVXZTT\fljdaWLA=DKHMallnponklkjmrtswttttwtiIHܱ۵v}қqjbZyżempjlfnopnjgffijllifhkklolfjmijmmgegklmnedcmoJ$ A`>&($#'('+,..-*&# !Yxy~sYOPPNEOR[YZ`[Z[ZY[X]_K*'+%")'&KjogfjroC0A@').*06-6TQKaws[P{bLhoriagd^gqvrsutfanvspllorrrrponoonlte^w÷¿ſľĘqturqrpmnnknolopg^XY^juyzxwwvsiwwftyxywyxursutqtuuwyutyvwvxwx|sozƫи뀀d|z{~~ſoX\ba][[]]ZYZ^UNXikfg`RKMMMSYhkllonifhklklprrsovvox{hIUگ޻t֛nid]yúʐgoskjfmnonjhggklmmifgjnpnhddhkimmiehjknjcc^joJ#!"*;+  !"&(*--..+*)$ Hzruw{y_ORUTQJ?IXXXZ\[[Y\X]_L+(*'(+)0TpjkmhnbEGVB"%,*06,5SPKaws[P{bKhoqiagd^gqvxwvsd^lunnnpqqpopnmnpqpnsf`xĺĿŔquurqrpmllimnkopf^YY_lx|r{yvzwgtuewzuvxpquwurswsvwtrvyuvwvxwx{rh{Ĩζ退g~~ÿ¿vUXc`_]\\[ZZ[Y[U[noc]UNMQSWaijijjjlmkdimkkmpppuysqxtcRVֵثܽv~|әmie_|ûƋcmtiihkmnmkihiijlkhegjkmmjfdhmilmifgjkmkif_mnD # $%(-.,-,)+,(  3r{stknrzmWIT][^XCBX]WZ^ZZZ\X\_L-*)'+)&9awhjmhmVJGH7$%*1/5,5RPJaws[P{aKhoqiagd^gqvvtspb[jsqqqpnmllnmmnpqqprfbyƷý¾þýŔqturqrpmkjhlnkopd]Z\bny{u~}y{ucqwgrtuzyqsvvwwwvvsrrqsxzwxvxwx{rhŮ͵耀ez|xv{|}}}}}}|{{}{|~ǾƿdU^`_][ZYXYZTZX[ee\YNKINT]ejejpoikkdbimmjlmnmvvrtvj[YSgغת׹uΗojd]~øamshhjjlmmkiijhiklighlomolcdkllmkhdfjnjoohdl_1"##*""&#&--+,+++-)%! &_|rm`fmgOBKURW]N?N^YYZY[[]W[_M/*&&,#"@cqgimkiSF=2,,)(1/5+4RPJaxs[P{aKgnqiagd^gqvrqrrc\irusqommllonmnpqporgc{ǵĿȽ¿ÿŗquurqrpmllinnlope]YZ`mx|wwxzwqevwhuxwyvqssruzysuwxxyxvywxvxwxzqjȭ͵耀e|wzwyy{|utxxyyxux{{yz|zvx}{}|{ÿ¼úutwWWb^][YXYZZXV[`ZKGLH@BTdfgkggjklpofbinlkkmnmipvtqhTUXPgԴ٫׷t~њqkd\|þhmtjhgjlnmkiijjkmmjhjmllnkehmjnmjfcdkpjmjgf]C!"! (3)#%!%+-.---,(&&%##%%!"P}i`Xgpj\SILWXZZSEP]ZYYY[\]WZ_N-'$'-$(Nh[TRPMHH@C4*/+%'05,5SPJaws[Q{aJgnpiagd^gqvwtvug]jssqooqrqoqpnnppnmrgd{Ǽþúļ¿ŗqturqrplpoloolopi_WV[iw||yy{ztj|qcy~tsuruvuuvvtxzxwwvuyxxwxwwzqf|Ʋζ退iyy|{}}||~~zx{~~{zz{~z}¿ƿvaZfg[U[\^]ZZ\\[XZ_^PCAC<>I^ifbgiddiklkifkmlkmnnnjlsqgZPPSOPqұڬܺv||{ڟqke]xÒfismigkmnmkihiklmlhefiejlhhjkioligddjqrkeieJ-"%"""$ "#"&+.+)*.-**-('##-5/ F~hXW_\T]^RP[acd^[`]USQX[]^VY_O,#%*.)5][><@:.&3HF.$+'#%17-6TQKaws[P{`Jgmpiagd^gqvxttte]kwvrooprrprpoooonlshc{ǸƼÿƓquurqrpmrqmpplooe]YZ_juwuyutxsctsbv{sruwyyusuvtsruwttutxywyvwypf{ζ退m}}~}}ſ~rsuj[a[OQWWSY[\[[]]ZX]WIDHKIJYcfigee`bflnjghjlmkloqpookkj[NQOJVWQٸ٬t}{۟rke]x¶agtlhimnomkhggkklkgcdgkmlgdehklkjjgehntigl_=%()$'(! !#'*,/1.-.-)''''#"%!'26#:sfV]WBO\RIT\clhcb]WQJIW[^^VX_O.#(,*);`hZ`f\J5*IK3''$')28. 7USMaws[P{`Jfmpiagd^gqvwrrrd\jvwtpnoopoqonnppomugbzƹŽƓqturqrpmpploolopc\Z]bluwtvsszudsxhwzuutvvtqrvxvsvvsqvzwyywxwwypj}͵耀k¨}{~ÿÿĿvk`Y\^YQZa[PPUUTXZYZ\[X[]M=@DBBWde_biiediihlmjkmnlklqrqnjkj^TQSQNRUHZĶثŠu{}}~ԝsld\{ɺ¼enwidknopnjgfflmnmifgklknlddkmiikmjfgllejkU5$'("##!" ##%,.-0/-+,--*(()'!"#&#!$6,.d|^Sg]>IP>Ea^Wa_aZNTZOQW[^^VX^O2%*,$#8Y[bgkjk`A:\U9'&.(39/!8VSMaws[P{`Ifmoiagd^gqvzuvwf[grpqrrqonnommnpqqovg`yźþÿºŖqturqrpmnnknoloph^XX^lz}vsy{sdq}kyztw}wttwwtuyxwvwwsrwyzwyvwypi˳怀lȭ{Ǻƾȵpula^^XX_a\[ZYWVTT\WW[\[\\WWIBNTRVakkeeedddfikkjjjfkokhnpijmj^SPSUUWRV[Seرw}{yјokcYybowgdhlikpnggmlhlnihjgnlkjkkllkjlkefjkhiplN."%$! &&!%'%##'+--.1-,.,)*.(*(""(*)& #% *.*^u^MPK'!"!! %)'')'%$'*,.0-//-++*))*)%#'+,)&  '& '/+\t]A766@YYXa[TWda`^ZVPINONLFMWJ0.2*%.-)-)&?fpjo\:'(''++09/5VSJ]wu]Pz`Lhopgcgc^dmvyrquiYdurqppqponrrqpooppsic|Ĺž½ÿÿļǕossqprpmnpllkkqsc[XZ`jstwxxx{ucw|hu{tpruwvvxuqtswutwvsrxwtwwy{qi»退rįâurfTYZZ]^\\[ZYWVTSRWUXXTWZUNAN_^Y^aSIS]VOUY\^W[`P.$',5@7+(+#.SlljhD&&-'$.09/5VSJ`xt]Q{]Okrmfdib]enuvxvsg]huvsppponnmnoppomlqi`w»ż»ďqttrqsrnlnlnnmqpg`\\_kywxyz|tbv}ivywvtzsqrrtvsvssxzxwxvxvxtv{tl{倀tί˽̵~|zjWS[ZW]_\\]]\ZYWVUVVZZXXRD?8=P[]`e_ababdfecehiiiiinmkjnrfOMQTTTUUSVOSWQUZUSԯ׶nyzy|{̕opg[~ķÌckshgillnnlhhjoljiffilmhfgjhhijikiddjkfyZ2',+%%()**&)))+,+,.-/.,*)))((),-.--$&#%/03,&FZQ=,%'8SYQQSOCT]UMXZPIRUYZTX_O-+..Hi\8+.&/TlliiJ-(,'%.09/5VSJaxt\R{_Mipoeeia\fouytvxg[hvqrrrsutrlmopponmpjbxſƿý»Ďqturrsrnnolnnmpnc^\]`itxqz~xwrbuyizysvtquvvvsqvwuxwrtxvtwxzwvzrk~瀀sδǿƽʸ~x^RW[ZY[]\^\[ZYWUTSUVTX[NAC;;JYZZ`ca]^cbacffggfffghgglpi_VNNRTSTVTQHGRXPNSUHnӲڿw~ЖopfY~˷cjrigilmnnkihilkkljihjkkhhhjjiijnoihjjjwsS8*#) */*+*))+,,*+-+,./.*()))*--++,'&**+,6*5?6,'&(6R\P?=FIJOMQZUIHNONMHQ]Q1)*6ZlE/0"/[ngkjF)&*(),09/5VSJcys\R|aKhnpeeja[gotwuwwe[juqrpnorrooopqqqqppjcz¿Ŀ½Əpstrqsqnrqkkllpoc\YZ_jvyzyy|vbvvfxwsxyxsoptvvvvrpruwwvuvuyyzzor倀tѷʼȷtynVNXUX[[[]]\[ZYWUTTYYYZUG@C=KY[Y\`_b[]ba`cecddddfhjcdlkWFHRSRPNOQSSMPXXLBBEEWѲٽwԙqncW¹ŏdiphijlnomkhhhjlnomighlljijjkkiilkffiikkeQ8*$&K\6%,-+))''+0,*-20*(,**+-*''*&$+)(+2*$($&)$ :OOA;@PZMKJRZTKLGGDC?JYO5!":bzeE)&$=epjpb8"),()*09/5VSJdys[R|aKhopdfj`[gpttxytd]jtutsrrqnlppoopqrrskc|¾ȑorspprpmqpjjlmqpj`YY_kwz|tsyu^r{gu{utwyuvvrsxxsstxxsquwwtwxy{phu wЯ~~ɿȤkxwdWSUPV][Z]\^^\[YXVVSRZU@=A8,#2UijiiV+1/%&)09/5VSJdys[R|`Lhopdfj`[gpttxwtgZewwsoonmmoonmlmnprvja|ƿĽþƿ¿ʓorrpoqplmmjmoorod[WZboxytz{xzuaqou~xsxyupqwxvwxxwvuttuvxvwtu{ui䀀uԳ׽ǼŘwiyqYXZXYZYWWY[Z[[YZZVQW[XF8=HJHS\^[[]^^bb`aeebfeddegfehjWCDGFKKJKNPQPNKYZQKC3/*))()*&+..--..,1/0+$(+#%)&"&-+' "DJFEEMTA4;@>>GKDEGBJZQGUTMMWWY`V1(*4QkT-1:Xnigjb?+&-+'*,9<0!9XUMdxs]Rw]Ljoqgcib[frwqswtb[hptqopqonnlpqlilppxi`~žļȖrqrsspoovkgnrnnse\[VXn{zuyxtyu_sqr{xustssuwwtrtuvuuvvvryxwsw}qb¿Ļø怀sںڽҴqroVRXYXXY[[YWZ\]\[XTQWZSFCGNUYXVVVWYZZ]^\]`a^``acb`djkT=9BGFFLOSSPNNORUPKJC78605=1-6?AGPE?FX^PJQYQOXZ[\H/+/*.HbrmlmmiZB-.0/,)&&+58,6URKdxt]Rw`Okqsgcib[frwywwtd[gnmqsommmkjnqpoonltkbz¿Ŀ·þotuppsrmopokjlnmd[[X\oyxzytqzy`qojxsqyusstvwwvutstwxwuwutzxuysd耀tظÿÿ~dNUXVXZ[YWVWVWY[ZXY[YOGDDMVYXWWYZZ[]\]^^_`aa`bbbfi_Q02>C=CLJFGJNRQNJQMGCAAFNNNiǾЩٽ}әmjfYwȚǍ`hpgfihlllkggnjlnmhdfjmjjkjfefikkieehlmgi_mx2"(>jP%#+)**%%(''*-,,-//.//("! )-+)+)$''#!'86;7/IBFMIHKHIDP_[QLHMJLVX[]K/(.,'/9FV\ZSG4'(-.)&)((/48,6URLdxs^Qx`Ojosgcib[frwywwtc[jtwsqtqmnsmnppoonmrldx¾¿ĿÏnuvootsllljikppmd\ZY^owwwyvt|z_plo|wtxstuwxwspruuttvwvtvwzvsztgꀀuӸ}~þκ~w~~ZMWXXYZXWVWXUUVUWWZ[YKA@FQYWXZZXVX[][[[\^]^_`_^ac\J918@>=BHGIMQRPMLLOKF@AIOPNJVŸ̦ڽ}ӚmjfYwàʐelthgihkllkfgnmmmmifgjmkjlkhggijkhedhlmkk`nc+!'/UM/&,(('&&($'+-,+,--/0.(!"$&)*+(&%(($$IFY^D=NTJKX^XVSGHKQXVZ_P1%*+('(/,75-.-*,,,))-'$-58-8WTNdxt]Rw`Mgmqgcib[frwvwzxeZhtvspnnnnnqonnnoopqmdw¿Ɠnuvootslkiikmnnoe\ZX`ouwuyyv|w_rjt~xwtvusrsssspwyvuy||wwsuw{}ma怀sٻ˶üҲ|fonVTWZ[YVVWXWVRTRQSXXUJ@59JVVUVXXUTWZY\[[^_^^ac^^bZG848<<>BEFJIJLNNMLLQIB?DPRIENMjȥۻ|ԚmjfYvźʐdlshfgikjmojehnlmnlgfhjnokggiijjjhefhkpljmtg@!''&+47/()%$&&))&#)-,*+.0-.,'! "$&),+)''"!&7?4;=+!/GRR\Q58KLIOZ[V\]RPUZ]VZaR/#(-**)/),)(,*&*))*,+&'/48,8XUNdxs^Qx`M~flpgcib[frwtuzyeZjxutrppqolsolmoopqqlcw¾ƽŽēotuppsrmlklolhjpg]XW`otxw{ytyv`uiuzuvrrtuwwvtswxusuxvptyvuuz}nd߀yոDzƠ~sd`UWTWZXWWXXVTUXUPRUOD>86ATZY[TUVVVWYZ][[_`^^ada^WG849368@CEKLLKJJMOSEDIGECDC?AFKJOQMMD204)5URLdxs^QwcPgnrgcib[frwtsxzi\hsxusrolnsqmmrtqnnrjayſƾrqrsspoojijnonnqk^VT`nszzzvt|zarkpyqqwoqrtuvvvuttwyxy{yuqxyxzqb½؀tּ|¿˪ynXVYXWXWXYWSRTURRTPE>>>=FU]YVXYSSYZVW^YY\`a^^adZF1,47457:>ADEEJIJMMKLQOFCBBDD?HHGLLZŠ}ٽyؚlkfVrɨ̌akpghfglmlmigiqmjkkeelnljigefhnnheihgjpkdtzU>IC,&(&(+-)*'%'$"%&)+,++-.1+&#! ## !)+)+,)##/DW^^]\\[ZZ]^`^\Z\]Y\__][\^__]\\\\]KKKJJJJJHGFEDCBB??=:62.,,/'5XXNcws]Qw`N}ipqichaZfsxnwzsa[ittrrqpqqnlnprrqomxqf|ýſÿÿÐousoqqmlnnmklnood[[[`muyy{yv{u^tpnyursvpqvwuuwwyvruwvvxvu{yuxpdIJրnβ}~~}üʡ||}hVQX[VZWVWWTSTSSWXL<6;DLUXXXVUWYWSTYZY]\]^][\`RB30368;569<@CFGIHHJIFHKFCDB<>DEJJEDNNgľ}ڽ{؛mjeUtаΎbjphiginonoliknmllkgeilmmjfefhmnifhggkihimqW8;D7 &&)#&)(()&!"'),-,-./.+&! #&'#%&+/+)/0*"+CX]]\\\\\\^\Z[\]ZXRTUTOLKLGFEDDDDE;;:87543....----.-+*)+,-11) 7UTLdxt^RxaM~jorhchaYfsxxvuse\jwtomnoqrpmoqrrqnmrkay½Ľ‘ousoqqmlmnopppoofZXYapuvtwut}{`olkwutvsvywqqsuuwxvsstuwutzxuwod׸рl̰}y|}~{ȿt}|}m[NT_`\WTUVTTTTSXWI:BGIKKMNMIHIG@@ABGKHPQOESOKû~ڼ{žכnjcTw˪БciphjjinonokiklmmkkhgiimnjfefglmkggfhmlkkcZchYC:'"%&+'%)(')&""$&()*+,-+)$#)*)').2-,3/' #:NRQPNMKJIB@>>@@?<9::840/./.-,++,,++++++++++,,----000000//11+#:XVNdyt^SxbL~jnshchaYesxyutwj^hvyplmoopoopqrqomlpi`xſľ¿ÿĽƿĿ’ousoqqmlnlkjjknpfZWX`ouvtwtpyy`opo{wtvqsvvvwvqmttsvtt|uusywuwndʰʀpӸ~¿ǹp{}{xfXT[^\^WRSUUTTQMOPC9@MSTVYYWWXWTU[[VUYZ\YV\bYI886203675558BGIHNRRRSROQJKQUYXPQNLMUQIRPYĹǺuϿžͷy؛mjeUt͘͟gfrlgfinonokikknnllifgkkkigfgimiihfhlljsdaqaOBDRT>*"))%')%"&%%'))('#"%*)$%)'(,-.5:76431,#!%&(''&&%%$%#!!##!$"""%$! !"  "$%$#"$$$'@XSLdxs]Rw`M}jorgag_Xdqv|wvwhZgyxrppnnnkllmnprstpi_wĽ½ousoqqmlkgimniimh[WV_nvxytpr{u]tkiwuuxtpqtwxuptvwwwxwtpvvvvz}naҼu´¿ukvrs~}z\LW^ZVUVWUQQSIA?>;;8569:84889;?CFHMPPLLPTTSPQQORXWQOXORYMSRRi̽zʾît~~~ؚlkfVr͠˘ggslfeglmlmigilpnjiiggjmmieehjnhhhfimklkebgoqdEEYPIQ;*$*("$('$%$$%''&$$'+,&!#()$$(+397740/.*""$"!!%%%%%%%%++,-/01155568:<>E<9>R]TQfzv`TzaL~jnsfaf_WdqvxxwscZhuysonnrrnrrponnnnvodz¿ÿĿ¿¿Ĕousoqqmlpkjnnklrg\ZX^lv|tvvuyrYnop}yuutrsusrtutwxvvttxqwwwv{~obϹt׿οĿpnwpt~}p|nSNZ`YPOSSQQPB<=ACLVWUUVVVWWWWWSNRYXQ[WC5<=8;<=74:;8:<56<=@EDLNQQRSTTYTQQRUXWQOQURNLILQVٳyņr}}~חnodVzͥɕbhthhfhkmmkihhllkklhefjjjiiijklnkebejlijhg_fviG^jL?H6$$$%&((&$#"""%((%',-+(##&"%),-035355/-/&!&'% " !#$$%%%%&(*+.10--279;989:;=@BA9>IMQVWUUUVVVVWUUSRRUXYXF309??>@948;859957;:=DGINQRPPRUSTWWRRVVPRTSMJGBOROjйzÐyۚmkaU{Ѭ̘cishgehkmmkihhmnnmjfgllnomjijmlnmieeghmniifgme`th:(31%%$$%''&%#$$$"#%'*.,'%$%'%$$)/3216310.-&&+-*()++*-36533369<@@@@ACEFGIIEEINPNNMMNRUVZZTW\^b`bb_]_decfgjfd^RTexv`RydP{knqhagc[epxuuxyh^jtspqpnoqoqrqpppposg_|¿ƿ¾ûŋkttqrqmmikoojgjpf]XZ`mwxswwtzu]qgmwtvxvussuywtvtutsuxvtxwxttypeƳsռ¾ÿlkmfrrn{sZU[YWXVROKB?;CQUTUVTTTTUUVVSVVQOSY\H3-6:=?9<4389556646:9KTTQPQSSSTTUUUUUXUPSVJ7&'-36:==87;91054335;@ABDHFGKNLLNONRVVWVSQUUI?CLNUaLEʼvzٝmidVxͥΘchtijjhkmmkihhhklkifeilljhfeggmklljfhllljgd`jn1$&&#%&$""$%&&%# !$%$'&#!$%&('$$&+./.0242140&"5N[]\a_ab`[[_Z^ca][^baa```abcege`^adecge]]ce`cc]\]^gh`hi`\`eeegf^^^RNgusaSubP~imthagc[dqwuvzwdZivqqooqmkopnnoomlnti`xſĿ¾¾ktuqrqnnjhiklllmd[VW^kuvtrsuyn[yhovquwwuuttstvrutrtwvvvuqtuvxkjٲrƺhjz|zxxtxqufWWUSLB@?;;MWUTTSTTTTUUVVVURSXXL6&'-07ENKGC?>>4-/6666;CCBEHDDJMLJLNNQSRTWVUYUG;BMQOD9]аs۾z֜lieVvϦ͗bgrikkhkmmkihhooljheeihkmlgffhkklkiikomlgjiZ\tQ%#*%%"&%#"#$%%%##" #$#$$""%%%''&%'),./3/2752/+*#)BZa]Y\\^a`\]aZ_cd`^^_bba``abcdfd_]adefhe_]ab_bb\^_ahhajkb^eihhig^_`SNeus`RvbO|imrhagc[epxtuyyf[hssnpspmnnqnmppllovk_vǿý¿þľ‰jstpqplmkjjllllmd[VX^kuwvttvzoZvmpytqstvwxvuttttvvuwywswvwssxocԫsþƼnrxmltoqor}~qcXROI?:=>FRVTVWUWUUUUVVWWTQSWO<*#$0:DSYRKHC<:6..6;;9;ABDJGCDJMIIMMMPQOQVWQXUC8>GF2-Kѥ}mο׹{њljhVr̦Ϙbeqgiihkmmkihhllklnkggjlnlhfgjnonjghkmnmghhc]N."'("!!#"#$%%$#!#$$#"!"%%%&&%%&()(').25037511//+4L^`\[[[^a`\\__`bba```_^]]]]^_bdb^\`decca``ceffe_bfgkidlmc`hkhghf\^aUOdvu^PwbNyilohagc[dqwuuxwf\hquopspmnopoooonnnqibyƽĽ¿žþĻȽďirropokknnmlnpold[VX_kuwyxvw|qXploytrtvvustutqusuwuvwvtxwxttypaӲwּľønilkotvqqtrlqyy]SMH?=DJUXTQVWUYUUVVVWWWSUVO@0('(8GMMJC>;;>@EOFCELLGHNNNPPOQTUY`YA2;C?;RѡoӴ{ԜjddTsΫЙbeoegfhkmmkihhkmmlkhgkpnlifgikimnkikmlknhcmC%$#$#$#""#%&&$"""#%$!$(($$&&%*(&')-./64531340//;Q_`_caacea\Z]db`_`aaaeeccbcddbdc_^bfgifedddfhfd^afgidgoncailgjkg\^aUNcwu]OxbMwjkmhagc[dqxxvwue\irrpmoroovoqqnnppmsjawľĽĻſhqqnonjjipqkinoke\WX_lvwwvvw~uZpkqztttstsqrvvtqvurtutsxwtwwx{ndĸqƿž¾ùm_ksnmsqhouki}yVKQG@JT]RVUTWQRTXXRRXYVX]W@65-*+CLG;,*,;B>30246=?88DEEPIBEMLIJJJLNONNQTWVYC/9B?h¢|nĿ˾ѽzՙljeWy˯ǘaipifigllklifhmllkhfgjlpnihddhlnomihkolon_|>$#$$ # !""##$$$" !"" &')('&&')&&)++.32454477657;F`bUbdabdb]\_defedcccadgea`ejcikfbcgigikd]ekelg`]^bfheig_\__ZY_[]b[RMg{u\QvcSzijsk`b`]hqtstvwh[gutpqqooqqppqqqponvoe|ƽ½¿½Ìkqtqoonkjkllmmll^YZV[lysutxtYtgmvssstvspruuuuuwsosxwwwutou}mfĀwüýɻjbqxmgmoktxpv~v_JFNTV_USOQSOUVVTSVXVR^YL835-,7DBB@1''29;88975;A@:;DKMHBEMMKLMNNLHINQQQV^M8145,+AE>BL?0./29>??;65<<9;CE>322355313003750+,3?P]\TScvr\RvePvkoug`fb[epwwwyyj]huokmpnlmnpooonmllti]vľ»½Ŀ¿¾½ýǿkqtqoonkiijjjjkkfXVX_kt{wvsszpZxhnywz}yttusqrrpuuqruvvvvvupu}ldнrżѼ¿ɽt`\^[^hqpqtw}}x||fHCT^ZXWUSTTRUQUXWTSUWM>4286)*>EAAEMPN79FLW`WOfws^Ss`Kxmpqd`hcYbq{|xvui^iuxposspppqponoprspi`y¾½ĻþŠkqtqoonklkkjkkmmcWTT_pwvz{wvzpZyfoyrsvouvsqoqwvustyvsutvvwrv|ke€wƽ»p[[`_^hojdgilu|xuzybQVXf^RRVXQTWTPSXSI6/034-'3HJ:6IYVH/47BJIGGJJIJJJKMMLJJLUYWRSSG^ğ~}xnƽƾ⺰͵wþ֚ljcTuǿ͑]nqhhggllklhehkmmifghiiikkhgggfikjfgkooogijcrT<8*!"&##""!! #!!##" "$%%%#&&%'.1146656897960!2iyW5)(%"$&')))(()-../03576?BGFAAGHDCHIGIEGJKLNNLQSVVOPTIX}{tiŷ¿̳xՙljeVxƲː`qodhjimmlkgdfjlljgfghlmkgdcejlmmkhgjmlmihdaxwF51$ "$#"""! "#" #$$"!!"###!"%&$(-1146656897:903kzZ<4;=86KDA?>@FHECEBD@?EFEGCJNMLNOMPRW[OLSIXڿ|xqfͽȷȾʹ|ĽՙljeWyԼЏ\oqhiggllklifhijllifeghkjhjgeglkjighikjmki`c}c9.-"#$! !!""#!%&$!!"#"!!"$$#"%"#'*+/3467569977>41kjYW[\]^_```aadbaacdeedbbeedeicdabigcefdcdeecacabhmnkgG9JSWZPSfvscXu_MempgagbZdpwwtssf\huxmjopmoqrqpooopprh\uþĽƽkqtqoonkkjihhjlmdWVY_jpuxytptjX{cq}tswpqqqrppswrqstvxusuwysw{in¾рsû~}}ýlb[\gggie`fonpq{wx}xzjUPQSVRQXT:)1565/)')+CHB8@OK:2+$!5F??DABDECDGBA@ADFFEJHGGJMOONRWYSPQLHȞwxteŵÿùηz֘lkdSyϾ͉boqklbelnljedikmnnlgdgmiffhhijkmkikijpkmhk_eY0+%# ! !"#$%%$#""!"###!!##!!#&%%())-39328:648<>13hs`a`_abbbccbj`dccjigfecbeghidedcdbdkcmjeeegafejonkhcKEMPY_TXewsaWxdPxknoe`gd\epvxtqoe]gpupnmloqommmnooppjh`vǿÿ¿¼ļĿhnqonnnkmihiiklhcXUW`ouwxwprym\|lqxutvuqnrwuqtrrssrrrswwtvtrukgՀyĻ{èûze]`irihmiacktwxvst{gSRUPKV[E0-0354/(%(,AIA6?PM?4)%&087>C@>>>?ACBA@ACEDDFFGJMNNMSORWRQRJM~Ǜzzwm˷ĿŽȻ{ĽɾזjmfQvҏbkpijeelnljfdhknmljhefkhjnkeeljjihgfglnjee_d_/&&!# %"""! ##!!!""#%" !! "%&%&)*+/4778899889=15k~mdadfffinmjc`bckoeeiccijhghaegfcadibiffecebeckqlhe`FBKMV_TUewsaWxcPyjnpf_c_Wanvzywtf]htupnooppnoonnnnnnmj`t¿¾þ¹Ľhnqonnnkliikilnl^WY[amtwtzutyo\vnuxrpsrrpquvroqturswwttwtustxkf؀u¯yçºkaiqnebjka^hhiloqpvxuZRSNOWM5-1.033.'#'-;HE7;JOJ9)*00.2>@AA??@A@A@@ABCCBFEEFJMOPSOSTNPRJPܺ}vnƿɼծ||´Ⱦ¾νؕhngQu֕bfohhiflmlkfdhjmmijhfekjlmkgfgkiihedgjlijgefrqU,$% # "!! !""'" ! #'&%'*+,05679999988<.7o|tk[Y`ec`^][ECXlmc]hvh^]]XZaaVPWej^OQIDQQDCE@D\omhf^GCLKT_UQevs`VwbP{hnrhaf`Ycpxwxzyl`ispnqrnjmopponnmmlqk`sĿ¿¾žżhnqonnnkjikljlooaXWW]luzqwsrwn_~jtyqqxrstrpstqwvutuutqqwusqw{kbЀnŵ|ʹtgovlkfdjlij`Z_knqwzvn|pYWSYJ3.53-.01.&"'/7DG;6FRQ?--83.16>ABA@CB?@@@AAAA@ECBDHMPQPPRQONJJ{ʚ{tpųġtuüזilgSu՘bdrifkglmkkgdgjlkjjiedkkjhggggnklmggkmiikffii^D-*' !!! !""##!  !!"%"!#"!"%%$&)++.36449;8689;*;tkotWDBAA>;7DSO=,-:==?6;==:,'% """"""!!!!!  !"##!#&&#"#$$%()),07348=;86:9%Ax~kmyeN?4672.0+@^T99=7MU?0=81ES93LipW6-LPD85AJQP?79:;;>>==?ABA@@@A@ADHLMMQPPRTFXŚ~xɺƬy\UVV``Z`Ǯx~zʻӚlhcXwɼΔbhvkehhkkjliefijjllgdfjjjjgfgjkijjggjnjgdfgfmeL1"&'%&'#"""""""  "#$"!$(($!"$$&))(*-4899;@<695"N~w|{uz|zkYL=007,@LDHL9/BSB3>93HM34UljS7.6IWE16:1Kgohfd`G4?NV]UVcuq_UvbP{hmribf`Wanvtwxvg\gssrroijoplmmnnoppnh_y¾ȿhnqonnnkkikkghkj`WVV\ktytxsuyl[|huwrsroqrqpsvtttrrsvtqqwusqw{ki€u}ɪɾvcf]bf_jzwiuyyzspxxffud9,83-220*(,,)+13:BEB>CLRKCHOUVD78:=?=<<<>ABB@AACA@AEIMNPPSTUGXѤ}r˿½ƳTObos~eaд~ƹԘjieWu͸єbhtidehkjjlieejlkjiffhkljghjidjjihgfhmmkfdgclmZ?+-0--+))(''&&%! !#$%#"$''#"#&&(*)'(,09=::==850")]sz}~u|uaL=79=;>RM2/CJ@563:RN05\oeM4<>KVC01/A_qjdehiH4@MU]URcuq_UvcPzjnpldhbZcpwuwxudYfssqokjnoinnnnnnooog^wýüühnqonnnkliijikmj]UXZ`lruttnqyn`|exzrstlqssrrrsrtvvspswtwtustxkj÷Àq°{{ϯȻrhall^k{pgpz~{vpmz{gbczqE6;1-3/1)&,.+,1::6?NH=ANMCFMUYM:88;<::<;>ABBAAB@@ADGJKJPNQRZNBbԣ~nλ¿ſíǓ[^m|j[pԲy}֖hjgVsӵԔchqgcbikjjljeejnlgffhjkjiigeefjkiggeelkojbgejoudQKKKJB642/+(&$!!!!"$%&%##%%##%(()+*((+.5==968<1,"1hyz|xsz|z{|o]C>62?B44@>=;55G`H-8`jYC2:Q`U:5=29:;:<@BA@A>@ABCFKOOQKOPEBHlΛ|rȭśjqxytSuî~u~ӓeheVwնԚdgofigglmlmigihlmlkhefkkjhgffglhfijhgjkgfggehntrlffijh[NF?:5-.,+%#!)$%&&&&%$#%()(*..,'-5:9534, "Ktvpz~y|you|yocN:48;9;=8>G<DHMB57AJK49P84UnjbfhdK=FMU]QMbvq]WxdQyhnshae`Xbpxpovte^fmnqpllppmnnnnnnnnpjbyĿ¿ſiqplmljkkjihijklbUOVbmttsvqtxk]{fusorwusrssrqpu|vouvqqsrptutuinr~{ͭĶzlbdktywtvzyvwwsw{pernW`p;)-*+$10.,,-/15:;:<60+"(Tw}wlo~{spmsyuy|q]G:56<@;9=::G>457=E?;6:AD5E^B3MdhccggK>FMU]RNbwr`YxaOzimph`e`Xcpxxvyue_hppnmmnmmnnnnnnnnnpi`x¹¿úĻĿĽiqplmljkiijkkkkjcYUWZcovrtqv|n\|{fvvrqsrruxuooutoqussusssquuuvia½z}zÿ˶·hgtxtr{wxz~znr|udmiTby<*,/4122/,,/28:<;99COSOHJNOVWA78:988=9>@??@AA?>?BGJJIQRRRJFNPNХ{rʾĽĻ}zu~w~|Wg~ՕgheUvȒahrgidfkljkhehjlkjjhhkhijigefhjmmhfhjgmliffiklnmjginqoc]^[WN=7W\D-%%&&'''%$$%*-+*,/00--4=>6-)#0^zzuntxjjlnqpsx{t_F6D=6468FLU]RObxtaZweQygotg`e`Ycpwutyvg`ipnoolkmnmnnnnnnnnph]w½üiqplmljkmkihijmn`YWXX`mvuunrzn]}|dsurstqtutrssssqstsuvsturuvwxjiȀvƲ}{˴õwurq}||z}}xu{uekfYu~H)++.61110//13989<:69@OKDIRWWO@6699968;=?@@@?>CA@BEJLMMMQSJJUQIɝ{pȽľ¿ӿxoxvmumt~`ct~|֖gieUuɓairhieejkjjhegfmomifehjiggffgilkiihhjlmkgddgijllihjpqod]]XTM>9WjZ<* $&''''&%$)**+-/0/40,-473,'!4d|x~yu||rgllfkmonps|lWJ60887875776=>1513=<.IfM3AVfjecfM?FLU]SPbxs`XvbPzimqg`eaYcowwuzwf_fmmqrmklnmnnnnnnnnpg\vþ¸ļiqplmljkjjjkklll\UTY^gptvvorwl^zcstrrtqppsusqprrrvxtqvtvsvuxyjiĹsï}}ɰ~ty~z||zwspvh[xsJ/*(+.0/.03565668;;989?EDFLQUN?679898:=:965<=5687BE>?EKH528:7769954:@A@@;>ACCDFILIMSMLRKUΝ~zq|uvux|vjokkminx`vՕgjgXx˭ϗdjrhjgfkkkkhehkkjjiffjkihhhigfnlihhihhljgeceimmmjhimmj_[[TPL>6QdibM1$!''((''%%(),.,+.328:3-,+(!!9fvu{xtx|}|ojkfgplfqvxvw||x}ul\IIQONMQS[^RQSTZTM]hZ`ehfdeggO@FLT]TRcwp\UueQyhote_fc[douvtxte_irupmoqpnmnnnnnnnnph]wþ¼¾ȾiqplmljkkjhggilmaVRX`jsvqvtuvh\vewvpptptuqortsuturmrvrrwtssyzhjtŰȫô~z{~{~~lefZ@2/+)((//136788;;<;989<77233277--587657:99=AA>;>?BCEFHIJHKRNKSSP͜}siĕvq}zmrz{vnphf`WS[juqz{ӓfihY{ɬʔcjsijegklklifikmlhhfcblljgffhjnmjeegiihigc`cimkkjgillh_]]VROA8TfdaaL2"'(((('&%((,.-*-32892-,*%%Agqr|x}~yx|{nihdjnff{sptwvw{~smjcg_cfik^UPTXRQ]dcdgieabfhPAFKT]UScwq]VtaO{impe_fc[douvtxue^fnsronnpnlnnnnnnnnpi`x½¿ǽiqplmljkghijjjiiaVQW_jrsuwrrth^{esspswtsrssrqrpstuwsrupvtrrxzfhoı̭ĵ~~}syzuxfN8.-)%'0/2565568645:<;<=AEFGNMNSMGPVJfǘzsj̏y{}~jev{svunfQFHFDGJZlҒfihZ|¦ďakujidglmlmigijjjjhcaejlmkfegjjjihggikhlkdaflohihgillh_]\SON@8Sk_XlfA$'(()('&%*'(,.--0474-*-*#(Hinp~zz|}{|xjihfjg_k{oluulq|z{||hXXYZb\RLTXOLSZeeilkfbcePAFKT]UScxs`WtdQyhnse_fc\dotxuxte`kuqopqnijpnnnnnnnnpjbyºýŻiqplmljkihggghjk^ROWcmqotvqsxk]fturrttvuqorssqvtqrrprovtqqxyejºǿb}Ĭӻ|yqljnnkmj_ZK2%+,+02454248=8:<<;:;=:;:63310/036765679;:;==>AEHIJKLOQNMS\QRŒ~{tkswxw{ubblvwvpcUI@><8:A7M̿ԑgmgXϟdhqjeechjjligjhkkiheejnnmjgfhkjhfefhijkjgdehihnlhdejlkc`\SRK:8NgbYcvi3"'&'/+%)%*+,,,/0571,/.*)0Tjkr{xtxuwrcac_^\]fnhgjf`iyurt}fLIWb]SNNSQJP_efrlgfdhkN;EPW^URdun\XygUyhkpi`eaZdotxwwqecltimrpklonmmmmmmmmth[u¼ŷȼhppklljkkmihggie\UUV_ptruwusyi_xcpqqrronoqqoqvtstsoqttwtswstvefÀR~ͨѹvqnovy~xRKRG1&+/-.0367557:79:;::999=<7468721475249:<;88;>@==@CCBFMKGELSSQOSLsʓ}vkܡ}owxvqeecnxtnh^KB<=;537;7^ԑfldTzιΟdhqkfffklkkhegglnlgbemjkkigefgkkjgdcehhhfddgjjmkgdekmkeb]VTM=:PcaY\sxP+*('&&(*$+.-*(-1351-//*(#5Wmou{ws|ssr`[]\WW[fa^be`ahjmry~oYT^_WSQRRQX_bgnheebfiP=FOU^TMavq^WvfTxhkqi`eaZdotstvrfagnpkjosmknmmmmmmmmti[tŷǻhppklljkjmihhhjg_UST]ntuptsrxia}hvurqpoqqqqrrqrsrqrpqutpnrptyjiJ|ˣнzRPVO=-/430147987678899:9867=?:7985332147986:<;::;<>:;ADBDIFFHJJJNSUHXЕwlʸqjmkjjhidkpgdbXE=87611489>:586324984689879:9=99=BCCEHFEGIMOQVLG՗|xmŲ|qhggcdlic`ZVabQ@956644686<;LxӐejbRwŲ͞dhqkfgjonlkgbegkkijighkjjihggfilmicbfkilkfbdimjjgeglmlc^[VRM?:Pba`etq]4)-(%&.*--***,,00///+(+B`sx|ztztmoof[]]X_baS]ghhomagdjzze`giaRP[abi`a`hkfggM8AMW`VPcun]YxcQvimsiaeaZdotxwytf`hqupjilooommmmmmmmnf\u»þŹŸhppklljkhliiijnk]QRYbmpsuupoxi]~xevvrqswsqstrqrosxvppvyxsqsoptdhN~~ŠؾîW^hsucPGC=:9767788889877:<=<<==<:87348956;:96569987;><;8678986898679<>>?A@<:::77;:65899779;957<=9;ACAEHJJJNPQUXEq՗|ylſǷnwpc\agc]SVIAXlZ832146876<9NbYbwqmpgV@/#%7J9(*,-1+*-/21,--)1PjtzzutvcdmcYXXY`ZP[gjlqlfkghlrx|{cg\OMNKHKPU\hifhcS?IRV[QM_ur_WtcQvhkqi`eaZdotvuwte]fsolstjkqmmmmmmmmmof[tºŸhppklljkhkhhijnj^RQU_lqsnturud]}fwupquqsssttsqqtsqsutsrqt{wwwddȿ¿Hv{{zxwuuwvxsko{}}{}~|~||yy{vrĿżýþʻ¿¼ĺ̾Ů}~d_ovaSF?<98::8578:::99:=>?@?=:89999:;:8::878996::98=CB>DFFDEJOPUVDsҔ|xwmɿ~wtme_^`\SPPCJilJ0740/1577A38AEJNUҎdjcSyɳ̝chqlhhglmlmjgjlkhhkjijkkkjhgijmlkhffhjnomhdgmqppnklnmid[]ZTQG?R_[lwccudUK=)+DY9(./+/,/0130+,.+7Ypuywrt}~rdflcYXY\]XX_cflpicfjgktvt{fWPSVUUTSZbifflfQFQRR[SK_ur`XtdRwfjoi`eaZdotqqurcZcpsnpokorkmmmmmmmmqfZsƺhppklljkhkhhhiliZRSV_nqprvurve\xcvvootqpqtvtqorttutqqvustytstaeĻĀM{~}{|~}~{u|}yxxy{}||~yw|x{}utƿѼ¿ȼȼtv|xy}shpzfXJ=;:;=;729:<==<:8>><=?A@<9:<<978;998667768:<<<=@B=CEDDHKJTQAvϑ|xjzz}qc]\URTVTNARuh?/7643465318>?HKEKyӿҎdh`Ou̝chqlhhhmnmmjfinnoqofchjlmljhijoonkiiknttqkhjorqrollnmibY[XQOF=W^`wvV\wgKGK85L[=&+..4.-2241*,/,<`uwxuptwidhfbWUY[WU_[Z_jolgefjmnoui`cccc_[bhkfhqkLHUPO^WLato^YweSwfhni`eaZdotsrvte\ftsmospklpmmmmmmmmqfYt¾ƻhppklljkhkhgghkh\TSS\msrtsooyl`{gzypnqutpmpttoqsurpstorootqsvflÀK{}}x{~{ӻκŽÿ~q}}|}v]^]B66<:895777:=;78<===?B@<:;<:9:<<98;:67:96779:;<===@AGIEIVJBΌ|m}uxi_`\LISWNOK\nS3852456;=6278;DIIJ\͔jncOy˽Нflnigfiopkkomgorqnmjghjlnmiehlssojimppsurlhlruuvtolmljd[ZYYRDCSUdrNWqgLDJA?R_>'++-..-,2..0..(::::<=<<<;>@><98:==<=<:869;;88899::;:9>@?ACLUEUϓ~yj¹½vmrc`XNJQTNPIJY\E3976/.9=72/128BHJOOuҘjm`MwӵΟjqtmiemmnpomlknusnmnljonlkgegklllnoomkouxsmmtzuvuolllj`Z]ZRNKPN[pzbJVnnQ?EHLY_A(+**-/003400*&4ayyvwpty{rhdedZWY\ZZ\[VY\ahkhbafgipwupdECY^NDT[WZfh`SDOTV^VQcvqaYsfUyjnrgbdbZapttrusfahprnmmlllijlllllkjnhatļþ÷ùhomjmnkjhfhjhjml]USX`krpnrruwb_hevxrpuoruupnqvpsqoqrrsrrutqtrimN{ֿ˶ÿĽrzuy[U[TWQ;-26369::87:=<>B>::>>:;?;<:7898899999999;49?A@?ILCuÏzj|Ŷmiqd\OL[bWIOEMWK95721)(272//--5@BFONVӗgi_Myϱ˝hovsrropqsspnmrwwrrsrptqpnmjkmmlptustwyyuniltztwvpkklkg\]WOPNLKbypUFPgpU:;HSbc?'//.,..237..*%Hw|syxnwz|zlcaa`TUYZY[\XWXY]did]bjkfnzxjusPDKNIJHO^ffglS@KVZ\RPduo_ZtdSwhlphaba[cprxvuqgafoommnmmomkmmiimmkqi`rµø¶homjmnkjkhhhfhlj]URW`kqostrsucbgfxxqorrutqqurlptvutsqqsrttrtriqz}|}{|R}|~~zмȲþŷwn~su|[V]]Z^M2*020668759==>AA><>@@:??=;>?>=>;B=9<8999:;;;;?;8@?;K{Đ|zzn~Ĺnc`^CJjp]QOCGNLD=7.-/3/&)/.+&(6?86@MC_ƾђad^P}˶Ϧorzuompwzwvyvnrtwyyqlnqruurmnrsvyvqmqvrwzwpnrywywqllkj\\bYQSL@PwwURQCSgW:.,AkrF(/1-+//21,+*#@wqy}rmp{|~shcVNTXY[YX[^YY]_]\\\[]fmijvzmis|hI?IPLKSbifglT<@HRZMB_tq`WqeTximqiaa`\dpqquvriafrrmpsnhkqnkjjkjlnqf`w²ƼŸhomjmnkjfdhjijli]TRW_jqoqqnrxfafcrssuuqopstrrursuurqqruqrssvsgkwwmiimtpqssrmifnmosxywsbսxɷɮɺǿu^`hw|\X[W_[b_F358<=<==:8=AA=:<9::;;<<=B=9;A=Jq뾇|uti{·~jXMNDXtmWJB@CA===6/062,+)%&&#'5=74^sqaVpcSv~hkohaba[cprvvvpc]ftrklqolnpnjilljjnfc_q÷Żƻhomjmnkjfehkiijg\TRV_jpopqopsaadfuqquurttppsrmqprpkkosuqqrswsgjyxkhnswwvtuurkelmquwxura~Һrîȫ˿ǾĻghty}|{{|hU\d_b^`bVA406;99@>;?D@>ADCA@:>A@?>>>>?AB@??<<<<<<<<<;9;9>V껊uqshyɹveWMQVa_L@EK?:668972374020(&'%'29418>=Cqחeh`Msœزuq{xrruwz~}yusuyzyyvrsvwwvsnorzyyzwrsvxwutqorw|ztooplfg_ZWYOER|iJTVF>eoJ2*,VrM'-2/-0,.100'9mko}tipw}tj[MUc`USVZYXZXYYWWZ^^Z]bliepvihq~~tt~}zmMHQAWkrrrqmO=EKUaWL^ro_WqaPt}eimgbdbZapttqrnb_jutomnmmmjkkllmkkkgb\nļ¸Ƽhomjmnkjigiigghf\TQV_jpnlpptwc`ejxrpsqpprsrqqrolmqrrsqvqqrtxsfkvuow~wnhffnqtuvuvv[}|{иs̮̽ʿȺ½dgy}uv}{w~x[Y_d\`d`_dYA09@;7@?:>>>>>ACA=<=>?@A><>?@<8=A=>>>=<<;;9>?5L㽎}xwxgt¼mc^\g]J;8?BA@>?A<9@AA><<=;78?A<:>?;8@8@w¸縊wrsiwżpdfrdK:3/:C<<37=849<=?DJONHC;2151.01:>JtҒagbPtƙʪvpxsosyzy{zvtw||{|ytvywy}{tswxt|{pt|z{wppyxxtnkmnld\]\SPouYSYT=3[zb?0!7fa.$86++2.4-&8twmyjivugcfdabbOO\`ZYWVTYYVWZ[\Zhun[bshZhzzff{~|xg\gibhplnsoW<>JW]QP`pma[scVxyfnrjbeb\clovwvnb`hnnnmmnnoommlkjihhnh_wŹjnnigikjilkgehkk]USV\fnnspnruechhwrouxoqrssrqqttprxuoqrstsrurgkwq~m[^imruutuvvS}ǯx̯øè»|k~WW_`c_ddccfhaW?58=9<@9=?ABA@@@@@?>=;=:754>@NzҒagbPtȨvr}xtv||z{ytrvyz}~wqtzy{~zsrx{|~}vsv{{|xqotz|yytmjjjhf\\VRonXVXTA6UuiF4%0]]5'/.,./.,*8]vhydgyyrfadb_``TOWZVWVVWWVVWWW[^fqpbdoi_etyhdxurwtcfddoytoppV>?IW^RO`pma[sbUx}gkmibe_Xanupprnc`hppnnqnhjqklnonmkjof[u½»Ź·ùimnjhjjighgfimlj`UOU_lqmlnqvvdccfwrpuwtonrsrrttrvxsprsqrsrrvsinvpjX\imsupnrtqHtx}ohmq{~~{|̸vȬɾvè}iZfaZcfjjjhijihY9/;>:;:???ABBA@BCDA<<>?==?A?:9;B;785//4;9H{ⴈupqgs}mojRB8431471,0;@93:CJMSZ`a`][QG@=AB<:CaҒagbPtǩͫyt}ytsz{z}|xww~~{|zutwx{~zsu}~{{}{ss|~xqnoruzzvolkkhf_Y[mbX[WTH:ImuR:)(Q]=+--+.,0$4Wrmwgp|}yoebcb`aaZTWZZZYW[\XSVXZ[dhigcdmjdalwiat{vpz|d\aeoxvnklU?AHU_SNaqma[rgUv|ejnd`fbYanvqqvui_dmomlmnmnoijmopnljjaVpŸgloljjjgfghhjmkh]URV_jrrpmkqvgedhysorrqqrqpqtwqpuvqtwrnorrrwukkvwxhZZclttmmvyu8[jd`b`UTY^q}~ësǩƼþ»zsaZbcaehlkjkjklnmS<:B>:??@AAAAABBBFGBBC@;;<><;:;>732.,3>@;gݱwrsgr}{u]C6123012..3;?:9BLSTUY]acec^WPNRN@9OtҒagbPtǠΫzv|yrv}}{}{vtw}yzytswx}|ttzy{y{|xuzztrppswyzwroqpnf[_XW`VUM=ACB@?BEIEGJFDB<=AC>97;>=84346;BC?BDB?>BGHDDGGFC?ACC?>QթytugpmTN;52/-+,++-37>EHHLPTY^aehkjihdchcVV~ƸҒagbPt̿ɤut~{{rsz{z}|xwz}}~yswzy{~ztu|~wwspz}}|wokq{yzwrnnkg^xkPW_W\R6'Q]7"DlT&!.,-))Nrwboymjyzohcdaabbacf`Y[YUWZ\ZVVWWX\cZXko``jf`cnh`m{ztx}{}{mkoqojkkkTAEKU^TRbrnaZraRu{dkrh`b^Zclqrtwrc]dnsolmmkijspmjijkmrh[súgloljjjgjifceikiZRNT]jqpppptucbegtnptrprrrqrrrostqqsttrrtrrushpozvso`Ycmnnorvwv3]h^^]XTVU^s}|}|}ḢtɿÿdZ`ba`^Z_`bgjropnjlpojpuyqW>;EABBA@@CFGIHGHFA=B@?@DGHG<77=>>HVҦvrrdkX?>-.+(&()((/6:?ILKNRV\`ceilkjjhfig^kҒagbPt͸̥wu}z{pv||z{ytrw}}y{yuuxz|rqv|z}|wvvyzzuonu|xywromjfgeSY[X`Q/!GrD'>lT$%<63-AfxpeonfmyqfddgecfedghbYZYSVZYZWTVX[^]NRlmWZje`coi^k{xw|}minohdhhU@DMV\STcrnaZqbRt}hjjc^ca[clqssvse]guqmiinqnipolkjklmsh\tºù¶imnjhjjihhfdfjjgZTSV]hqsronqsddfiupormnssnlnporttoottmoprrrwuklr}}sssaV^inprw}|w;dmbb\WZZ\hy~||⸡}vũÿzh[YX\a_[[abjnflknmhjqrnquvzpL7BCB@@ABDECKLIKIB=@?ADEEGJC@DIBA\Ф|soo`g}P;5)/,((,.,+5<=?INPVXZ]__afjjjlkhig`aҒagbPtģɣvu~}tsyzy{zvtw|}}zuvy{xpr|y|zv{~zyvrpruxvxwsqqnk|bUYXYcO+@M+8lP#5P?53_v}|ninoioxria`db`ccbemdVUUQW\TTUYZVV\XNYur\\hd_cqk]i|tkov{whjqjeikV?DNW[RUcsnaZqiUqzjlhb^c_W`nwusvudX`oqljmnkkmjjjjkkkkkdZrɹjnnigikjgklhfffd_UPU`lrpinswtacdjxssupoooprtttspoqqpppstusquqgkwzy~}yyuaXeqsngkx}z'Fmswinwmoywkeddc\ZbgfdmbSWXQXZRSWVTVYYVUXim\ZgcXbmi`dtztqpqy{lisndecT@DKT[QQgwp`YrcUv~gjoa]c_X_krvuwrc]eqqnllnonloljjklkjpi]r¿~emmikkijghikgekh[QLQ\iolqoouubddhyvttpuooqppqppoqsrqstrprrrsphmyrp~vu{vb[fokimtxyxAiqefe_[filu~zzz̸~wÚżļ©rprjjgeilf\Q\cehklnnnmoppnlntutwxdJ=AA@BA>=GIHHHEBC@ABCDIMMDOGD_ʡ~vqmdkaC;831,''**+/17DGEB?KJIJKFCFFHEBEJKKKKKdͥ~uqogmrO@:62.((')./18=?BGNSVXWY[[^beeegljfgigZn¾Ўai`Q{ƟЧrv|xwr{{{~zps{}yzwtvy{uquzw{ztsxyyysjjsy}wuumioUX[[Yd\0F_:*PjUS|_/Bkwzt`Uakmtupkif_efehhda[TLV\Y]]Z\\YVXVPNW^kmccgaX^ji]Ygu}uxtq{xfflghjT=?HWaUPctn_YscUv~gjoa^faX^ltrtwob^hqoommlkjjoliijkkknh\rŸemmikjijkifhffkg\RNS]ipnnonpsfefjytqrnqpmmsrqsrprrnknqorsontqdhywjt|{vyzdVblomhgpzFovilojcgiku~}}}x{ſǽŽþƺoksplmjkopnotcX\jkiiinoonllnqporvw{ypT<;GFFHAEEGLOJGKLMIGMLJOPJgЪ|tqohn_MG;20*,/,,201;AAAFLRRQWZ[[^figdgijhhii]hļ¾э_h`R{ȢŽӫtv}{vrqz}|~xow|yvz{tp|}{qpx~vsz{utz~{ztkku|zrtshorUX]^\kf7E`>7ZiX[}U5Xxs~iVCOW\fliiifbfc``]VRPLJUZWXXVWWTT[[SQW^iibcf[X^in`QUruurnt}jfnkhiS?@GT_SNarm_YtaTt|ehma`gcY`mvuvuncbhnqomkiijklkjklljhlg\rŹemmijkijjgcffhmh[SOT]ipotqmoredfiyrprprsqoppqupqrroqrqrturqvsgn}{ljxqqi[hqrlehryyEvpjjjiddit|usz~|z{|}}}}}}~||xty~²uyŽĺ|¾¿˻}hnpnqplkljhlsiXXflkmmmnnmkknpilou{xxpLAFHJKPTSY\Z\bfffffhkjii]bþʎfk\JyŖĽԧqxxsuuz|yy{ysw{xu|}vq}|}~{srz~~{ywwwxxutqlnv{xsvqi}tTUZ\]rp=?yfFCave`oNGkyvnbTAILJNQRV[\]ZPHKMJHPNOWZWY\TVVST[ZRTUYce`dgXUYem]N[z{|{wmlu|qkrmgeUDGJT]SN`ql^YsaSt|dhma^faX_ltyqqod[ampomjihiklkjklkigke\rµŸemmikjijifcfghlg[SPT]ipolfelsegeiwrpsrronmmpttopsrqpqoqpqqprofjwzpkzys|f\ejoneahoqP|lhkkhigjwztw}{|}~~~|}}ywy}zuyyv|~}}~szȽŽ•}¾ĺmipppxruslijifocaillnqmmmnopmkirsswww{`BISTURRV\\YZ^UWXWW[XPTRS_bbffZTVelUIa}~}|wrps{wkljhhXILNU_UP`ql^XsbUu}fin`\a^W_jqsprqc[dsonmlkjjinkhgijjjid[søemmijkijigfhgfidZRPT\gpoqmksub`dhwpossmloplorqrqsuqorvtqststqiiuxtowwr|]\ljprmkpw{Nynorkannnv|z~yvvt|ƘĿȼ{kmppqprutnlmicnkhhjiimonoprqomqsroquwxzqTAFEAIFGGHKLLOKFGGBENTn㶈|wutknu]YI941,)""(..1;BECBEJMLILPW_cdgighfhlhej`X¿͌`h^NyǙø¦us~}oqw|}}{uov||{}yst{z{~vty{xz|srwxyyslnuvw||nmhTKLQ[I-Okp]TgxbQOntrlacdh[VY\UMNNHEHHJSYXVWVY]\XUXVVVVWZZVXUU]acf][VUbnYI^zwyysoz|leejkWGJKUaVOarl]Xr_Rr{cfke_c`[dnrnrvob^itrnjijklljijklkhehc[remmikkijbdfjhgkgYRPT[footqoqrceeiwpmrsnpsqonnlnqsqosxxqqrqpspfpxurvz|wwWWmoomnrx}~Dwqnqmhlpor}z||~y{ȸÿ˾λ½ſúmglokntrsmlopljkloljoomoqrrqooqttjilmtzy~~hMGHFFFJJJMMKKQNIKOKRho{ű㴅zyvrghjZZH50)&.'%,2.1Xe^_bflnihmbV~ĿˏllVKw̦sy~xuq{~{}}wqrz||}xpqyzz{xrpt{}}ztsuzv{{qjpwut{j}|rg_[VfV.''4Seafpvdctmof`cWRTLScdSKUYPSUVWYZWTZZZ[[[[[[VTUW\ZRPTTV_d_YUWPWigOPk|~|xqqv}}qcagT?AHS\RQ_on`]rcStzdhlg_b]V_ksvture]ervpkiijkliijjiiijjf]t{`ikhkkhiefdijfggWRTRYippnooto^gdftqoppoonoruroqqppqttqoooprxsgjyz{|vtwuqqwq`\hsqoqv~Cnymmnopti^lz|{|nqvy;|ʽürfdekpqmknpqooooppqqsphchopoomkknprrnoqppqtv|z}{fMDHKJHDBDKPLJMRSWh|rtytq廃{{rpdeseXK<30..0//**3=CFKFFGHKLIFNSH?L\[`efgkgdkdUǿľĉgjXO}ʽŤsu}{wrq{~{}}vps{zwxuoouvx|{rouuvy{xtuyyzysmpvwu}tjoincb`n[2$',8QdmvwZi{j{ya\[JFSZ[caTQWWPQWWUWXX[VXYXWVX[_WTWYXTLQTV[aa\ZWZQUgkSNgyy{{wsszzh`eQ;>FS\SQanla]qbRu{dhjc^c`Z`jousvrd[cntnijkihhlighkljghcZq{`ikhkkhifechkghe]QPS\imnmlkrq`ehjwtopppppoprssprusoqsptrrootsjg{~{|zxtxtu{xe^hppnlr}@pzjkqpkti_mzy||y}xz}ux~dzϼƼzgghmojopnkjklklprrpopsuslcdlpnmpqnlnqsnpppopsuptvyxeMDFGHIJKLMJGINRWdrwrz~to}纄~{sqef{ttbJ<;630'-/*+9@@DIINPJD=515BMLHRaed_bkgdm\R¹ʍgiVP~ȷy~ævw~}ytq{~z||upu}|wuqnrqpptslktwvxzxwx||zwslnv{{wqqzyeiqcehpi9"&..>Yo|q\ntpsa\WHJ]hcf`WTWTPU[XW^]UTXXZZURU[UQQX\\VRRSW`gaZXWZQSdoYMc~xvy|ywtu|zlbeR<=FS]SPaom_\qaRu|egic]a]Wamttosr`^ktrljmomklmjfhkmifmg^tödmmijieeggbeffig[LKR^koqqnkstdidfurpqrrrppqrpnmnrsortrrsrpottlly{yvzwuw}vb[fptspvEuqswtmyn^e}z{yrszzv|}zǹûibfjimrlonnppooportronqtoqpg^epppsspnopmopqpppsusryysk_PMLNRSPMMKLQUYdpurtyywqh鷁{sqegxx~jNAC?<4$#&'0@FDHLLPMB;96999ESPP`h`VWZUT_ROϏggVQ~vw~ztq{}z|{toovvsqjejqonpoggpuvwwursuy|{rjnwz~zuw{i]xuehlltB"!/13Ksriqxhzi`]WMS`caicVW]\VY\WU]\TR[WX\\VX_YXY[\XROTQS]d_YZUXQRanXG\|}zyyuwusuyqecV>?GU^SO`qn]YsaRu|dgic\a]Xantqmtq`[flojghkklmljjijiignh]rµdmmijieeegeeecih\VVSWejipposp`jghurmnnmnpopqrqpmorsstrssurqtrhnz{}xy}{vuwq^[houvu{Fq|tuupmrl_g|s}wtxqnjbi~}z~}}߸acfjkfhkjhgjnolmpqrssqppqrqrhX\lrkqsqnoqqpqqqppru{vuz|vpqmdZVVVQMLKQWWWduuuursuuqtw鵀{srfhyr~lVJIED>/%(.9FIHJSSSK>9<>?QMDNSU`h\PIB>DOLQ͌dfWTŹ{pv~}wrr|}z{{tntvsqrkfkonnqpihoquxzwttuxy{ulmuzyyptzZrylmnfozsN'*2/Coto}zoz[VSNMYb`af_WY\ZYYZURVXTUWPOVZWUVUWY]]VNMVRS\d_YZZ[SQ]lYFZy|yxutwsszug`W?>GU_SN`ro[XsbRt{dhjc^c`Yajonqurf`foonlklmmmjklkhghikdXm¸{`ikhkkhi`gghedgeYPPRZhmnonjpn`hfhwtrssnrsplmorsrstsppstuutqsnbk~~y{||{ztrwsc]iptvw|Gq{qturoqi\e}x}}|wt}ofaZ_wvozxx{ÿݢt{ztuj|y_dgeacijkoljknpnmnlnprrqppwrqm_YeskorpnoruqrsrqqsuywsupwrmjgaWOKINXWS^qvpswuuzsmq|·流zsrgiyt{ndXLDEFB76=FIHHGORYXNGDADZYHDITdfZQG>HX\HY½͍fiYQ|ɽyƷßpyyur|}y{zsmsupllifkkkiihfipmosvsptzzwwvnkszxwnu|wv\ivqqldfmmZ1%-0=Xqy{ulVPMNR\ddge\VYVRT[XTU[[QFEDGNWZYWPPT\`VMNTSV^c]VX_]TPYj\JXu}yxwtxtt|xiaT;;DU_SNbqm\YqcStzchlg_b]V_lsuuul^Ybkjnoljkkikllkhgghng[p¶|`ikhkkhiafffffhc]MJR]ilnrnjqtekeguqmnnpqqpqqpnqtsrsppvqpqqsvqdlz||zz|qnqxud`mxyz|BoximutmldZh|y~yzud[\Y\r}xruxx~}žſnq{vmnfedagoidnkiimswvsrtutqpprtoqvn_arvrnmoponrssrqqsv|qiowz|ssssrngaXPS_`Zbsyvv{}xv{~rsts㶂zsrgivysrdN@AINFAHOJHJHMOVYUTTRKPROICJZj\SF@VkeEa¼Ňdj[RzŴy|›kw~wsr|}y{zsmltrljgehklkihefkmmszvoqz{yvpknux~zy}sfuosqjefhib;'$(17>X{yu^XTU[^^_^de[SY]XVWVX[\[RGINRTWZYUWRQ[`UMRQRV\^WRT[YROXi[IRp|wuwytt}yjeQ88BT_TOdpk]ZpdStycima\a_Ybmssqwr^X`bjppjhlnlllkjihggng[pŶ·dmmijieeegbbdgidXQQQXgmmklmsq`gfhvspqqpqrppqqqtuomttoqsqporvpbf|{}~|wrsw|vgk{xwz~Gqz|wolscXd}v|jqpb[agit|xkrv{{x¾ݦ|tlqqojgfhlmiellmprsqostusomorvvqqskgounmsrqvqqpruursvkRFQ`ntrrw{{xtqokklrtjfousrrstrp~urttw|roceu|wbgk`LCFGENOIHPPIFJLS[]Z[^TPVUKFJXhZ@5.4RbQiҿƾȆcg\TyƬ|ʠp{zwvr|}y{ztnorpjfdeikjhmoc`mpstsrsuvuwxsmqy|yyqyvy{mjibolE)%)/3-W~~tZWUYYV^gcabWQ\]WXZZXW[[WVYXY\]YVVXQR]`UMLRQW^]YVTZXPMXcZIRm~|vzzwuuw}xgN>9BT[QO`pm\XqcRnehlf^a]YblprqspaWaoprjfoqllllmmlkjipc[qøųzflieijhhgfeihgjd]TPSZenorolpo`idjwppurssplnrrnqooqrqprqrurosskn~|~{{~uty}{~Cr|zurssdYcz{zyfgm`Zbimv}optuy{}żᩂrure^fhijihghqmnsuropklmoqrssqxtossnmqroqqqtsututqwvhQE@DIU_air|}zyzqtuyzqlr{yusrrrrpptxxvw{}|sqdfog``caUF????OUQLQRIAIKQY]^afb]_YLDER\TD?98GNQw̽ļľºɈdg[Sx̮~ßqx|vtyywxvqonqpnlgcciliijeclnnquxvspwxytlmrtxuo}mmlnalqR-!*/18g]SUV[]Z`d_ccVQ[]UU[XRRWYVTZ[ZYZ\[WVRV^]TNOQR\e`WUXRWSOZf[FQm}xuyvutu|zoT?9BS[SP^pn^WofTlybimc\`^Zdotusto_Xcrolmmgghfhijkkkjij_Xoĸbjkillfeacehfdjg[SORXdmmspkom`jfkwonroopqprtsoppnotsqssqqppushp}}~{zxxwDmytyyrood\g}y~qgjk^Zcjlwqcaovyy||}z{migcbbhmkebejljlqsqprusqprsrqtusswrigouomrststpwxttjRFEFCAHUZ[cpy~}~~vz{zztpqtttsstvxsuwwzytn峄}usfgz[Z_ddWLEA@GNOYYQTWNAJLRX]_ejfa`WKDERZUIEC?CCWŻƼþ¸ʊfh[Swʬ{žqw|uswyz|uoonomjifegklhfhfisnpuywstyxxwpiluxyqht~jjkftb5 /-.IgQVXY]]\ac^b`TP[]VUSVWVVTSU][XVY]]YTTY^ZRORTRZbZMMTYXPISb^MSmzz|vyxwtty{vZ?9DQ[WP`ol\YqcUoychie^b^Yblqqpsn`Xanpptpgjneffhijjjika[q¸ðwekjfjjhhfghjfdheZRORYdmmonlrqahdivoorpoonnnooopqqpqrrrmnrsuwpbm|{wy~Gmtoy{sjsg[d|~z{}|roslbcnlhxgQakswyz||}m[ht~mbcec_ahmkdadikoqqqrsrqqpqrsssrtomrtkbmtpnpqsusmrzscTHFGGFCFMSSV`nz~~}z~yvvtsknqttvy{}zuux|yuov沇~~vuhij=RkcVHELHEO]MWXSUZSFJNSX[_bf^\]ULEBM^YOLMKIHgüɿʌhh[SvȬx}Ɲn{zvtqx}zplqsrojdchljimkcbkroquvsv{yzztmnux}xqh_yvikgrm?$1(/cwUW\^][YZacaa_TQY[TTWWUVWUUX\VRUXZYYTVZ]XQPRRS]g`TQU_WOLS\ZPNhz{sy|{wtw{{]<7EQ\XOdngZ\veZswdkje_b^W_iorrsoaZcompnjjnlggghjjjiine_t¾IJzgliehjhidbafeegb[SPT[fnollmusbfgjuomomoooponpsrnpqosuointvuunbpxy}GtwwwtqviZc~}w}~xqpiep}sburgSmlmrvz{zw}}}}ҿþbD]~jYajhdcehjifcdfknoopsutpprtvuroqqopvsh\irtsolswurprlVINKIHIHDHQVV]jw{~xux{xrrrrsuxzzurtxwusrkr׶}wuiic,>\WB7AQKAJXLQRORVRFEKRW[^`ac`[NFA>J[ZVU[ZQNпǼɍihZSvˮxěnzzusoz}{rjmopnkddjigilja`gllouuqrw{|{umovy|ztnid|yajrqH(*$?_R]^^^ZW\acadaXSWTPQWWWWXUVZ_YVZ^^\[VVZ\XQNPPS[]WRTWZSOQV\YOKczytuz}yvw|_;5DS\WMdmgZ]vbYuxejh`[`\V^jqqqtoc\dmokegnjgljjkkkiihjb]s~cjkilkgegedhggicZRPT\fonnmlqq`ghjtpmnnqppqomorllqrmqwsoprpptqfk||Fx|wutsuiZd|u~|~}rkj``qu`o~yZ`unntxtqtxwwxyz|~ӿVQi^]dghkkhhiihfedljikortupooruvspvqovysf]jppsrpsrqxrmk]OSPONOLFIUVX`koqw{|{wrw|zyupprvxyssuwvsqrqwiw}|vvihi/-FXH8?QIAKWMNNMPVQB?GPVZ^`_he\KEB?KX[[YbdYXϿøǍjh[Uvʩunw~zrp{{wxyrlrqoolfbfgiihfgimimtwsnqw|{yqjmvz{}v`jlhrpQ- *^}`QU]aX\YY`d`]c_XUUSQSSVXZXST[YYWWZ]ZUWVY]YPLMLV`_USTTTPOPT]^RLbw{xowywvw|gA5BT\TM`nj\[r\Vtzdhea\a\U\homorob]birlhkljinkkkljjhgja[qŵwdkjgjkggefghdaeaWQOSZfmmmmkrqagghtqoprsrrrpnoqortsstvxspnnrwrdizڀHy}{zuptjYa}}}{}}}skjjbbo~ykv~WUr{rtwqkpwstvwxxxxz`xrc__`bjlcijjjigedqomlmosvwtrrtvvtttrosyrdhqoovurnotojpm_URSPMMHJTTV`ikhmwssxxtw|yxsoossoiepzzutvwqtrp~|zuuigo50LaT=CVTOUXNQSRWb[DBIPSW\__bd^PMG>F[^^Ydj`e̾ǺŌjh[Vxƥqnw~zrszzvxwqoopqsridgjkmkhgiioqsutstv{{zrjkruuyrkcxmml]6>dMP[_bVZVVae`^a\UTWVTVYVSUYVUZYZYXZ[ZVXVY_ZNHJNV]YRTZZRMORT]^TK`v~}tpvvrqqwvM7@SZQN_nl][p`Vqubjle`d^U\gntsrk`]gpnllkijkihhijjihgnbZpxglidhjijefgjgejgWPOS[fmmlllsrafjjtrooqlmoppnnorqmntsoprpoquyrbl~ހJ}ytng[d}}~vppojqmr~}ZVuiqtrpnstvxxxvuz~tbcXX^`fmkiklkhfeflppiflsvutrqqqrsrrstxzpaautpuspqumdfqvjZWXRMOOOTUV_jlhipqqy~{||wspstm]ONg{{vwxu}lgnu~ztuhgr4=`cM8Daf`ZNNTYX`phKHOSRTZ]]ih\IFC>I\ac]hodjʼöċjh[WyƩs}lzytquy{|}xqqmmllh`_fjggieadikqvuqpsvxxvpimw{y|q}}t^`|fhe@!PoSPXcb\XZSQ]dbbc\TTWWTUVVVYZVU[\ZY[[YXYXUZ`ZKEJRW[XRUWTWMLRTX\XPbrv{wuxtnkkq}W:>SYOPani\\s}aVrxckoc_d_W_ksrqqj^\fnmjjlljiidefhhhhhnaYq{ckkhkkgfigfigfhaXQPU]honomjon_ghitsqqtppnmnopqmprrolntrqqqrwsglՀJ}{}tgY`|y||}~~|~~~|}|ulkmknkrzq}xwwfjpmsloqrqrvwttx|»๝xYV]YWZ\^bggilmhcejnqtpijrwsssrqprupusswyseamtnqvojchnopqpoebgfZWYS_WS`njcgqsutuy{yvnnxv_NOJTdtytrvwoqsox{wtvnj?+HOB?QljTHINS[[ewI>MQQVUV`feYKCEa_MGNNNOKHFIQVRT]\GBB42CLSfhcfkamżſÄe_DEyrdjtolnoqsttrpnrnkklfdglmljiffjnoruspszxtspifjnnji}k`mhfqom{ggkW][[]_^\ZXZZZ^^`d\URUYXVV][WVYWVY[\^_^ZUPVYXZ^RDDPZ[URQNKLMMRUWZUO\lv{~}stuqv|uyuQJXVPcwn[Yqw[Urybfgc`aZU`jrnkonb]ekjlmlkijjjiiiihhglaXpíxbjjfghfgkeceimj`VPPT\fmlmihpo\ffjwplqwpqommqrqmpssqoopnnqqryugm؀Glwwzp\a}~}~}~~~~|~{{~||sotuliZW`_blkmyjlmnpprqpssmlqu{Õskc]\YY[\^dh`TY]adiopmqolpskbdltzxwxvqpnouvxwi^dmjhpssvyyvuwxwrvwuu{xn`[[^[]lznxqfo{xqnwywwzzy}}xvwuruuvupqvywwtqfbua0;G?SnbHTxvVFNQIEFEFKRROPVRBA=))>JT[bhce_jɾƼĂhdCCxzdkuohjjnquurnlppomjedikkjhgdejoopstppwqqpjcfkkmlhxxk[mphsuiowo~^X_Z[\\``XVWX]cece_XRUYYVXYWWYXXY[ZYY[\\XTWWV]`PAEU]\SRURLGKOUWWZTO\jrv{rrywx|zzaPVSO^qiZZs|\Vqxcefa_b\Xajpplnl_\emnnkjijhfiiiihhggoc[sưsckkghighfeffehgaWQPV]hnmjmlnl`j|hnwonqqppoopqpmrtsqopruqprqsytdtրHpzu~}yxq`_v}}}~|~~~~~||~~~~~|{z{{vmiiklZT[knhvxhmklrnnnnqrmlrvzĿѠohaYXZVXZ]bfc\^ZY]dinsrnpxxmcaikpvwspossuyvvwmdlywoqtvywuvz|{xyxwyzvuztj_]\Zbptvkerztmr{~zzzyxwxzywwwsrptxtpu{z{xukfwR1BAFdmfennTPNHFHAFNROKLNPGC@98@FKJTikf\kȾÄkkRNxxgiqg^_gjnqroljlppkfabhfhijiedgmnnrvqnsvtpicfjjpngpowWsxrqvmst~j__d[\a^\^^ZXVZbddgaXSTVVWXWTZ]VW]\[VTW_`[UVWW]`N@EY]XOQVTPFLQVVWYRP[gou||suzwtxzznYUPM^ogZZp|[Vqvagja`b[Wajpplol`[dklljghkkhiiihhgggnb\vŮpbjjfhhfgccedacd_WQPU]gnmjommlbk~hmvonpnmllnopqrpqqoprsrvtsqrwp_oԀCp|pnopr{oaew}z{~}{||{}~}~|y||ztnkhbca\fpnwx~oflnrrquusrpoq|z̾Ͽ{kb\VUXUY]adgfc][XZ`hlmmrutrqj_jorsvxvpptrrrsxxqqyxqqtwuuvxwtsusyww}ysw}se_b_]dupa_qyoirtuwywvxwxwvz{xtrrvzupqwwwtrjet~}P;JAStnlof]UNIAJTVPLMMSRJGJHGNKNLYgh[l˼ýȋkk[X|tacmfaeghjkllkkoolhgfccfhiihedhnmlpuqosvnhgdeiknlcjqXqxfr{uuYY^Z\a`[[^\ZYX[```d]TTXVWZY]ZZ[ZXXY^[Y\`_YRTYZ\]PBCSVSNORPNHMQUTUXSNXepx~}|wopvyw|u^TNM`of[Zm{YUrt_hndaaYT_ksqmom`[cilnlheghhiihhggggj^[w®qaiieggeffdeedgf_VPOU\gmmlmlonajfgtolnplloqomnrsnlptssvsqqqsysbr¾ـBpzpspms|tbe~|{~|~}~~|yvyz|yuqf_cdiiurh|wklnknomqppoptusppxz~Ė}wmc\VW\U\bdefe`a\^\`hghsuuuuqidclrrsvwtqpooqrstxvwyumpzvssuvvvwvvvuvwvtuzwkaacaopY[xwhiwxv||w||xxyyywxyrsttsqpprz{vmlx~}~~gSMHStoi}|gSDAMVSLJKLJROINUX[_YOUfebk˼ļljhfVT{x^cqkcbhikmnmlknnlkjfdfkmkhgedfkhowunnrrlheadkonf^nrRvvz{vz{nYX\^^]^^^][ZXV\cccg`TUZXST\_YXZXVXZWVX[[WUURRW]]M=CRVRQPNONMPQSVWUVSVemv}}yxvtvzrtziZOO_pj]Zs{]Tuuaeh_\b[T^lssnol^ZfphhkjefjlgdekmighkaZr³ɵu`kihjfgeffefdfibTPMQ_jlghjjonakghvtrojplnqnmqsostpnprtuuutswo]sԀJv}qx{xy|vedx|}~}|~}~|~}zz{~|vojdcghcrzeo`qpsopqnnroorttrrtwzɤuqzp[W_^X\[^dfdcee^YTZghjstuvxuoiigmx|wqqusqqrstvsxzwuvvuyvtuvwy|wsqsttsst{}ukea_kkW^xvmuyywyzy{|v{~|xwxzsrtvtpnozzuqhdq{{z}u`NHbtimus|wkXEJTZRKHGCITTNQWZ^bYMN[`epͻżƈgfVU{u~`doiaaghjlmljinnljgccgghhhgddipmoqmijmpnlf^`fjlh_m~yUzy~sc_a^]^`^\ab[VWW\ddabaUTXYXVY[XX[ZXVTVVWZ]^ZV[TSZ]M;?LXVPOOPKJNTUTTURTXflrzz}wvwxvvswr]KK`nfZWo|ZRsucgk_\`[Valpqmmh\\hojghhghjhjhgjlljjmd[sµƯq`lhhifgecdbdcehb\UMNZinmkmlom`ihkvqosstpnonnonqsrpoqssrrssswp_z׀Gu}ptyvsvqcbu}}|~zv|~{~~{{}}~{wpifgecWbnq}pktrimpmpssssrooqvwcuxjY[\YYb^^ab_dlhc]W]ikhmpstuuohegmrtrppstrmmpsruxywwxxurtxzxrqqrtxyyxvuvy{{ysg\geU]son}yxyxz|ywxz{y{|ytqprwwrpquwxxlbo~||}|tbSTTZhqkjw~}mTDFO[]N<;DLRSS\c`ZaYQNU_jrտ¾ÆffWU|p}_dojehfgikkjhgjlkieabgijjkhcdkpopokjmngjlgacgfhk_mq\|y{ud^a_\bbb_]`_XZYV\gd^cZRUWVVW[YVVWXZYUTVXY]_\WWTZ`ZA4AOUQQTSOHKMRSOQVUMUdlrx{~xtsvwtx{dLMcmdZVm]Ttuafh_Z_[Xbklooqj]\delijkkllhjjhgillii`YrŪįr`kihjfgeggefegjcWTQT`jkflmlom_ijmwokoqnnmmpqqqponmnpppporqswq`|ۀGu~ooqppok_bw~~|}~x|~}||}}zwwrikkeda[]{kmgnqghqoqrpoprsssur{έslv`Y]YSX]_cecadifba\[gj`enutrsndajqrrvupsroljlnpxyxvtvvvquyysnnrtzytszztrvxz~~rehhZ_lko~}yytv|xwu|}zwxuqsrsvvsonrwz~sen|{~{q`NVKSon_nxqxrSCFN^cP4-6AEFJVcc\`\VQScrwҿƿefWV|kz`enhcgdfhiihfedgihgcbfijiigbbhmmooifgghjhdainlem`mkb}}~|oebZSXecaa`]YYYXXeqg\b^TSVWXUW]ZWVTVXVRVZ\[[YXWV[^ZH8?PTOPTSOHGJQTSRSUNVcmtxyz}wtvvssvz}jPSenf]Zn^Uutadg^[`]Xbjknmoi]]egihjkjiifhifcdjjgjaYséo`lhhifgeggdecdf_VROR\hjgjkjnm`k~eivqnnmgmqtvrlmppoopqrrppqqqvo^}߀Gpzomnorqndew}}|}|~}}~|{{zvtmghkdcliYuxmnhnmknptrrqrtvwvvpyfyq\V\]ZV^^befdeggab^Wcoc_lyyuvrghgiovxwtwrpqojmsswxsruwuvvwvtrv|wzyuy}xlnw{ww{yrnpebkow{xzuw|wtrx{xxzytoqstvwtptuswtjo{~yx{iKGQ\oi\n~mjt{|\EIS[[RD4(07=NSNNTUPFDNQSWOB@MRTams|{}{zxuuvwtx||_I`mg[Wo|^Uut`dg_\aZS]hmilpiZ[hokhhhfghfjhgijhffkb[sï®q`kihjfgebcbcceibUSQS]hmlpolmj^hhkvqossolmpppqqtqpqsstusstsswp^߀Ists}~|smt}|w~}~ǽ}}{||y|}yyukhkjipink_gvj\Wl~wnrsmnnorsrtvsoxxz~agYs`YeWW^caaeijoi\YZannaZevvpomfeejqvtqutvxuootqpoptyzxwvvvtsw|~zuruxxuztu{{tsxwuidjv|twwtyytwxyvuvyywuurpqtvvtpsqy}ss}|z}|uz{mwwYCEOix\KKPUWXVMCGIPSSVO=?XYSf{|v͹˼ghXSwq\ania`degggecbfjlmkfeighhigcdkegjhehljkgec`bgimd^}tea]Lzum_]`]_e^_le]juinypa_cWVZZVWY\XZYURUVQRY__][YW\[XUZVE=ITRNRVQDIMNQXYK=ALSdos{y||~}wrtzzsvyiPcqiZVq[Ssubgjb\^XS_ilfmshXZgkllmkffjkhdeijfccg_YsıƯ°m`lihjfgehhffdeg`VSQRZejjmmjlk_jghvtrojllpropqosomorsrsrqsstyraހLwuywfdx{w~{{||~źvr}}y~~ywwtqppoljlpifypX\ɣ|vnpomllsrqqsuusrssuy~zmxYlv\[ba_\]adddeih_[\cppeZ`kvztrfcfkmqwzttwyxurpppqrtuvvuuxyxyzwxutwzwvzxutvwvtttmcdpxxx~xx{{zyvwxxyywutquutuuw|yqs|{su~{y}}tzv~yO@BAciGHQSQSROMKJJQYWRRS\\XjzrþÃeeRPxnz^cmhb^cghhjfa`cghggdcejihhgdceenofbghcgfeddeginecyi`YZHwtzugab_[bdmz}mfw{gisldbZXWVXYXWVTXYVTUWWXY\_]YVUWSVZ\TB;NQNNUTLIKMSTSTOAFJQ[ht|{vstvrqqu{}oYbmj^\qy[Rqschha[a^V\dioopk_[clgknlllkhiiihhggflaZqļƭs`lihjfge`dfigfg_TQQT[gmijkhmh\mflvqpoqmmnnnqqolpqppoqurppqtyq^݀Cmwnortupphjz~}|}ųht|x{|z|xrmllgacnflxmztooqpmlrrqrtutstuuz|~mtjg}zia^][`\\bfffhli^[Y^kpj`^cpyrojedimsvtuwvtsutqnquvsprvttttuyytwutvywuwwvvxxvsq{m\[l{}{wxvwzxvwzxwvustwsrtvux{{qpquwuuz}zx{kUUXVa{PCE@TzlKFNPPRRPOPOKNX[WUT___uwv¿ÃeeRPwr[akga_bijhfbaefijjidaaggggebcegcbeebejiheb_`cfkddrf^[TJqsysgdeb]bso`jsiklcfgYZSPUWUUYVYZWTTTTXY]_\WTUTRUX\YJAIRPLRSKEFLRSPQROWNN\lsx|xwvvtnlov~wd^jj^Zp`Wutdgg\X_\V_imolkg^]elhihhhjjjiihhggggj[Wvk`liijggeegghddf_ROOT\hoknnimi]pbhsopprlllorpnqqoqrqsuttrqqsxo\߀8anggdehlngfw|{|~|}İfo|x{~}|xvohhkh`cnin{joyzyunnrmmqpqsvvtrsqu}sqmki]a[Z[[Y\beeehkg`][\irlc]]kxtmfdfhlrxwsrtwxtsutpmqtsrswuwwvuvt{yutwxwxwvuuvxxwxj[[o~~wwzzxyxwx|wtvyyxvwtuvuvxvwwttwtsw{olqhS<3=CF`q_LCG@DgnRCJLMPRQRQRNKSYYWd_\e}qsɾļ‚efRPwqZalhcadiifeaafeggffc`agghhecdgiihggfdbghigddgjidevhi`aSQngqwpghhc_q}kbprmoj_ei\YWWWYXXWWWWUTUUTUY^`^YXZXVXWYXK@HSTPSSKEHMQRTRPQTNNYhsxx~{xxwuumosx~l`gg]Zo^Usrafe\Z`[T_ikfkql_Ybkkihhhgijihhhhgggmb\tõæg`lihjfgeghffccgaVRQT[flhikiok]mdktqrrsqspmqolnnorqlloqvsrqswnZր4\hdc_afh_S\w|}~wz~öǔafy||}}y{uxi[]ceddfzsuwwxnouronruwvuuuuuwz}vqu^`_XXZWZ^bddefhhdb_^grkb]^gvxocdfhiqwyruvwwvtqlnqrpmpuuqs{{xvw{ztsvxvvxyyxxwustj[Xhy{vstwxvz|yxvstxzwruvsrtuuxutsx|wvzg]ZYO?757=AIMJEBFB?XhTAFHINPQURWUQRUW\\Zdntxts˿Ľ‚fgSQwl}]dmidbehfefeabefedecabghjiebdgihgeddeehigb^_elidejbhZ^LRr\]oumehga`|wgfvqkom_`e[PTVVUWWVVVUUVYZZW[ad`\\_YVXVUSE8EPTUVQIINPORVRMPNQX`hryzy|vvvtypqsqz}rhhc\[pzZQpqbfg^]bZS_iiknpj^[bhihhifccghhhhghggkbZp²÷Ĭk`liijggecedfceidXTRUZejfeiipl]kdirlnmmoromopmmstsqrqprurrqswn[΀5[f``\_dbWO_{z{xtuz|ĿƔhp}~~z~zv~kZ\]dffxnqoqwonuutrrvvrqtvtyrt{z|}~``ga]UV[^`adhhfikib_]blob^`bo{tjedfjpustuvtttsrsrrsttuuvqrwzywrvwvuxwsqzz{yxvtsxp`Xev{wuuxxwz|xvyzvtwxvvwurssuzyvsuvsvdPPPIA;>;12BIIJCCDEDMWM@CDFJMPWZ\\[[XY_^alihrslʾĽfhTQvk|^emgbabfgfie``fiihhebbghihc`addgheceghbdfdaafkidg~caeYcJZveOUntjafd^ctcfvmksqcdh^SRSVWUVXXXZ[^^^^\_dfa[Z\\W[[ZVHCC@AEFKTZZYZ_]ZZZ\numghpfiUQvnz\dkeaa`figea_bcfgdb`bgjiihebbdicbfd^`hjigdcdgkfallee^^unOvd[KLirh^dcf{k]nxgflgZ`dUJWbcdgfaabefd^\]Z\`dc_^_\W[ZWWK<>OVSUSLIOLNQTTROMOZabmzzyz~xuxxyvuxz~tga_\n[Rqp_ccb]`ZS\fkonkd^agiijjihijigggghhiib[XpŨi`lihjfgeefefcdhbVRRT[fkhgjhnj\l|cjplrqoqsqnoqpnqmmnopqnsqqqsxp]̶߀:clfmpjacjfgwy~woottw~~~zwz{kgejrpjxumprtvtwvqpturqtttylajl`[[][Y_ijfgmljife^Ziqp_[_bluqebhjimrttuxxwvwurqrrprvrsutruxvxwwxwst{wsuzysu|wtdV`v~yutx{wuxz{yyyuqsyuwvvywuvzwvrpyzlOV\UE??=A<416=>=ECFA@CB@EC?@CDISW]\Z]_^_\^nkZdssȿҿgiVRvju]fnheedgdcec`bdikgc__chgffebbchgfffeeejhda`chkc_o~igebczsV~a[MKgrh^eemi^w~zoqpb`cZR_jkd^_ccbcb^Z[_]^aeea^_]Y]YV[R@@LRSUOHIMMRSMOSRNLYfcfptzvyysuzzts{{z|j`_]oy[Rqrcgh_X]ZT]iqnome]^dfjkjiiie_fgghhiiii[TpìĦg`lihjggedeccbelgROOT\hokmmilh]o~ekojonknollpnlpnpooqqqsusrqswn[}һހ@ivtyytoszj_ru}ywrls~|wy|}ϼس}}yt~a_mlln}|xqklprurrrrqonnqsoos|`hljd\X]Z[^ejebgknke_\^dmm_[_bnvrffgfghrttussvxussttsqqrusqoptxzuuwyxvuvwwwwwxxxzveV_v}wwvxzzwwx{|wv{zuwyvtwyxwxyttwsuymYUWVTMAA=>;548>BA>BDBCEBC??CC@DMVUVZaebZX\_VRkssȻºֽfkUMsjs_dkgdb_befedddbfgeb``cegcaeebeihhgdacfhie`_agnb_p{gadlvkV|eXLKasg]_hzlqvushca]\^^XYcfc`^_feeb][\[[`dff_[]]^\YVYUB=HPPTRHIKHNSOLQVSKR_ehotvzztqquwvttx}~od]YoyZRrp`ef_^_YS_ljkpodZZcljiikkhgikihfffhii[Vs«±ƭĥl_jfefdfdbhhgdfh^PPQRZinggnllh_o}bktmpqpmoonpsropnqpjkrrrotqqxm^݀Cp}u{{st{j^o~}}~}zzols{wtw{yxy̸޻xtzzhZ_oron~q`krpikmpsqptutpnnnmnot~l]kogb`\Z\Y^cbegbjmkiheabord]^^iurhfefkmtyrptrpswrqrwvsruuttuuvwxzxwwvutuuxywvwvstviX\q{xxvuwxwxy{zutxyvw|ywvwwxyxrtwps|q[PTXWVNG==<86:>?CB?BGVVUUZ_XM^h[]dlnrʿø~ciXRtir\djea`dghhda__defgfa_bkjdab_]bliffecdfmkd_^_bggdvuTRhzyzmXNVhvokq{~~vjdeech`]_]^aaYYab]]]a]^ab][ZXZ^acd_\^^\Z[YZTA;GQSSMFLJKPSQPRTSLQ_ehntz{{zywuupsvvuvz~|sh]Wn{\Tsp_cd`]^YT^kkgfki[Yenlifffehkjigfffghi^Zui_jfegdfdafeecgkbSPSWZbiilkhni[m}bkumpqppqpmmppntppronoprotqqxm^݀Bs~pt~tsyi\n|}{y{xonw{|}z⿰urt^WUV_ie_jglvzkuyoiloswvuwxtoostrsux~}[_lpgda][\\\^cfdaeijklhbalrf\][bpric_cjlqorutsuupostppstrttvwwwvuvuvy{xutxzxsruursxn\[p}{wtstvuvvtwyxvuvxvxxwwxyyxtstos{s_PW]Y\WND?>:58=9:?>?CA=@AAAA@@BELOSUSRW^ik_a`bhn˼þdjYRuir\fmd_`fhihd`^]edcee`^ahihhga^cihijjfdciidbccbdigwcFTtzrii{~trz{gd_\^_]]_d]]_^^_^WV_a]\[^\\`a^\[X__^]]YX[\]\[USTI9GSRTRHGNPOMPRTVSLQ^egmtux|}}ytqwvvuvwxxzym\Vm}[Ssp_dea\]ZU]ilkkngYZgniijllihhiihfffggk`\vţf`kgegdfecfdcbekdUNQY\`gknjfmiYm~bltmoqomopnnopnspnpspoqrotpqxm^݀Ar|nr}~vpte\rzvxurj`gw{zx{yssYR_`S[c]e{skZqđplprtwusssssvxxuwyz}^]cjmlh`[^^\Y^hfab`gkljfbcgog^aaervmb\afjnsssrqsvvqstsrrqqqqqrrsstyvuwyxvuwxxvuutrvyo\[p~|utsuwxwuzwwxwzzytxzwuvvuyyqpsuvsbW^_YYVRPDA?43:<>A@?BBACCC@<;?CCAMPMPY^___ab]a\d̹տgmVOtku]jpe`cbeggecaaeffhgb`cegfec`ahffffc`aecgheddiplShjHbzwxuoix~li|jd__a`\]cd^^b`__]YV[]\\[]`^__[[\[Z]^`a[VW]\ZXRTUI;GRRSSJGNQNNSTRTRMP]dfksxz{{|{xtwtqsx{ywz~q[Wo}WPpqagia[[ZU\hllqpf^]chhhiiihgfhhggffffn`YsǤg`kgfgegeegeebcgaUKLW_dikjkglg\n~blulopolnonmmmllsqklstprotqqxm^݀Dq}ssspkrse^swx{~umtŪ}r`RVebM^gcqsdss^Бzmlruwyvtqpruxvutxwz~fTaekmljb]_`Y[cghf_^flmlgcdjrl^[Z]jzqb^cfjpsqtwuqqsprsrrqqpttsstvxyvtux{yvtxvvxwuuvxwm[Xl{{vuuvz|{yvsstuvwvvyzvstvwxxqptuusd^^\[XRTXJGG;05=C@AA?BFEAB@;;?A?AEL[rwhZcd^\^_VeϻʺѾglVNtox]ipe`c`cffeccccffdb_`cghgc`_ciihgfcadhiiecghhjO7`uUqzxxukduf_{sb_\[YX[``[]bb``^ZUZ]^_\Z`]]\ZZ\[`a_^^ZX\W[[XSTUF~xupprtuvvvvtzjVY`]_eimprof]]_\]hpmida^agllienri\W_hpul`_ackpstuurrvsssssstvtrruvwxwwsqruxywtuuuuuutuz}q[Yl}{vuvtsyyyywuy{wvxwtuyzwuxrlmqv|q^]bYV[ZTV[YSVRBEKNLMRPGEHIKKFFN[_ZUWZ^fc]OOžҹy`nVIro^ckgc``gifefdbbdfeca``mjdig\^cjijhfjdT;41781++ &^t`~cmrirxZ{X_a\`[\pve\`a]`a_XTUWWY[\Z][YZZWUTX]_[VTUUTSWWTN?6@SWQMHGJPOLNQQRSLKWcbcnqqtvwzzssqrqpuywy|zhq{[Ssrbik\]bZRYckmkkf\Y`fhghkjgfgbehhfddeg^Xpµúƞh`lhgheecgfdgece`WONU\diikdbniZncktknnlmoqollmomlkossqqrqsmpwka܀>ʼ|xssvxxvzy{bZ_`[[dhmprqjb\^ZYdnledeb_cjmlemsm`X]fovpb]\bnnqsuvrqutuvtrqqpsqrrruwuvtwwstwurvxvvxvrttxrb\l~xwxzztrvzxyxvxzxzxvvwxwvvxtlilt|w`[c]UZ^WR]]PRWJFINLKSTGEFHKIBERZ^\UX\^_`_KLøµӽsZmR>ji\_kcVRYdc`db^aacdca^^_dgchfWW^daflgWC352495)##&5pt_qYbigtsVyU^c_^Y]ptaZ^]Y^^ZVXWWYWV[][XZ][XXWZ`d`UONUTRVVTO@7@TXPKGHIOONOOPSOIHS`acmpquxy}~wsqokjqwvvv~~op~x\Vvo]dh\Z^XU^flllnh\V\bifgjjeegdfhhfeeff]Xoû¤Šj`lhgheechfdgebd_VNNU\chhigfmfZobktostpjmonoppoonponnrurqsmqwk`က>u¾ywxyy~acc^Y[dimoqqkd__\[dppigea^`fkmflspcX[emwse[[blnssqrttuttpmmrtstttsrvyvuttvvxxtsvwvvxwuzuvueZe{zywuuwy{yxx{}vsxzxwyzwuuxzulfiqxxe^c_ZZ\YX\\TSTRIHMNJRUJBCINJBGUX][VX__[_cNSúԻ|_cNSw^drdIB]igdkgahghjihghibnknfFFfrsmZA2*&5<=5(&.7/9ldfxR\jjqqV|Y^b_^X]mrd]`_\_^]\ZRLIGHOXWUWZYWXXY_fcYRTUTQUVTO@7?RXOIFIINOOPNOTNIGQ]acmqrwzz~zwurmkrywrry}qp{vXQqm\cfa]^XT^cghijg][bidcehihhifggggffgg^XpǪȟe`lhgheecdhfdbeg]ROOQU_higihldZp|`hslpqnonmmoqolpnprnorpqqsnqxj_ހBbzxxw~aff]Z_eimnooke__]\cnqlle^]_bgkkmpnbVZghuwi_^adpsroqtuvqqqpswvrtturruyvtvutxyvtxuuvwvxzxvzxfTa~|zzz{zxuxwx|~wswwvw{zvvyyxtkehqvyh`cd]YYY[[\]WQPMFKOKOSMEELRLFO^_`[SS[_[`cOUĹԽz\`KWz`cphK?`khfmgajijjifefhfgcoiEEhtcK5(#*4;70,/488 +bjewScvspq[e_^^^[]inf]_`_^\_bZQICCINUWVUWVVW_\]`_WVYTSPTUTOA7=PVOHFJIMOOQONSSNKQ\_aiprwyx{}xusrnlpursu{|tvxZQn}n^ccZ[`YS[fnjhhc\Y`eehjihgd`ggffggggi_Zq¹µƥši`lhgheecbedccfi_RQPMQ`jijeeogWmfovklmlnmmmlklmnnonnopppptosxj^ F_}xx}bikc[^eimmnnlh_]\]_gnmnf_]^^cilkpqdUXdjtvl`^`brqprtsrvwuqopssrrrssqsvutzxuvvtuyutwwvx}vtuylZa{xvzyvutvvyzxyzwyxxywuw{vvsldhqw{k_dh_WX]\YZ\YSQPIJOLMPOJHLNHGUc[[ZVSZ`]_aV_þտnSfJG{xtwJ5&-513AF6,3:6*"+iod~tWj~wqm\|i`]^`^agid[\^^[X]^WVUPQVWSWWTVYYY[YZ^]ZXZTRPSTSOB69KTNIFJINNNRROPTPKQ[`cjru{|z}|ronnklnmrvz{uzy^Tp~o^ba_^`YS[cijgfd\\bgcghffjhdhfddfggfi`Zr¤ği`lhgheeccbafedgbWRONUdmiheemgZp}cmthijjnmmnlkmpnsojlmntpptpsxj^ѿကDgz¼}|eioh^_dimnopolfabc`dmqnje_\\bgjiosgUUamruobY]dqqprtrpsqpqrrppptsuvttvwvwuswzytwvwxyyz{zuuzt`\l|uvywuutwxywvz|x|zyxwvvxvwwodepu{pa^ebYU]]XVZZWUQMKLMMOONKMMFHVaWW[ZVY_^]ddi¿¼ӷnXgLR͉ɫlG,13499;B;645/#"4mw[xp]p}yxm`q|zl`^aabhhdd^^]^]Y]ZUVWUUXVRVUSVZZY]]`a`ZTQTRORTSPB56FPOJFIJNMLRUQMQNJNZ`dkrv~~yrqtpmoqpswzwzwZRqo_df^Z\WV_finlmi`\bgfhgdehihhfccegfeh_Yqµƙ``lhgheeccbaedcfaWPOT[djiffdjf_s~cltlnpoqonnonnmopmkprpooptptxj]ʲр@ks»~řgekgagdimnprrpgabc]^hplnkaZ[bfmjmpdSTcknvvfWYdorqoqrqpnoqsqoqusqtwtrtvwwxuswytswyyy|{xpsywa[l{y{{wwxuyzxwxxwyxxxyzzxvwz|rbblrywdW`f]RU]YW^]UVPQMJLNNNQORQKMX__[\ZQR[^_daUpǿļһlUhOR˾Ƿ_icddonknrledeaba`__bec`agX>2(72-3>;5971(  "7q|a~sgw~}tiqw|s``cbdmja^\[XYZVW[WX[[\\[YZWUY[ZXZ\_`a_[XTRORSSPB54COOKGIJOMKRWRKTPKNW]`fns{~}|squqmotprw}{|xZSslZ`d]]`YS[dkdfjg]Y^eghhggggfhebbdffef]Xo´»Ûf`lhgheecbfdcaeg]SLOZ]_ejhb`lj^rdktmpokkmnnnoonmknpnsvroptqtyi]βԀ=`sÖhgmbadgdinnqtphfb]]agljomd`^]_ikptiVTblnsreVWbloppstqoqrrrqrsurvtrtustvsvyvtvuvwz|socD:Nr|xhVj{~{vxxuyyxwuv{{u{wttxzzyyuun`bqxuwiZ`h`TU\_\[\[ZVPJIJMOPROONGRdbXV^[STXaaYa^YؽkUcSTķθŹlsrhktuvz|vmmsoooooooorkuuT4.04/'4B<53*("""5n|xfx~mw}rvuja__^`v~h_`]\[Z^^WY\XTX[YW[YYZZXWY\^^]]^[WRTTSPRUJ:4CTOEGJIHOTPNOMLOIKZ]X[owx{{strsrloqqqvy|}s\Vo}j\_f][^YU]cfefjdXYceejkeeigafffeedddf_ZrŽěe_jfdebcbbcbdcdg`TMLS\fjfgfgjgZsz`lrommkjorpoppomlorpopooopmuwhcˬۀ>gtyýfhngcdghikoqpojc__^\bkmolgb\Y]fhosiWUchlqocXZbnttooqrrquupnqsssussutrttqrvvwwuvvbI60+.>f{}mXdx|wtwwwywtv{yvvxzywwwxzzwwyrccmpxzl[_hcXV^`\[]^]YQJKORTUTPJKN[g`SS^^UTV^a]^`Z_yü¶ٸjVfUSɹyei_^ifgjmlcYUWVVUUTTSSX]b\G6,'.-&090(# 6q{p{}uepvzmfc]Z`g`_}g`[\__[\\WZ^[VWZZ[YSSYZVX^[]]\]^[WQRRURQRJ;3@TQHHJLKMNKOSQIMIKWYV[qxux|y}~{xuplprttwwy~wYTm|i\^dYW[WV`hmkgki]Zcjkiijjigfffeeeeddf^YqºǾ¾Ğj`kfeecdbcdcdcdf_SMMS[eifgfhki]w~clqnmppnlmppmkmqrqqqoorqqqntvf`ǫڀErxoҾĻxsyehljfefjhipoloohddb^ckqqolh^Z^gkpriVR^hmqmbY[blpojmrsqmrtrqsspssrstrqtqprstvwtuJ,)#'456ZwzlY]t|yy}zzxvuwxvtwxzzyxwxyzuw|tfdlov{qbbjfZSZ^ZW[^\^ULJNPRRVSIIUag_MO^aYTT[]d`\`[]mɾþϹiTaPMyyktjhoijnnifgkmiihhggggckl`K4*-.2-..$ !7wzw}n]jnrj^\ZZ]l`bd\^]^\Y[]YW\\YYZYXZZYWXY[[Z\]\]^\XQPPTSPQJ<117MTOJJKIHIKNOOSOHL[a][``_WXWBozff\tsdfer|޶ɱpazty~}ȿv}whpo{s_e_bghkfefccikimqqojfcbbcgjopg]\[imop`T\`]`c]UXckpqoonlltqqtsopusoqsqqrqrttsqmnv}f07bzxm_]p}|zytuxrrwxwvuwxyzzzzz|wuxtjfmsu{wg_ekjUR\]\_^\^``][[][TS`aX[`]OT__XOP_d_Zeup^|λڷgRaNK}i]xlWaqspqvwqnpmmmnnoookwsZA5346&!7r|b~zh_eZ`j]aa`e^\]rS^a][ZZ^^XVWYURUYUZ\\[[]][]^^[[[WRPSGBJPQMA;35CONHGKKJIEITPLIHGINOORKNORciQ=EV_ihftrotx~zi^p|k_aeYW[WT]cghjhbZUYcfijgefijfffeedddgYYtŸŹľěe_jfdebcbbcbdcdg`TMLRZcfbfdfheXqz_inljllpjglppnmomopnoqrrrrntue_|߀Eiyadzbkuպݼ{cvz}||~~űzgoozdcfe`afihheccehlqqqomkhfffglrqng_\bkqq`R\bY]d\W\amopqpoqurutqorrpvspnopqrnstrqssp~k;/Wx~rf_t~{}zuvuutuvyzvtvyxwwxzywxynip{xzzna`ee_QZd\YY[X`f[W^d\SQYa`]][WX`]XWQ_aXYhurax»Ĺ׭hOaKKyiWljakturv|yldegdcfhcafefgVB6.2<)!7msb{on^f]]d^gb][`VcmYbb_ZWX\`b[ZWTUVUTYY\][Y\a_]\\[[YSUSD@JRSOD;58>HLIJHGHKMMLPNMMICADF=AAADOiT2/<;IVTNS[\`mwk^rk\b`[[]UR^ehihgbZZ_bhhjhdefcbdffecdek^[p|²öÜ`ahcehcddfdbcfea_PMMQXbgcbdfjfZw{akplhklkkklllmonmmppoopnrppotb`倀Dmq[}wdu}ɴѲtZkoy}}xxz|ɧwqqnq\igffebcfjeaacflrqonnlhgjfgkmllh`ZajooaS[\V\cYT]emprqonpqommoqrststqmnstsrvvssvsmwmA)Qt}pcV\agt|wmwvuuuvwyxutvwxyz{|zzxojqvuwyreaggXQ]aYZ_ZV[b_\`c^TPXab^\`\[a_YXQYb^[hxu`tķѵmRbLMdQgcW_ipllpmd_a_dbadb_dihhR;4.69' 3gq`vwn`f]]f`gb][\YmiZb^][Z[\]^YXXXUTUUXZ\]\ZZ\a^\ZXZYSOMA@JQQKE=649FOLNJFEGKNOQRROJFGJI?;:<;8DN3!*'.48349;EXda^r|g]c_^]^UR\ceijjdYV]bbdhhdeebbdffdcdef[ZsͳÛ`ahcehcddcdbadge`QNNRYchdeccig\w{alpljmmpmlmoppnmomjjnponrppotb`Iifhue|z°ԸpTceu{{vw|{rlc`fa`bb`elhdbbceinkoqoookedinnmnkb[bknodW\ZU[`WS]dinqonprqopollnppptsmnuxtsusoqvwr|rH3N_]UOSNMOVdqwxwvuuuvvxvuwzyxwsx{{zqknrtvzyiahl\TW[ZZa[Y\_^[^d_VQW`da]\YW]^ZYSUb_Xauxcr¹ƸϳnWgRNwspr{~|zuvxtpsyxw{qccRA4)67%.`n_nnah[\icga^\Y^}c[a_]ZXWY\_YUX[WTVVZ\]]\\[Y\Z\\\\ZSNTPLMORPB>935DONKIHHJLLLLPQKEDGJKEA=@@>GL7#'-+.20/233;ISSTmo[]dZZ\UR^fhehkeXV_g`adcbfjhbdffeccddXYr¯ŲŴ´Ûaahcehcddced`bhe]ROOSZdieecchg\xzampmlookkllmnpqlmoqrpnnnrppotb`ۀJgj̭wg|z~޽ֺrUcdw}xwsѮwut^hggd`_bhmgdbccdgjnpolnqmgeinonole^cknoi]^_WY^VU\`hnqnmrtsmqsqkhhjpuupotvtrrqppsuuvzW$ %3:FLJDFMNKOPKPevxxwvuuuuxxvxzyvvv{}}{umjrvv|~n_djf\NW_UW^^\]^[]e]WUZbea\^\W[\YWPQ_^V[owfqƻɼѯkWfRLvm]{ohipnnoi_WUZPWaXMS`G-5AB4%:;&  ,\m`hochZ[mgg`_]Xc`\^^]ZWUW[^]UU[XUVV\__]\]]\[XWWWYXSQWSQOPSO?=;76@KNIIJKLKJINRQKFHJKJHDCEEGPF.$-.*-1.26536>CMZr}l[X\\[]UQ\cffjleXT]eegiifhifceffdccdhYWq~²Ÿ¿ęaahbehdddbccabgf^RNORYdidcdejdYwx`lollongjlkjloplkmpqnlmnrppotb`܀Dkœ|j{~ӰϲuWefx{ztq||ƛxĦ~b^gdhga^aefjfbaabfjllouumhhhgikklmj`biloj`_`XZ\UU^bkprlkorqqklplbepsuusqqrtrrsvtpqu{\)6CBIHIKKJKNNLQXTOYgxwxwvuutwxwuxxxzxyxwyxsnswwzn\[^caQQZRO_]Y\cb_c_ZX[bca_b`Y[^\ZTNZ^\]ksgoǼȻͲkU_MHox]AU^TPPQONNNKKMEF^b;%7J1";D4%7>' .[mbf}qcgZ]qkg`a^Zfy_][Z[\[XWXZ_VVZYTVX[_b^\\^^`]\YXXWQMSROMNTQ@;::7;GOOLIFFHKNRSPKKLLJHDCIONNM<&'/**0..23115=EO\pwgXZa\[]TQ\cegijdXV]cefigeefccegfdbccjZXq~Ębahcehcddgccdbcb[PMNQXcgcddeidYxv_kmjknlnonllnnllnnlmoolnrppotb`Fpڱ~l{âٻtVcduu|ppy~{Ыz̽p_ee`aa_\^chjfcbbadgmmprojjnjghkmool`^djmj`_[WZ[RTahjnoljmoongfjjdfnrtvuqnpstrrttrsv{lN;DRPHFFHLONKHOOQVUNSdvwwxwvutrwvuxyx{{yvvxwpkqvux{p]TUU`ZHKWY]]\^db_a`\Z_cc`_``WW]^\VSZ^^]itlmƺĸȴmXaQLltdKXZQRWTTSTTTUVMRdX(*7%!6=3+44#!1[kbewtbe\_uph_b_\is_\[Z\][WVX[^YXZXST[X]aa][[\^]]\Z[WPLSSPJLTSD<9:68DNRNIDCFLQRQONOQNKJJNWWZ^U,")*&05../.-039BKSiwiZ^iYY[TQ]egefgaXW^cbcddadgedfggdbbchYXuȻŽ÷ėbahbehdddf`adbcc]PMMQWbgcgdbge\xv`klhjmjmppmkmnnlnopponlnrppotb`ကH}ɚ}iy鿫sUaaqvroyųջx~ǯidjdeedc__eligeedbbdgmqpoomjkhhlptrkb[aini`aZVYXPSageillkjkkddfgihfensvsooqsuurqsslcUMFJMFADIMPPNMMNJRRUXML`tuwwxwvuqwvtyzwxywwz{ysmptvwzudSNOafJE]d\\]^_^_c_[]ejd]ZbbWT[[XRW[^][gunmƼ¾źʵn[dUOkrrU]dbbb]]]\[YWUTUWE%$% "7>201( !2[g_csxac^axsh^c_]ko^Z]]^^ZVUY^]\YYZTRZ[]`a_[ZZ\YWVVXXSOOMLKORJC=;;68AIONKHEFJMQQRUWVTTQT\`Y_dK(','(11021/2655=ESmuf^a_\[]TP[bdgggaWU[_egjjgggcdfgfdbbbgVWuíźĖbahcehcdd`[_bagiaQNNRXchcgdbfe[ywbmlhjmigmplikopnkknnkkmnrppotb`ހ@جzetϯtU``pzn|ylx|٨Į|ͱ{nceh`bfhfbbeilhdcbacfkjjmpqnklhffktskd[`kojbc^WWUOS^adglnkfddbaabcdhkirvqnqtrtyyuuqZ>6AHGGFFFKPTUTQNKOUOP[QJ\stvxxxwvvzurwyuuzvvxxuqnpswx{{lWGPgqXK_d\YWY]adfd\[chd][bcWU]_\VTZ^^[gshoȿҺpY^PNkzu[]_\[\^]YUTUVVVYXN9#&( :D42/$2Zd]aoy`a_czth^c`]mm\X`Z]`^YVX[]^YX]WQW`__a_[Z[^\\[Z\YSJLMMLOTM@=>=9:@BMMLJGFIKJKPVVSSURV_d^hc6%&($*-(241.2965=GMgte[abZY[SQ\cfdefaVT\aeegfcefdefggdbabhVTrŖcahcehcddfcec^cdWROOSYdieeeehcYxxdnmikniknokknomnonllnnmnrppotb`ڀ;âygwỘsR]cumuymt~~Ɣźͳ{{kklf_cefc__`chmlgcb__bjkgfnrrspnkhghlnk^]gkjfa[UWZUW^_aaehfcbdaabdfghjpnorspoqsslu~a?@ACECA@BEMPRRPMMOOPORRFFYuwrr{zuwwxxuty|yxyusw|wmjz{u{p[K\plTUgb_YY[\affda_bfa]`fbXY_]ZVMU`^YcommƸýѲlWdQKqvtV\`]^XZZYYYXXXPUUPN<&+$)-?E3.7% 0clY`s~~y_e[Zyk^]aftk[b_\[[[ZZZYZ^ZWZXSS_]]\Y\^Z\^_\Y[YRMOPMJOQME@>?<7:AIJLKHFGJOQQSYXTSU^idelN)!#%')+/2110//26:EIavhY_`[Z]XT\bgjgg`TU`fgfefgfecffffeca_bVXsŻļǚd`jecdadced`aacf^ONMPYbeciccgfZv`ipmjjflomhkpngmoljorqqpnpppt^bကE̴wcr{ѲuU`dr}mqtgn||ݲŠ˃ɿ¹}fcccah_fe^\_gojjfdfd``gkjjpsqrnnjghnomo`^ipne[[XURRZabcaacc`^_baabdgiknoppqqpoqrrriQBFBCDCA@BDKPTRPNJECPNHICJcvwvwwtqsvtuuvy|{tyzuruurmquvzv_]mm^S_j_[^^[]acdbeccdc``c_UV]]\YPT_b]eqsmÿϤbQcRLqvtV\`\^XVVVUUUTTUSSORS@0-(*;?668) "-_o]\n}y`d\Yxm_\bjuiZa^\[[[ZZZYW]\ZZVRVZZ]^[[^_]\[XVYYRRPQPNPQNFA?@=954?A4?ewquzrpuxxxvrr{}wwz{yxyvqtvrr|~tjqlTK^pm^\a^Y\cedb`_cdb_`gfWRXYZYRR[a\aknnȸѬiUcOIrxsT]`Z[WWWVVUVUUUOPTSD=<>BFHIHIJJHILLNUY[`d]^th4!#$&'*-0110.-.035@WkcZ`a\WXTT_bcdejdVT^ceghhfffgbcdefedceYYtóķ½c`jeccadcaa`cbcbXPNLOXbfdcbeidWvcinkjmkkmmlkmmlnmlljmonnqsmmwa_؀J}{qy}|ȿ~jqlu}~on}opr|}}}ߵürwxмج}jgfff`^Y`a\\`dfilkhhgcaa_dkia`elilnlgiomd]bjjbZZWNMX^^a`_]ZYZ^`aehikmlikjlopnnplrm[KB=:>=<=ADHJKLMPRRMH94402Uzyqsvqryzzttuusuuvwutw{|yqvtrzztzo[T[hmc^]^\\aedcbcedbbbdfWQY\^^UV^a^dnnhְmZfQIrzrT]aXZWXXXWWVVVTOX[D!>^H5=B32;'"%!5al`\brrb~gd\Zxzuhal}zqj_X^\\[[Z[ZZYY[\YXWXYYZZY[]^]`\[ZZ]\XSQPOOTTNFA<:;<@DBGJJIJIFIMNPVYZ_og]aO$! "$$&(+.110/--/15;OfdZ`c]XYUV`deggicVT\_dfhhfdfgcdefedcbfYYsƺüÔb`jebdadcabaccbbXPNLNWbfdbaehdWvagmklpnkkkjklljlmmmomlnpnppou_aրM}vpwyz辂qxozwu|jlr~~}uu~̴بuhgdbe`][_^[]abbhmlgggdc_\dqqieedfhhhklmgebfmkb]XRNS\]\_]_][Z^becefdeijhjhjopmmpnscLD?;=><:<@EIJKJKNQQPPLE:5B]puwutspswwtrtwusssxwtsuyyvtuwww|}tfc__]dngZZ^a]_dfcfhe``a]dhYQZ\\]WX^a\bjimѨhYiSJr~rS^`XXVVVVVUUTTRQcZ-:[\NF?007(;dh^_akra{mc[[x~ulgozkd]Y][[[[[ZZYYZ\ZWVXXXTYYXY\^ab\Y\`b^VKNQQQTQIEB???@@AAHMJGHHFFJKMU[ahsom]6 "#$&)+010/..0157F^aZ_d^YYTS_fihee_SS]abgjhb_aegfdcbcddfYXr~õµ»Ĕa`jeccadcccacbbcYQOLNWbfegcceaVuakpjhmnljjllkklqnhhmompokmnnr\`̀H}rnt}{zؽ辅v|qv~{npw~Ǻθ̜ndf`]a]YXYZ[]Z\bfllijic^_\cnpld\_cecdillmh`cnm_USMPZ_^]\aa_\\`cdfihdeikihijmnnnornW@==:;A=:;?DGHFILLMPQQHKNPMIO]jsupqsuusuywtvxvrwzwuuvwytuxxyxqXTZiidgc[\_a^]`daceb`cc]cj[QY[YZURZ`]aikqΤfYhRIrrS]aVWVUUUTTSSSQNXI'!+>LRQA05=) >eg___gm_vrcY\yunorxg_][][\[[[ZZZYZYYZZZWRUYYXZYZ^aZUWZ]]XPQSRQRRMDCAABA?=BKNHBEHIJLLMU^hrpqvT!!!"$')/010//0255?T\Y^`\XYSR\ejccf`TS]bbccdeddcedcbcdeffXWp}óø±³a`jecdadcfeababd[QOLMVafed__cbYyblqjgjiijlllmoojjkmmklommomls]^ȀHqmt~~ѯ辆yrp}yjs{}ϽϿ̿gae`[_ZTVXVTUWZ_eihfjlfa_\ajooh\^bdcachmig``ii`ZRKOY[^a___]\_dgfdhhddhhefjlklppmtgM:;>95D@;;>CEEAJMJJOPNSMORNKMM[oupqttvsstsopuwvwwvvxxwvqnt}zstiWWptbZY[]\\__`fgaadefd]]gYPY\Z[\U[da`hlbɪjYfOHsqR^`VWVUUUUTTSSNV]K2#!.A;/9A%#>dg`^\ef[oubX]{upstwd]^\]Z\[[[ZZZYZYZ[XUUUVWWY\ZX[]YYZZ[\ZPOQRONNLID>:;>@@EMOE>BILILLLQW^fksn? !!#&(/010001334;MXY^\WVZVR[cghhi`QP]ecegfdbcd_acefffffXVp{·a`jecdadcgfaa`bd\QOLMVaffe``eeZv^gomklhgopjgkljlghllmnlmqsmmxb_ȿ̀Jnrwylt}~}qt|Ȓ|¾|feb_[XWUKPVWUSSTcehknnjg^\_hprlcZ^ccadhgbgecgg_YQFN][Y^ab_`a`cffffeddfikgpslgimmrY@899:=?>:99=AABKLFCDFGKLLLQXYUjwZ( #&)+.--/231/26=HRX[]WW]WRY`ehde`TRZ_^gkfacff_addcdfi`RXwƼd`f`bfbeecdbbaad\PONPXac`fgbdfZxcnsnhjmjkiilmklifinlloqnssnqx`bЀNx~ñt{xxzoqums{{яy~̾jjhd_ZWTSPMKNTWURY^ejkjhha^`gmpmf__acbcefghb^ehaZKEP`^Z]^_^`cdefdcdedcejoqsqkfgkneQ=7889<:;:<@DCDLEADGKR[WOLQPJKTN^pvrqrotssssuwywruxsruuxrqvyywqlddg`ceRW\]]`_\\`bca`bcca^WR[_Y\aXX^]dnhf;Ľ˭o^gPKurTZ]VUWUUUUUUUU[LX_>(=<27>)Dfa[cbac[fm^`yzz~uuwg]]Z[\Z]][[[YWYVUWVWURPWWTUUUX[YYYY\[VTQQRQRPKEC?;;?@@CLNGCCGMHJLNRXYYgbD! "%)+.--02320407GSV[b\Z\VQ[chb]aaTPZehfgjiecedcccddddcTYwȾedibce`aaccbcaad\RQOPX`a^dfaceYw`ipnkkkkjjjkmmnlmnljmnjinmiksZ]ǀOs{{Ž‹sts{}|wlrwsw~~В|Ǹ}ımnje_ZUSQPNLLNPRSSYbhkkkkga_chmnjb_`bbceeffa_gkaUJFP]\Z^a^]`dfhgdhgd_\_hpklnomgc`SF:7878;9:==?BDABDEEHMSTTMLPPIIONWlxsnontsrrsssrutvyxtu|xrpswzulrtm^ZhjNR]_\``\[_aa^]]^^^\WS\^W[]V]ia]jkfϸüүp^hQLvqSZ]WVYTTTTTTTTTMW[? $&#-?=16<) 4U]Z^`ec\`om^dy}z|xsrd\\[[\[\\ZZ[ZYYYWWZYVUQUWVVVXYXXZZY[\YQOQSRPLFCB?<=@A?AJMHA?FPOLJJNS\dfM/!$')../03332657@LV[]ZWXRPY`deaecVP[efdgjgaafheccddb_fVYvĹǿdagabfbddbcbcaac[NMMPXadacc_bcWw|^gnmklklkjhhhjjlkkkkmmjknnjns[_ƿ̀Lmzyx~˹̕rnqyyxvzzspy}pݺΏ҃rmnke_YVTSQSSOJINSTV[bhllkfa`dimlga````dgfgf_[bibVBFT`_\]^`^`cehheec`[Y]goolhfb^VPD>9:9779<<@B>AB>;?B?>@>@?@BGIJIFIJFGJJGAWszssywuutsrsuv{wvrrx|vrnpttrqppgc]Yit_R]`^`^[]Z\]]_a_]_dbY\^VWWZX]^dsih˾֯o[eOKsrT[^XWYUUTTSSSRVUNNUI1%+$1HC369(7ek[^c_`ZW^m}lbmz{}~ojbYUTW][ZZYYYZZXUVXYXVQPPRUTSRPTVZZX[^]WTTUTSPKA?=:<@CBDJMKIGKSWSRQQPW_W@3." "&',.013344722:FQ[bZVYUT\ab`ci`PO[bcffaadgebcdcbabceUWt~ºü`dibce`ba`bbcaabZONMOW`b_cb_dbXy{bmokillimmkmjhliklkkllmrrrqtw_eԀFˢ»|ƿ÷xnokd^YXXYTSRSUUSPTUVWZ_fkfdcccgklf``eb_ae]``]_f^MBMZ^[\`caadfddca^]\[ZYZ[]]XSTWPF9:;<;988<:;?>=>>?>>BFIHEBGGCCGFBA_wxtvwtutrrrsssvuuvvxxxrmnttqpplc]a`chbX[[]_`^^`bb_^^_^\baZ\^Y^bf[aa_mmbɻº˰nZdOKrtV\^WVXVVUUTSRRPNOQTVNAABJO=2::' 6oz`[de_YV^hqthory|nga[XXWZ[ZZZZYXYVVXYVUTQPQSSQOOSUW[[XZ][URQRQQOKCB>;>======@FHGFECA@?>?Snystwtsuvvvutssruuvxtptrnptsqpnjg]^d_`e`\\\]^_\\^_^]^__Z[[Y\YVb~\Ya^knhȹβpZeQLsvW]^VUVWWVUUSSSQQWWQSXVTV[S:0<;& *e}eZaf_ZV\fit}op{nz{ob]YZ]XY[ZZ\[XVWV[][YTRUWVQLKGGOTVYZY\^\WSSTTTRNHGC@@BB@EIGABGNTXYZYYWSN98<<4(! #%'),/1113539:8>O\^ZX[TPYageac_SQZ_fcbdfca_`abbbbcceUWtľ`_e`bfcff_aacbaaXMLKNW`c`b^^c`Wzz`ilkmniigjkjkkhkkjkmjhmommnrrZcӀKƖmlvqg^WTTUWWVUTSSRSRQUZ`celd`cinojacc`agf]\[[Y_h]CDT_^[\`dgfgd]\_aY]`^YUUWXURQOJA;88889;;;AB<9>?<;;88=@@CJQF><96:Ci}{ntwsvqtutqqtxwuvvtvyxqqstpoomlf]]\[`ba\\\Z^b_\^^^`a`]_[[]_XVhnV[`mhdŽϵr\fRNtwX]^VTUXXWVUTSSUSSTSPQUNNVV>1:8&*^xf`b[_[V[dekztpyp|}l`\WVZY][ZZ]\XUVX^ZX]XPRRPF@DCEOQRVYZ_b_VSSUTTSNKKHDCCA>BIJDDIORX[ZXXYTL8:>B>0! #&')+/001357:;<@JV^UU[UPW^cceg_QS]_cdfhihebca`acdb`gVXtù]chbceabb_aadbaaXPOMOV^_\`\\b^Uyu^jlhjmkmfjmgjnghkjhlnmlollnqqYbрFŸƈkusia^[WUWVXXVWZYVVUSSV]beihfcejmmifb^]ab_Y[\YZc[DCX__`bfdeb_^_`_^ca\WY\YRPWWPH?=CB=9;=88AG=:?A<:><<969AHKQJEC=9CRm}zrz{tvvutssstusqsussvxtpqpmrvrndYX^_`d_]]^]^abaa^^`__d_X]]`^TapVbbkhgҾ¿˯r_fPMttW^]WYXXWWVUTTSTRPNORWZTPRR?6;6$!&[wi_^YZ\[\chik~ut~wr~xi_XXWX\_ZYZXVXYT\[YVVWUQURDKVZ[XXQO[bceac`TRY]cfifa^adeeddccbbeVVsõƷ]cfacb^a^c]]abb_XMKJMV_`\abcc_[|r^klikmhknjfikhghkkknnkjknomor\dπC~łgswlb^[VUXVXXWVVVVRTUTSX`ghgedfjjigcba`ab`\ZYY]bXDAV_bdehfjd^\^`_]\]ZWX[WPSVSKC<:?C?::<9:AC=:==99=?<:<<>ENJMOOLKMQ`swqsurrttttsssrytrssuxzsnnmkptooja]]\[^_`a_]`a]__\\``]]\X_^a_U^kW`aloeѽ˯r_fPMttW^\WXXXWWVUTTSPQRTUTSSOPVT<099"!" %Wsg`a`caZYaedfvwt~zwyi]VY[[[ZZ\ZY[YWZXYYXXXTOURE?IPNNNPTWYZ\^\TQWWRPSRPLGBBBBEHJIGINSWXZ]\WOHA8132(  !$'-,,,-/246888>JUZZXXQO[bcb`b_TQZ^ahic_bcbedddccbbbUWuû]`c_ccaecb_acaa`\MLKNV_`]_`ac_\}v^ikiklinkggigfjlhjnlkkjkmomor\dԀGz˽zgvuj_YVRSV[[ZWTSTVSUVTQS\ddghfcdhkhcaa``cd_XUX^aTB@S^dgdeccb`\ZY[]``\XX[ZVTSKB>::>CB=9;;BS^gidb`[]_^[YZ\_^[VUVVURNC<;;CB?@@<;:<=<;:986:;;>EKMOPLEFOPLO_szrsvssttuttssrrvwuttsxojihptnficZX]be\`b^]bb\`b`^^````^^XX_]`\^Z[]`gjhþЮq_fPNutV]\VXWXWVVUUTSSSTTTSQPPOUT>396#!Poeaega\UW_ccfamztytsqe[WZ[YXVWVZ[VVYZYZYWWXXWQUOHIOPQMMOTWXXY_]ZWTSRRQQSL@AGDDHKJHINTWY[\\YTQK>40-07=?3&#"#&+),.-+-274578==CLPNKILNMJMYq}wsvsttssssttwvwwtvxxtmkjjprkfjf]\`bb__^\[_b`_^^__^_`]^`\[`_\a__\Y`jijĿŽҭp^fQNusV][VWWXWWVUTTSRSUUUSPOSPTTA7;5" Qqh`bb\YWZaabhcezuzooynf\WYYWXXXY]\XX\[VXZYXYWUQWVNKNQSNNPTVVX[da\VTTVWWSTLA@FBBEIJKMQTXY\\[YWVQE;40<<>??;98:=@CEKHGKMLJIIXp|ursqssrqqrstwtsrruxvnjjkjopiehbZZ^__^]_a`_^]`\]aba`^]^_][_`\Z[^^Y`g`jЬp^fQNvsV\[UWWXWWVUTTSUTSRRRRRRQWWB6;7#Psja_^^[Y]a__fecrypn}tmi]VWXXXXW]\YYZY[VXZXWWVUTVUQNOPRQLLSTRU]_\[_`[VUWSSOEDFEGIJJJKNQXZ\\[ZZ[TKHEANeoXG1#!%%$+,-,+,035579:>HQUVZSOX_adbc]OMV]dedcbedaaaabbcbc`SUt^cfbddaeb_`b`^a`XNNMNT]abc_^b]WzvamogdhjkhikhgjnkjkkhillilmlmpZb׀RϏq~yYSPTVTTVOLLNPUam`YUVUSV^aceebbgmmc]^_acd^ZQR\ZH9BQTTVWXSUX[YURSUVTRTUTOKA?97<>=?BEB>?><<@@AB@??@?;65?=;=@ACC@=?DB>::;<>AHIJKID?(%**('')-145679:99BLSV[TOX^aa]^[RR\bdggcacc```aabbcc^RVuû]`c_bb`dabaa_`fcVLMMMR[`bb]\a]Wzt^jnnok`chmkfhkhihehmnklhkmklpYa΀Oʑz_V[PLWTILMVXY_\]iifaZUTWYb_^`_\aik`V[^^ce`SOV^UHLXYTLKPQKPQRSSSSTQRRPKE?<9426;>???A?;=?@@DB>?DEA?<=CC;9@DEFIKG?;>8469::;=?@?@;:67<=:834:@>?EHFHHD>=@Dizxtttuosnmrroosvwutqqurfahnoonrgda\]]W[_`adhhfe\^aca^^^^^]^a`]\]]]^Z`eXqþ¿Ȭo]fQNvrQWYVVRRTUWWWVUTSRRRSTUSQQTE247' Kpj`eda^ZY[]ae^bfeizyv~|rmg`\YXYYWUWWWXYYYYZXYZXYYVUQRUQNMLDGCEOUTS[]][[]]\VSV\\UNLOONLGFKSQXZZ[]]``^[YPSbe\WB)!%%$'()+-023568856?INWXPNY`_dba\RQ[dddddcbbc_baacb`bhTSqŶXad_ba^b_]ab]]ecVLLKOW`b_`_\`_Z{x^hlhfiihhiiijjjfjljhilnlnlkppXgˀ&|Œn]a]YY]_agjfhbWTQVcefc]YYWTZcaYUMN\gi]TUV]b_STZZMFRSMIKLJKNNQTSNJKMJGBAAA?=7447;<;:>><;;98:<92046454138;@FHINME@CLPctxvvsrlpooqsrrswvrsuvxrgaiqrolnnh`XZ\[`he_`jmigbcff`Y[`^\_bb_^]\[\\Z`f\túʮn\hSLtqPWYSSQQQRRSRQPYXVTTTUVPNQUE247' Hnh_fg\]]YWZaecddbdrz{yqnh`]ZWXY[[XXXXYYZ[[YYZYXXVVRQRQOLGAECBMWWQW]^YUW]`WST\_ZSQONNKGFMUX\]\]\]ab`^\TUbe_YC)!"##'')*-024568965;DLUWOOY`^`^_[SRY_dcdec`adbda_ba_bhTRn{[ad`bb^b_`_`_^a_VNLKMV_b`b]Z`aZ{v]hlhfhhhjifjnmhkjjkjhjmknljppXgˀ Y纂ja]fqmbbddh_`\YVPPYflme]ZZX\`]WQKKS_kaTRTZ^VLQ[YLFRPNKJJKLLQSSPLHFF@ACCBA?=:7578:87>>>?>823:955873283147vegkwxnbbededhf`]XQLU_hie_XSXX]]SKKJUgeXSTYZNFNXVJDMNLJKLNOOSSQMGCAA>?@AAA@@87669::9;;;<=85777559:8853358:>BMKLNPOPRNYnyvoqynpssrrtvouxytqtrjgkjjnnohjwuc`g`\aaahllmhdcec]\`^`ec]]a`\[]]]``Yz¿ƫq_ePOwnMVWOOOPPPQSTUVQRSRQOMLMTZVA9@8( @jiaeecbbYLN\eea_`bgt}trpne^^]YWUWXYZZYWWY\YXXXZYXXVVTRRPMJG@@HQUTQWZ`aVJP_ba^WVX[ZVSPMIDEGSVYZ\[]b\[XYUT\a[R;&!$&'&'(*-0346677536A><<>AA@=;;:99975;:76889<><647:;?775588>>==<<>AA?<@?;76898<=<:;<;<@?;9:;:<;;7588;BNKJIJJJJFQmvstqrtsqnoqstttsuwyzscelljnpsxxx}r\[b__ccahlfggfebaabbccaa_]]_[][[_][¾ͬp^eQOwlLQSQQMPPONOPRSSSUX\`dgdjl^@:D8!!Fqk]adg^bcRLXbbcbbbaixrsupb[WUTWYYX[YWX[\ZWWYWWZXVWXWSRSQQVH9;KTSRSPVY[cljb_\\^_]]^]\[ULCDHHQWX\^_`^\UVURY`_N2 !$#!%&(*-1457422116>;88:;::=<:;<;:==979::<9978=<<@DHHDBBB?@Rswqwvvonmortrptuuurswtkhlidhoxyzx~xd^^^_bYMXlqihfdbb`^`ddcc]Y]\Y]\]a\Zʺ¾ûͮn\hRMtlLPRRSMNOPTX]bemoprssrrpqqbA8A8%! !KukY_fdZbgWNXaa`[[``eorsvo`]\[ZZYVTZXWXXWURXZWVYXUV]YSRSPOUM>>NUSRPQVY]grrjj_[aaYVY]\[ZTKFFLOU]`\\bc_XXVSZb^K.!&$"%&(*-1458300016=BMRNOZ_]]_`ZRU]`adeb`aba]`abec`biSPn}Ydfbddadabdd_^c`TNLJLT]_\^]^]X\p\iieggbcghefkljgknmihjmiljinnVe΀&,3qeo|\^``][ZZ_efcelmhnmjcYQPRPX\`bXG?JTbeZRRU@BLM@@LRQRQOMKGA==;89=?>9<=>@?=>DB;427978;=<<=?@>><99;<=@98=>:BD9@`qpuwuvusqqporwuvuvuuwrfbkkeiszyytxp]\`acWJShoiojcda^bfdedb`_\X[[\]b]^ýɨq^bQPvkENUPMOJPJCHMPUTTVXYYZ[aYSJBGF8%! !Ryn\agddec`]]^^b`^bbbh{|oqzq`ZXXZYUTUYXWY\ZWYYYYYYYXXX\VQTROTSFENSRQMRLQ[akuwqrj_\aa]]ab^XRJCEQWX]`__`^ZZVOXdY=($$%$',/1479752/159;FPSMNY_^a_]WQRY\cedcec`bgd_ae`\_bUTl~|Zce`bcafd[_c``eaRNNIIT]`ae^Z[XY}l^kigiifhiiiiijjkiffkkjmjkijnlVj̀"12]n}iY]ab_\YX\_dhklkimjknk_SNPSV^e]K@?I[d]TNG>CNK<=LSSSOIFFB>;>>==???==:;AA>=:=<769:959>><:;>B?<::;=@=978:<=@BC>AB@=5Dgyomvxssqqrqpqtrtttrqtqbdllntuusxzz{wi\^^`aPFYmrkehidbfgeeda_]Y[^\YYa]^ž˨q]bQQvkFNUPNOLR?! #! !#$&' '=>????>;@>:;AC?>:>@=<<:8558==977;88::89<:50025565:;><9BGPcsqjnsqqqstsqqrtsqrttun^bhipurrux{vsyr\]]\e`JNfojhifdfhgefeb`^[\a_[[b][»ʨq^bQPvkGOVPNPQXJ-"%!"!0BBLSG)" !Nxphoghdbdda^]c`_]\_fhpyzrqum\WYZYWVVUWXWXYYY\YXWVVVVWXWYVPRTNQOMPTMGINQPNR[hurvwpf``bc__bb\UREEKU[^_]a]XZXRY`Q3" ""%)('*2750/01/-/7>FPTMNX_]]ZXSMPZ`fbcc^_b_\___`]]dbORr||Yce`bb`eb\\`aab]RJLKNX_][[Y\^XXp`mmjhgdjjjhgghikmmkhhjijkijnkVjʀ/+3fuekc_^[WVX^ccadlmihlhmmffmok]UOTadYLB?Qb^UNB:FMC5;IMFE@<<@A@<:8<@CA=@?<=AB??>??=<;72238<933720278547=<6015525<<<;E]mcao{urvtpqstsqqqutqstvvnbckprsrxzvxwsyvc]d_ajYGUikljgfggjiihc`^\Z^^[Z`XVȽĿʨq]bQQvkHQVPNQlh\QLJFC9<;5/--,++8>=OT<% ! Rsjnehkkgcaa`b^``[_feirwsopi[TY[ZWVTRYZYXXWX[YYZZYXWVYX\ZQQURRKKOKE?9EOPMOT^lkottngfjle`ac`YTH?DS[_a``]Y[UQZbJ,! !"&''*-0013////.08@GQTNMX^][YYWSU[^cdffa_``]``_a_^beTSm{{Yce`ba_daa_`a`a^WKMJLV^]\_Z\`Z[~palhejmjefffefhjggjjgikijkijmlVjʀ+00Ebwq[cbZ[[ZYZ]`ggikkiggcba`^^dkjkbTT``UG6>GF>>=:<@A?<:9=BCA=??=>CC?=?>=;;;975359844651155226695,*02-994>C2((++,+(++(%')''-S}tjspglldafgd`]ab^_dbcksqjig\VY[XXXYXWYXVVVVYYZ[[ZYWVWX\]WRQSSPROC94/8ELMOOR]cekrtqqtvsmgda]XM?AOY\^^a\XXSQ\aB%!!"#%&&+0/+.63/-/239?GQTMNX^\[]`\SQW\cedcda_`bc`_cb_`dUSk{ô{Xce`aa^c`b_``_`^WKMJLV]][_Y^b[[o\komihidhljfeiojghjikmljkijnkVjʀ.4-8UggzX]_YZYXXZ^beikjiifbba_\ZZZ[bjpgVORSK>BW_RC:==@?<@?ACC@<;??<>DD>;???=;;<=>625;:6363243/.02/.-,+-03,6azvstdZgzvoqrqssrqrrrsuuvssurifmnjrvstwzwsvyxe]]]htcT]jmjmjefggigb__^Y[[[[^VWǼ¾ƽȨq]bQQvkJTXPOSVNKPQOG>HFEKVXG2#!(-(*0/'#')%)/..,'&*-/4-8c{ptrmpokkmia^^ab``aa`fomdce_XYYYXYXWVXXVWXXYYYYYXXXXXXXZZSORORRQMA2,.7CLOKJP[[`jrtuxtywnc^\[O@HRTNMX]\\\]XPPV[dd^]bc^_cea^cd`_aQQn|zXde`a`^b_]]__^`[QJLKMW][YZW^cWZtakjffhhjgefhifcmgefghiijkijmlVj̀ .1*5Wlhe}TZ]YYXVVY_dbdfefggedlmgehbVWVcqkYJBB?CRZN>8;A@;;@?;@A@>?A????=>BB><=>??><<<=:77775321110.,+/*)-.++/,"-Zvqosl_fyzpqwrtusrstsvustuvwpmhjghqutwttrnouxzh\]grn]Rdmieefhffhgb``_Z]]\\a[Z¿ǼŨq^bQPvjKUYPOSYRQQMIE=JHGMZ\G-*$$*,)*-(""$#'.*&)%"&,,,.Af~{uslnkhgfb_\^``_`__b_bki]]dc\YWVXWWVVYXWYZYXYYYZZZZYZYXYZUQRNOMPVN>4./;KOJILSSV^fkoqqvysi_[YSE=GV\_e_XVXTT[X4"!&($%(+-/1335/-00/4=HRUNMW][_\ZVQTZ]ca_^aaa`^ca]acaa_OSr~zXde`a`]a^cca]\a`VKMJKU\[ZYU]`TWnanjgihbdfiigfghigfgjkihjkijnkVjՀ '-+2M`^b`sT^WTVY[[\^```__aehjlpmfeihbZSVagg[J;ABDHB:=9?@<<>=9AA@>?BB?B?>?@>=?>@?=?>=@==@CDB?=<;97652/11/--.//*-,(*0.&$)Vvvqmnffntwuporsrqrrpstrtssupqmnosvu{xmmtrqvx}u][bgpiW]glhehmiikic__^[_\XXa^^ͽ˨q^bQQvjLUYQPTWMNRJDEFBEGMWZH2 $%'+($&''$&*+($&&&(,+'(3BRVSVWPRG@>;8?K_b_\__^d_`hgZZde]XVY[[YYWYXVXYVTTVXZZYXVXZ\][WRQUPONLLJB2,8KOLMNNNOSZ`egjmsxuldaVI>DSXZb`]\\SOTP1"!')$"*.+-3633..0-*1=IRUNMW][\\\VMMV\__de``cbX``]^aac_RVszWde`a`\`^^aa\[a^QONHHRZ\]]UZ]RVo\hjhfhiefhijihfhifdijgfjkijnlVj݀+303BQWRNogS[XYXWX[afjjjgcekoomlklic]ZSOLLJKLJ=A@>?@AB??@CC@>>=@BA><>AA><;:;;82.0).Lab^ab^_`cchdWZeh^^]\ZXWVYXXZYVUVVWWVWYYXTTWYYZYRTUUPJNOH905BHHJLILNOPTZ_cehilmjd]PFHS[\Z^_Z^UPWJ"!###$&*-..///-0.,--3=KUTNPTVZ^\[YRPU[`^cgc__`a_`cc_]_hQSr~ySdd_ab`a]a]\]_a^UKKIKT[]\ZX[\RYnbhhkfdkkeeiifeclkeafkkjkihkojUl܀*1-0>MRPM[`V`]XWXZ^ekojkjghkjhgihc\TLGEDDCACC@C@=@DCBE?@?>==<<@?>>>?>><>AB@?@C=?>=@?=?@@@AEGC<;87886332221/-+).0,&!$&(Ajzronhgpxxtsuplnoorwwuttsrstqnrwvsplosw{|{xwzxe_fmpgY\ikhijggijf_[[\W]rqh^bͽ½ƥm`bOPrjKTVNMQYOLMJJHBIPRPQRLCGGGE@:4054333445667778887>>IH===;I^caa_Z[]`]a`WYa`\]]ZWVXZWVVXWUTTQPRUVUVYWSSXYYVSRMMPOPQOH<2:INJIHLOOLKMPW\_`befdf\KFRYYYb`X[WTT=##$!!&),...022-/.,-.4?MTRKMRVZ[\[WOOV[dbba_ab^]]^aba`abPSp}yTdd_bb`a]Z^a``e_RGIJNW\ZX^Z[^SYh[fikifejihghihgkihimkhhkihkojTl؀)0+,:HMNPNyޛaY[WXWXY]bgjddccecYN[]XLDDED?CGECEC>BCB@A@@A>@A??@@>B@>=???>@>?BC@?@<;76:;;=DB@CGID>8556531010/--,--4<3$%&:auqmoihowxursnntuppssrrtusrrnmswsonmvtsvz|{xyk]dork\V_hhgihghgd][\]Vf{dV[ξ»Ʀl]`ORqiJTYPLNXQNMKJID?9447740***)&!#"! ####$$$$'(*,,,+*,330333CU`a`^X[_a[^_\]_[Y[\[XVVVWVVWWUUUKOQPQTTSWWVUUSUYTNNRQNOOMKEBEB??;:;AIOOMMRVUVZ]_bcTIQWWZ]]VWXYR3!##&))+,,-023-.-,-.6CSXUMNU[^Z\\UNPX[d`bc_[_d^_`__`_]]PUo|wVdd`ab`a]_`ba``]TMMJJS[]^_Y\`UXh\ikijhadhhfigefgllgfhjkkigkojTk̀+1,,9FKTVKeľļû`[VXYXWVWY[]^[XY\WF5?ILE@BB?BDFC@CEC>CC@?@@AED@>=@BCAAA?>>?@CAAC@<=B@;986:=<@@?@AA@?:7554100-,,-048:?J: #$6[ttnokhmuxurrnotuqprnpruvtrswonnjhmqxtprvzzxyyt]aomlcWVajifihggc^\]VPhdV[οúƨm\`QTskJU[SOPVOLPMGBB=&""/N^a_^Z\^_[\a_^]Y][YXWVUTWVVWWUUVRPQTVVUTVYVSUVTVWWWUPLLLJJNOIFB<82.222?HFCC@:BAA@=>AACA?ADA?AGDA?>=>??AB@=<>@B@@@@>>?><=<88:98876557:32120--.3457;AEHHP?$4Xuxoplilsxvqqonopppprsstvtsvqebfhkrwssttwwwwxs|zfckhlm`S[lofkiihd_]^]Si}`T[¹ƪo^bTVulIRYRPRXLGMK>;CH&  Mab__\ZY[X\`_ZYX\ZXWWVUTTSSTUTTUSQRUXXWVXZUOQSUYY[[XSNMNPJJLNQRNGD>969?DHJKJLNLHEQ[TLRYVT\]WSN?%"# %(%(+-.0221--,-./BBAAA@=>BAACB?<=?@@?=<==><;>BC>99<;874364211001431000037BCDEHKNOLOC&#1Rt{oqnjkrxvpnqrppsrmqrprvuruqdclqpqrstuvuvwywux{tjgiingWVfmfjiijf`]^[W_ria\`ĪqadTVvkHPUONR\J@FD78KJ(F_b^`^[YZZ]b`ZX[UVWXXWVVUSSTUTUWQSSQRVVSTWXZWTTX[ZYZXRNRJMMOTVSTQOKE@@CFEGGHLNLGJOVRIRYQSWYWSK6! ""%&((*,-/10.-,+-/0>SVWVQPW\Y[ZXUQSXZ_abca]]b___^^_```ORnztYdd_bb`a]\\]]_c\PKKIKSZZZYZ\[QX~c`hdgiffbcfgeijdchjjkkihjgfjmhSjþ -3/0=KPQXTaξRPTPPPPPPONMDC=648<<;>@ABCDC??@A@BB?;AB??@@ACBAAAABC@@AA@?=<8>A><<;89:8564243102200120...05;JKLNNONNMNJ1 *Ioznqokjqxwpptsopttonqpqurotmabntsrqsttsssvxwwwzyoijghi_TZfihhijf_\\]_[[X_\X¿¨pbdRStjJSVMMRYI=@A6;QI&<[b]_][[\[]baZX\VVUUVWYYUTTUVUVXURQSUTUWWTUXYWWVWZZYYTQTPPIDMWZ[VRMIGFECGJJJLOPNQSVQKRWQSRTVTF0"" "$$%&)**+-//,-++-/1@WVTRMKTZWX[\VPRXY`eb]_b`\ca`__]^_]PUn{tXdd`ab`a]a^^__^ZRJLLOW\ZX[Z\\RYg`kihigfehifeijhgggigcemigeimhRj+2-/=KPQRGeǰݘ[SUSPPOMJGDB9744:@>8??@CCA?><=@@@DC=BBABEC@ACCB??ACBABDDCA?>SG":]e][XWX[WX^^XUYWVTVYYXUTSRTTTUWSQOPTXYWSSUTQTWTNY\VVWVWSRNHIX_Y\[XRMJJLMPPMLOSTQXYSOSVUVRUXS@(   #'(&,,++-0/.,+*./1AZZVRLJU]ZV]aXPRYY^bb``_^ab^^``_`d[RYq}sWdd_bb`a]]`a^]`]SHKLOW\YVZX[^TY}c[jjdeiidhhgfdcfjjhgihfgigeimhRiµ&003BMLMMHşPGULIC;64321::::::::<<<<=====;=@BCDEB?@DADECAA@?B@@@A@<98<><9986.35300/.1/14630/53.1646DIJLOQQPOQRT? &>buropnkmsustsqkhouspuustrrvo\U^efdbfnqqronqox|ulhgekimo`QZqmfifca\bZ[\YX^^W˿ļƺracPQxjJTYQQUWFCF;3@PB$ 7Yb\]ZY\\ZY[]]ZWWSRVZYVSRPSY[WTTSMOSRUYVVWWUTTTTVTRVZTPVWVQKLSXZ[]\XUSPLTQRUVUVZZWWSKLSUOPQTQ;$ "#!#&)*,--,+++)/.-/9UZXUMIU_ZY[\XRTZ]\]ab^]__`__]`^Y_\OQhx®rYfd^adcb\\`_\`c\QKLKLUZYXVW\ZOZ^aqg`ehedhjhedeeiggfehkhflhfkhSl°Ⱦ '--1=ILMFd俤‡I=E>;8448<>?::::::::;<<<<===;=>??@?=DDCA?@BDBDCBABA??@@?<;;<7679732342//./12-0343211.224757CKLNQRSRQPQWG" %6[sqnqoklsusvrpolostmqsssrsun`X\_bb`dlpmnqtuwwk[YgolkiilgWUfmfhic^]`[[\YX]^V~ʺŢracPQxjJTYRQUPEBB61@RG& 7S_]\[Z[XXY\^\WRUTTWWURPQSVWVSTVXVSRRVVQUVXYWTRRWUSX\WRQUVSLHLU\[_a^[ZXUQPSX[ZZ]ZVXXPLORPQSVN6  "#&&'***+,,,,)+,-63.ASVQKOVY[XYYUNOUW]aa``^[^_^^]`^Y`^PTo~vUfd^adbb\^^``_a]SOMHIRZ^`ZY\[OZhbjbbikgehjheeghhghfcfjjglhfkhSlǺ#(*,3@IM>B߼м~J573;855:=?@::::::::::;;<<=====??===FFD@@ACBBA@>@A@>?AC@<888977996441/.01/02021.-156.455768@MNPRTTTTPPYQ,$-Pprmrqjksvqtqssrqrqpquvtrsslb\]^adaegjkotuprrgXZhoijieim`SXjffib[]][[\YW\]UϾ½º¢racPQxjKTZRQUPE@:11BRI'!$$7J\^\[\Z[YXY[[XUWVVVUTVYVVUTUVWVYWSUYYUSVWXVUUUUTSUWWXYXQSSNGGPZ[_ca`_]ZVVY]^\[[^XWXRKMRPPRTH/ !##%%&%+))),-.-*++-82%+IYUKRXX[XYYTOOUWacb`b`^`^\^]`_[a^PVtrUed^acab\_Z^a_a^SLKILUWWXWU[\QX~b_jdbdffcdddefecgeedeghegkgflgRlɷþ!((*:IKG.XپٱI19=A=989;:9::9:9:9:9::;;<<=?<=??<;=<>>@ACBADDB@@CBA?@??<9535653464/,/241..0/0//14546645756=LNPRRSTUQOXV4$%Howltsjisvqtutpmpstsrtwrrtslb^bdfgegehmmopnlmh`^dggiieflhZQ`gddaZ\Z[[[XV[\T~ʿ½racPQxkKUZSQURF7.*1@LG'"""*4AYaYZ\[^[YY[\ZXVWWYWVW[XWUUWYYX\WRRVVUVXUTUTSV[RRWXVXZUQPQPKGKQX\^``a]YZ[]`a`___ZXWRKLPNMPO@) % %'%%(*('),.-,.+,01-$ 7V\KMWXXXYYUPSXZa^^_]_a_^[]^__\a]NSsmXdd_ab`a]]XX\^b\NKJINVZY[ZVY]QY`_kggifcbcddfhhghihfcehkhkgglgQmöļ*((>BCC@?@AA=;989864121/020-/230.-///./245448547846>JLOONORUSNUV9#$Cn{mstkjptrsutolmrurnqsqptto`]eifebahnmihlmdgf_\`efgggefkdSTfa]`[ZYYZ[WV[[T{ɽracPQxkLUZSRVTG4''.;FF&  " /07VbYX\\[ZZ[^]YVWVVUTSTVUVWXXWY\WUROPUWTSVWSSVWVQQTVUZZSSPPOLHILUWY\cfc]bbba````[\ZXTNJJJLPN;$#$&$%(('')-.,*/,,00*$!BWPJSYUXXXTPRWX`]__^_a^^Z^^__]a^MRo~rWcd_aa_a]__]\cg^QKJILUYZ[ZUY\RY`akcdihefiieacgkhggffegiijfhmfQnźȾ+**:@3*Vշ[VgR;978:;97776767776779:;<<<>=<;=<;@??AA@@B?AA><===<<;:8743310220/1231.-00.0/144348425:948BHKMMKLPUTNSW@"#>fzossnkmrtqrrsqlmsuqprqpruqa[ce`\[Zhonnkc]_fd\^ikdfehe`hiZOaa\^\YXXXYWU[\T}ùûracPQxlLVZSRVXO>315>KE%#" 3-/PbZZZ][YWX[[YVWTRSRRTVSSUWXXXYWUTTSTTRQXXRQVUPMQUTSWZYVTQNJHILRTV[cjifhhfb_`_]\]YSQNJJILSM6! !"$%%&''*,,*),.*04*!+FRPU]ZZYXTORVV`cbbca^_`Z^_]_]`_ORn}sSbd`aa]`][`^[_bZPHJJMTYVTVTX\RY}|]aj`agfeggeb`bdeihfimlgdiieinePoŸ*-1;5.7ί~|xjgpdefghijjgggffeedchlidiwfi~Z<:878741555555555678::<<=<<<<;;<@@?=<;=?@BB?>>=;<<=;830.1//10-.11111100/.0477544436975;CHKNMJKPVTPU[I$$8[sqqrqmjovtrpqsnmrvtrstqptpb[]\YYYZfjkm_NLcgbY^knefcdeace`SYca\[XXVWXVU[\U}½racPQxlLV[SSV[SHCDBDNA#!5-)I^^]X[^ZVVXZZXSSTVVUUVSSSTWYXVYTPQUUUXUTTUSPPRNTWXXVUVYXTNJIKKMRVX]cggdffcbcb^`]TONKJOIMRG0"%()&'()*))))--42" "/CMX^Z[ZYTQRWW^aa__]\_aZ_`]^]^^PTn}pTac`a`]`]]_`^^`[QILKIS\]Z\Y[ZQZ|_aichkfdedbcehgdghgfheehjieineOo¹ɾĀ'/9?23NҬƊ^XSROLLOSTVY[^_adca_\ZXW[[[[[[ZYhom|N976787414444444455689:;<:==::=>;><899752/-,.11.-.//00231.0423335774:65546?99=@=9<=;::97522441/1..120,-10/11./3368743664356558?FLKJJKMPRWROYY3!'OsrjnsnjoruttspoooottstomtobYXWZ^_^cdYH>CMZidV\eed\_a_\\`eWL\naUXXSX[TOUYQҿ»º¼sf_KQtnOVVTXXQKHKJECG?#+8) 9Z]X\^\ZYY[]\[ZVUUVTRSQSUTSUWYWVSQRQPQTVTOPUTNQPPRW\^]RRTSNKIFFMMLSVUV\`^\aa[YVPOSQIFJJLG4"! !##%%'*)()**(*-))24&"  =X^XYXURRVZa`^_a][`]`^\]\\a[JQnsTbaaabb`^\^b_\c`OJNHFS[XXXVYZT[yrW\heghhdchfdjjeegeggfgigklggkfTnκÀ0920)Pݲڟ^BLMMOPQRRPUYZ[]__^\aio{~ytoljZiڧhG67887531/////02354444567876788889;;;:;<=9;;8678722220/.032/,.11.440.12138765545688878=EKLKKLLMPTWUQXX8 &Jnslnrmjosssrqrvskqrqrtsruq_VZ^`dga_TEBJMHXfbVZcbaga]]\[[\TFN^\XXTUUUSRZ^Zsźs][PYulPX\[\WYQKIFBFOA$+6(-Q_[Z\_][[[\ZYUUVXVSSUVVUUWXYXVURPPPPRTSSUVVSQNQTVVXZ[WUWWRMNNILQTRRTVSTVXZ^]XRLLPNHFJKME2! ! $('&))*+)')-+.53#":V\UVVTQRVZ\_^]``_a]`^\]\\a\JOk~nWeb_^`aaa^`a][`]NKPNLTYXWXVYZR]xa^efjhecacffcdghgeffdfhglkdcfcToνƀ55//9}ԵwGILMNOPQRSOTVSXbgdieer}uppf_὇X=677764202231002422333445;746889:6:<99;<9898645660010123210-,055266326668955764477678 $Cftmnqmjouysonqtqlnprrpoqupd]__ade^LAFLIHKS``X\a^[[XWWYYVRIDIQQORUUTSRU\^YnrkjTPmnPVYXYVXOIIIFIOC$+4+(Kb_Z]^][[\][ZWUUTSRUYVSPQTUTQTTPMNOPSVXXURRTUQSVVUTWZ[WX\XRQRKGLSOOSSRQUVSVWONIILKFFIKLC.  #" &*)&*+-,)'*.-380 # !7SZVVVSPPTX[`_[_a`_]`^\]\\a_NSnmZaaabcc`]\_^Y[a]SJLLLQWWUXQVYNW{u[akcbacdfbdfdegehfhhghihjjdafbRmµǀ*5213ZaDVMNNPQRSSVXRLRbe^_bcr~wtunazԕU:77654200.//0/12401233333755665555:;88<;7677654331/-0463110244101//25358796677568458=AEFFLJKRTRQSY[WY[F"$=_uompmjnutpoppqqpqpprprttnhc``c_SHEFJLIGIMY]Z]^YUXYXWZ\THAHLLHFKSPTUSRURJY¥secMOroQVWQTWaRFGJJKNB$,2*$(A[^Y]ZYY[^`_]XUSUVUUVXSPRVVTRUTPMMOQVVVUTSUWXVUTUTUX\WRUZYUPMPGEKPTSOTTVTQQPJNJIJIEEHLL>*"! $('&()*)'&(+-69,! #6S[XXWSPORV^ca]^`^]]`^\]\\a\LRooVdb``aaa`^b_[]aZRMJHKQZ]XZTYZLYuXbkcdffefeehgfefccceegijgiefidQiр#84+?ޙQ@PNNOPQSST[\VMR`faells~xwvq_iۛT7875310//-,,-//000112333266433565878:;988666543332.-142/-12685/.0-.476686898679988:=AEGHHMJKQWVSQZ[XZ`M)$7YsomonikstusnjotttrqpoprqmfbaddWD>IMGEJLIIRVVXYVSZWUWZXMBCIKHJJJMMNRTPF@@HP^y;ĤsfcPSupRY\TU[]PGIMLJK>#/0"#"0LYWYWWY[_`_]ZVUVWUUUYVTVXWWWXWTPQRUZ_VQUYVUVUSSSSSW\YTTY\[VMRNGGRVNGNSSQRRNNQMKJHEEFKJ9$ !!!%&&&((''()*),66(! 8U]WXWTQQTX_b`]__]^]`^\]\\a`MQk|uYdb``aaa`^ba^ab[RMIHKRZ]YWV\WL^vX^hdifdfdbcefdefhgecddfggiggleRj€6,)gῴЃLGNNOPQRSTTXYUR\sw}}uqov^\ԕO48741/...10./00/.001122235532488687789987764321252/0430/1145323203668<;779;;88;<:===>@CEHOMKMQVVTZ[X\bU1$1Rpnlonhhptuvtpprpruvurrrsmede^SJCFFFHJKJJIMPRSUVWUUY^VG?ACIKIJKKKLGJVR=5@IQPNQQMORPLIGEEFIG4  #" "')&'*))(*-,+.51$!;W^TUUSRSW\\_^]``_a]`^\]\\aaNRl}s[aaabcc`]]_``ad]TJJJMRVWWTQWZP[{nU`ibfe`ahddfdegfdeefggfgjkfeieSl(.OзsKSTOOPQRTTUURUeuz{spxc[΍O99730.---.../////00//0122.03676417764688564320/14211221366640021/5756:978;;;:;<===>><;<@DFLQQSVUQX][Zc]9"+Jmmlopgdmvsrrpprroqootvtsnda^PCBFGFEGKMKGKMRWXY[\[^a[L>KWXfZPQKR\`}xlz~dMQWVWZ\TLG<0-10#41'% 2IUWUVX[^^[YVWZ]\XVWYUTVVRSVVWTPPPSXRVYWUUTSPMNQQNQWWOMPSVUQIIFA>=>@JOQPOONMPNKGEEFGFD/ " !! %&%%()('(**(25,! #=W\TUURPQUY[a`[^b`_]`^\]\\aYIPmmTeb_^`aaaa`a`_`\QKMKNVXWZXSW\V[vjYek`fid_defc``cddghikieehiedicQj%>¤eCQROOPQSTUUVR]wu~trqf[ÀG7973/-,,-/010.---0/.../12236752368534655642231//2430..13452012124768969===;:==<=@>ABA=!&Ejllopgbjutrompplrupknppum^SLGEGFIFEGHHJMKOX`b`__b`TCWZWWVRNMPT\ecZ\b`[]`^\]\\a^LPj|oZbaaabb`^a]^_\`^SKKEHUZX\XZYSPa~mUcmbb_`gdhdbghdeegeefdbccfdfjcNf)fֺ~`CNRPT[WYXPXUPȻwq~xpxu`z;563/./110210...../////023666566663432321241/010/0.*-102773001145358:==>?@@?==>@BDEFDJOU[VSXS[eceeF#%:dqjfrlajtsswuppqrqtsmouukWFBDIHAHEDGIJMQMNYaacd_TLC><<@DKMRSH97?GHFTcS;9C=:BJFGRRU[VMME1/=A?@BEKKFFKKFEIB?AJTXYZRLHGC<9;-%&5-#$##5CMPPRVWY`[TUYYZ[WWVTSRRRROMNNKJNQSQQSTSSUVPLMLKOUTRQSVVSNFED>59EMNORURNOTSGHEEHAEK; !#" "$&''&'&%$),*+12) ## ! -DX^RUWPPRPT^b_Z]_^__``][\^_aOTn|lV`ba_`ca\ba`^ae^ONLHJT[[ZZZZRH`|kXdhbficeiebadggfigeffeefjiikn^Th .ZШT==?BCCC=>?ACEEFHJKNUVVYX[a_`dM%!$7`oieoj_fpyvrspoqnlpusqtwgL=CEBELEDFIJKKMONS^ecVGA?@DEDCDKNOROB64<@COa\C47ADB@=@IMGHLLJ<&)7<;>ACIICAFKIC?<:=FORUVTSPKF@:7@80-+(')'&'%! !&+/1467/42&&1+!"" !&):GPQRTX\VPNPPPPOSOKKNQRQUNJKJKNNONPSUTSTWRPQOKKOTRSXYUOMH>:921?LQRSTQNMNLGF@@C>EK: !"! !"$%&&&&&'%'*).1.#"$ !0JZZVVSLNTQU\]]_`\]fa^YY\``^aNTqjScbbccbaa^]^`abZMNMJLRUUUWY\XK`zjYdlfdfccehhdbeebbhkhccegeeehl^Ti#&/;Ka~[F[eNT^\_\RXgƹvo}oqo]y۽q9373102321321//.///00-/45234566544652132/.0--031.,.//14335324547998::;>?=>@@@ACEED=?ACEFFFGIJLTXWW[Y^^]eU,"3[mkeni^cruttqlnottqopopsfMCIIABHDFGGHJJJJLMPTNC<<@E?ABBDD?987;BHKOS_gmidb_ZYQKJJGGJ69>5'(-(6@FEFJJFIRSIA>7-,(!'4-$!$:NPMRXXSPLMPPOQSPNNPRQPROPOLMQTPOQRRQPQRNLMMKMPPNQVUMIID97<85<;:;?EDCCDDCB?ADGHHGGGKLLQWYXZW]b`gZ/ .Uloink`aupospjmposqnpqptiREJHB@CFGFCDHKJFMKEA;;B@>>???AFGDJNLSYS?57FWd[C7<<:66F_zq]G<<>>?DFLRTROQRQLJB>CCCOL4!##"##%&(&%&&(**/GSL0!)K]VPUVOMPPW[ZSPUXVVXUUY[[[\]OUmzfYcbbb`^^a^]_^]]ZRILKKRYYXTRWYN`~t[dle`dfhcdccfifaeddggeegddein`Wm!(*)*.1;HaťTBPQNT^\`^U[оcp{rgh\sҰi;87544653122210.--/02430/021111122010/0/./--.00//0243352/152368;;89==;;:;@EDCCEEDCCDGHJJJJKNOLKS[_ZX^daf]5)Lhqjnka_msnknmnpnqpqsmmulM@@@CHPHFKLLWdXD;?OdcJ7:96=Osi?7?9(-7859;:;==946<91101578;@CLYafhhglfaa`]\]dabdirmXO]bYXekg`\TJB=>A8CLMJHD>@50/.,+(%#  "?PHGPPMONORMHJKLLKJKNPWVVRKJOQRRPMLLLMNMNNMIGFIKNNKIIKE?@EGHGDKT[YURPMJIB?@>GXK/ ##""#%()$%)*,+).G\eN-"'@[YOTWNMOPX^SHJPLJOQLJKLIIKRITm|iUcdb_]_`_[]_\Z]ZQGJGENXYTWSXZQ`zmWdlffgbdfgfbadffbcfhfbchfffim^Ti"(+*'&'/08G[zپ}}~}|WERNOT[Y\]U^׻og|shj^rɤc;:765677415433222210154/-/-.01221000-.00.-,+,/2333332331/153589;;99:9:>??ABABDGIIHHIIJKMMNLORPLPZ^[\]a_daA %Aaojjja[dopnnnomnqpnomnvjO>@DFGHCEFFGIIINPMF?:DKHRMAGRWXc^WIDU^P@KTexw\9.73#/6539;87/9DD8-.60+).368;<@DL[jleb]\_a`_a`_UVdbQI^kpg`cfcT]a[WYVOHGHJJIHJSga?.43%'$ ?98<>;=>?CCBCFFFHJLLLPNLLLORTQLMPMNTXV_[__bgR$"!6]rlknf]cjkormkknmmopnothPCGFACHFCCFHHKPOKKIA@C@BBCCA@EKPLONFLXXSZ`VEIX[cghmvwomeP85;0! +3218;87748CF;.''!%,1358BGHRdjefa_aa^]^[^TWjbLHO\b\TUWV```_\[]_VMHLPMFBWiiP8001&" ""!CaSJTTICDD??CCCFIIHHKONSXUNMPPLONKIKNPLPTWXWTRUPNPPORXTUTMJNL@D<8=CDDDDECHPoE#! #$$$$%%&&)(*)))-8=H\g^M6 4RWNTVMJMQ[VB-+0,)/2--6<:7667823653441130.10/2+*.0../++****++,/2676544011357889<;9::;>A>@DEACHHGILMLNQQQRRPNPTVRNPQNQ[Z^X^^^fY- ,Yrkhnh\`kpooonmlppmmprvkPDGBADBEDCDHJJJJLJECCCACA?>@DFFLJMVUFGZQT][E@U]lld^bbZTZQ?100+()45-.6947::++(%#!!!!GjYMOOJC;8:?A@GIIFFIJHLGPYSJJKHMOMNTXYQRTQMPSQQUUQSWWUWWSNLLJGFD@=<>CGKMO\e}F!%!!!%&$%))%#)&%&)49>;E]^QI3 >XZSQQNINTP;1**,+)*.,**.4;?G[mp|mXSTTUUTRRQNOPNNNJIAFNTTQUVSVXN`zjPafba]]d[X[__^^]_`aaaabceecbg[Qg%&"#'&$%)++.560-4:DZŭw~z|y~rXMRVd`b][YS]мe}qrjjv|xxrppxtS9==<98677875542221042/00.-0/12-,,*-+)((+.16300220-/255458;9;::;<<@A@BFEBBEFGILMNPSSTSRQRRRROMOPLNW[]X_^]e[0 ,Snlhok\cmrppommnpnlnpppoZMGAAFFDEEFGIJLHKJD??AB?@A@?BFJKFFS]TGELLPWOENW_aZRRQMKKH:-***+-*,028:516:FLKKJHIKKJEGIMRNJMQSSQQTSPOPQONSYZY^^XUUSOVXWRMLKIEDCCBCEFJRRVWq{A%"""$%&&('&$*()()49>;@LLPS=$+JWQUSQMLPI6##%''%&(''&&'*-/7@JQbiUUTTSSSSSSNOPNNMHA>DJRWTWVSVWM`{j[hkc^VQT[ZXXZXY^ZWX\YUYcddegjZSpț&% !#"!('.LhpqrO48Jgɷ}vyfbgVQg~pkc[WSU˸g~tieeimkkmkiiqpQ79<;97777742474211/2/+,//.,)*+,./,%'+.23339730..131477558;:;::>??AA@AABDHKKKLNOOQSTTRPRSRMRQOQSOOV\\X_`]d_7 *Hhngnn]]ionnonmimlkoqpqpdVKDCFHDFHGGGKNILLD==BEADFB=;BIGIPW\^R?DGPZREKRQPHCFF?CCLdmkjigfecbfbei`LBBCA>?IUZYPEA@@?=C^XI=;:Pm~eRWVSRQRSTROPPMLKG>>DHQYWVVSVVM`{jVbfb`XQTPVY\_[X\Y]ZQPXaeddgkmYUwڪ$#! !)/&Hݮa1.CWi|y|oVT_QU~md[ZXQ}ǸzqkĠ}tihdcejjghfdmnR75::98777775551/00*,,++-,++))**,,,*,/23432320,*+05666789::;<:;@A@ABCCCCEHJMNOPPPQSVVUUVVRNQRQSTPOT\[Y_`]bb@ %=dqejo`]hnmmmkjiopnnnnpngZOIDBEEFGHGIKNLOLD>@DH@CEC=:0*$ ,9;=979:99:>KK9%$+28?EC@?CGNZfYKFFGC>BHB5.26=IDXukJ5)/$" -Znf_XWOB7872;NRLILMKJJOMLLMPRPMNNQSUTRZZ]^^`ca]_^VNNRTQUWSNKHFFEEFGHHHOROU]xo2$ ###$"#((&%('++-).@G=9;EEDP\H.$!*JZVSQRQE4)&#$!!$$!$$$$$%%%))+/2=O^tdRZWTQPQTVRPQQLJJHBAGJQXUUUTVULa}k[egdaVLMYZZXVVX]ZYVUUZ`eeefjl[Uuҹ!!"""$*.<̩oD76H\úxkbSOSSmrh_\\SgŽ}iwÝyrokhggfhkhfckoV:4888887767542011--++,+))*,.0-+)+/001010003/,+.1228889::;:==<'+1/2<>D^ovxywspqstovvZ;7A?=@GOVajaQKMOMKRWPD@A=EUphT\YVSRSUVUPOPNMKEDAFKRVSTUTVTKa~kXbedcYPSMOW[WY^\]YVVWYahggfej]Tlxtyzzuyh!#%%$&).##Y|Z<>EUqoxyjic]WTdype\]XXúuiηtoekmhdglptpjpt[<3678888666311252+0+*-+*,.+,+,//./,.0210-+210013559;=<:9;=<>=>AA?ACDDEGJNROQRRRSTTXWUVSQRUNQQPQOMPY[Z[]]_eS'/Yrfdjd[gnoppnmkonmoqpqocXPHBBEGECEILKKMMHC@BDCFB@B@>AGOUIDV]W[cNG[[F=>53..3204-33387/*,)$!#5779<;78;85MLKKKJJLKRSWZPPebba`][^b^[[]\\]\^^b^SPTTTUUSRSPKPLGFHKLMMKL`ly{V$!#!"!###$'''&''*(%(@[\C>76BD@LM;6-,HUINXS@."!+.-(#"##! !#%!((&')-5?BYylW][YVUUVVXOKNQSNC?=CIPVSUTTVSJblV`ddcYSYTQY\V[b\V]YMKYcdfgdch]Rh~tu{}}rV#'*'$'&&# @fAEEKf~{ywvtwuomnmhkgb`_]ZWUMR\ZWY\`[[VVo{qj]^\Sk}mvչtkdmogcgkirngms]=156789765:40/011,-*,/.-/.,,*+22-,0010/-,++.00/16;:=?>;:;>:=>?AA@BEDHLMKLORTUSSTUTUUUTSQQRQTRPQPORW[[X[^_dZ+ +Qnlfga\hononkioqnkmnmnof_UGCFEEDEEGIKKKLKEAACCB>>AB@DLRaR?GRT][ECZX=9C>ADIKC;DIINWaf]OMQSV`t|pv~yl7''"  >eja`UJ==@IG40BTVWTQQSTV]]_^LGZebbb`\\_\XZ^^]\ZXW]\RPTRSVVSRSSQQMIIKLKIPMM_o~yH "# !""$&%&((&%*($*Hd]H?<47BBFNB1+KTNQUF-"!"# &1:<72/+*)'%"! '%()'*,2APbzoY^]\ZYWVVWOLORSOF<=CGOXVUTUVRIblU_ccaWT][Y]^Y]a[Y\\XWZafeedei[Rm{||`\ %*-($&$$#-azqePCM]jypqqqnjfcbccc`\XVUUVWXZ[[RVUONQPKJKLKJKOTJNXZTRUROOTSXvvlk___UWrmίrkkedklfejsogls]=====<<7<>?AAADECFLNLNTNQRQQSTSQUWWVUROORPMNMMPU[[VY^^d^."*Jkpid^\hppqqnmkonjjlnqmjh\HEICCDFFFGJLINOIBADFC??BA>BKSXTJDBHSP;B_\KPVY``XLEQfsiVLQSLFJDD@775*&&$ !,563:JG/$/84055:IOFFECA?>=;26:;A@BJPV]cVLMQQR\qpjwzp{k3'&" AgkbTEFA>=FJ:3APVZWUX]`Z^cf_NJVb__ceb``a[[__^^^[Y^^USWUPUVROPSTTNGDFIJJNHBTlw;$$ !!%&$%)(%$+)$+Nh[K>C;2=EHJF-+MRRTM7 %/316BLLIG>>=:5/)%%'$$%+-.4:D_y~|v~}yxqZ^^^][YWUSPQRPPPL=AGGNZXSTUVRIclXbec^SP[XY[[[_`[^XX^^Z]fcbeikXStwy[^a"$%)*''"$$ &?ZgVVVWWXYYX[SEG[jmpdRSb`UTQRSTTSRQFHLOPOLKOOOOONMLKJJLLKLMNTVPLMONVLOSUl~rtfTW]Tc{~yiv{qmlhhllhhlnqjks_?26554332253223111.-,,-.--//00.,,//.-,---,3213;CEB@@>=<<<<9?A??@ACFFHLNNQUTSQOQUWWSVXWVUTRRQPOONNNPYZW[^^^b>"Eklgmb_hrojlllqmjkkijlllbUMGDHDIIDFMOLOIKLD@DFE@AFD?DPX]\ZSBATS<O[NHJLMKF<5JL30:;;;BINU[[QEGNONWlwxsvxksc5%/" Fc^ODBFDACGC:=JZ\]YUX]^X\cbVKNX_cb`a`\Z][Z[^_]ZZ\`]USVUTRTVTUWUPJDEGHKNKMRUe|g3"" %&"'&'&'&'&&')'5XfTHRSA.-8>FE2/LNKN;&"*/:KQOKDAENVVUTSQLFA5,%%&$"#))-16BZomd_gomimwyww|~|{}xv|v^aed`\[ZWTSWUMORL@@AISTSVTVWNIdyfYacb_UQYXZYWX[`c\[`_XZbegfbblaUkz}|]RbW!#$)+'&#"! #1FTLLKLMOPQNW]WKELVcvoYW_ZORSSSQOMKGHKMNNMLRQONLLLMJKMPQPPPNRSOMOPNPJLOQbuqgYU\RWj{}xtpwulimjjmmjjmmpklsaB3444433331563/111/0110/..,,.0/-,-1/../00//5<@DEB=;<>?@?>=;??@AABFGHKOOOOQQSTTTUWWVUSRTWVSPQSTSPMKNY]XY\]`dD"=dlimc\erpkmlkljilmnnpnpk^QIFGFGHHHJLNLJLJA>CEFDDEDAGOXagibOENO?AS^^adeeinrz}ptu|Y:DGFEECCGB=950*''+*3?=0.7@?<@DABK@07<:98=DIS[^LBEJKLSdqxrccgewf7"&'!$$(ANJHGDBLIHHB=CPY[ZY[^\Y[^a_WPQU]ca]]]]^_^\ZYYXY[Z\[TQRQTRTUSTVTTNIIIIJLKOQTfv[+"" !%&#&'&'&'&'&(&&=_gXIU[M5-03@HQRPSTUVOIcygT^cc`TNU^XW[\[\^\[Z\]\`ifecdk]Sk~~{ysr}sW]VU!!#*,'$# "%->QJJKLPSWXW[_^VH=9@_tpeZTTXXXVTPMKKJHGIMRUPPOOQSVXJLOPQRRPMOOLORRMOONPRZwxmjd[]YY\fw~tmns}oihgeeggeegimikrcE42233444424530.143320.--..,*,--.0../////05>EGECA?>??@?><;===?BBCHHKNOPPQPQSTSSUVWRW[ZXVTRUTSQPOOONX\XX[^`gM!" 4\kileXaqrlmljljkmnmmloroaOGFEJFEIKIJMHJKE<<@AACGHFDEGKWafcRCCJFKXaddcbdhqywrszZABEFHGCDMJGEDA>=>FBAC@:>HFFEEHC?ATJGHHIKICMXD.3:;;;AFINSSJCEFHLTartcbxqwvX4%'&%3;9@CKTVSTTSUQJbyiZbdb_VR[`YU[a`^_]YW[_ZZcgddgiYQmvoqvvsnplhtu{[\WYo "*-'"# !'.AVPPNOPSVXXWTU\`UD4=UszhXW]]]\YVSQGFDDEIMOQQQQQQQQLNMKLOPONNLLPUSNMRPQTSj}kmoh^ad``o~~towwlfiffegffefdgegnbG51113344552011015//./010/.++-//..-./.,.48@FIEBABBCA?=<<=>>><>BDFJJMONNRTTQRRQSUUSWWUTSUTSTSRQOOMMPVYWY^_]iV( !.VkiieV^orlmmkmllnnljhrxxiTKJHMFDJMKIKNQOF@BDABBCDEEIMDKPRTPKLOPT[_`_]eeiu{pm{d8)1455634:A>=?@ADHGGILJGHLGMOMLKOYRFAEGHF@-9[bKDB<@AGMORUSNKLKNX`jpna_c[[YF0&##93/,,4ANSU_ipmaUJC4*"#').33K^U>9?CB?CGEDHJHJJJIJLMRRRPPQSTPSTRNNPQLNPPLLPRKNPQUXWSRKKNLOL@;>AIRTQQTPURJazjV`cb`UOW[YWZ]WWb`ZZ^][]bb_cij[Wt|~xv}yxysze]ZLd !*,'""!!&+:NMKJIKOSVTUSQV]]YH?AUmwlYRSSSROMKCDFHHGFEFHLNPONMRSOHHNRPOONLQVUOIQOOSM]okqrccecbo}rg`dccdbccdc^`^`f`I600123344110.1440210000.-/,*,.-,-0/..07@GEHHEBB@>@?=<<>@BABA@FIKMLOQOPRUTOQSTUVTQSUWWYYXTTRPMLLMNOUYWX^`]h]1+QkhfeU[lqjlmkklnoqpnlv}{hSLMMIHIJKJKKOROJNWXTRNLLLKNTUYXWZZXX`^]]^]_ce`_gh_neG813.1/,./--,+-00.-.-/3565567<<;@BDIC::@B=5+.+@JCCEB87;?AEJIJIIDDKMQgol`YTPPGDIJ<-$.FN@./FPJKOLGPLIB:?JOT\YV_^YZ`\[]\WVX]aa^`_ZYZYXXYZZZ\ZZ[XQPUWUWXUVXUPMMNLHGIJPO[p_5!!"#$$%&'&'&'&'&)%*6JbiZHO``M>3#!1&>L52OPLJLJLSZLBBB;7973-((,49;H\jlfdf[J6)"#)1->QPD<5915;6488:;<CDCACGHGKNRQNLPURPNPUUQNYUT]mvl]RDBIMPMC;?AGQTQQSOUSIa{kV`cb`UOWYX\[NEJTZ[\ZYZ`efbfjhZVo{tx~|tkqkflp|bgcP_{ !)+&#!!%,,4HRPMLLPTW[ROSSMOWQPPOP]kkbbcca_]\OLIFEFGIFHKORTUVSWTKKTWTQSRPQVUQLSRTXRYtxmnuriffjxtl`Z\]__]]__][[YY_^M911122223.//1231////.--.00,**++,/..05BB@DHJKNQSSRRQPQTVUUVVURUWVTTUUTQMJIKNQLT[YV[`_d`;(HhjecTXkqjklkegijlnliuzeTLJKDJMLIILMLNJIT`b_d`^`_]^bhhgeeedcca_``]]ce\WZWWqhRD=A@;9768:93//30..,**+'--,/1./1/7A@:1)05;73536--1669<:67<:BEKRNIKMFD@;BLMRZYW[\Y[^Z[_^WUX\_[WYZWVSVYYWVWX[\[[YSRVYWXYVWXVNLLLJGGJILO`mT-"!!$%$%'&'&'&'&'&%.33EVSJRcbNF>,#+%!184DQNNJABLRQG@@@>;;4542-)'&%(3FT^hrrhZJ8("$("*=>;:<>=;;:9;>><>B?CDCGIJLQZ_XOJ>>GKOLC=ACGPUTSQNVSGa}lZbdb_VR[ZY^TBGL?>OTQVYY`eaefdZVhqi_Y]cdagaWZt{d[[TVw!"')%$ *2,3LTQNKKNSV^ROX]Y[blpymJ>Yv|rf\VRQ`\UNJHIIJRTOQZ[TSWXSRTUSRVVY^ZZfuovzrnor~rkc\ZZ^aa^^aa^[ZVTZ^R>222211110..11/.0,-/.-,++---//-.105>@A?CBA?>===CGHHKKMQPQSTSPPPSUVTSUWWXXWUVVSOMNPQQONLLS[YU[a_`aD!"=engaRVkrjjkhjlllmonkuj]QGHDHLKKLMMOPIFOVVUZXUUW[_bc`]\]`dh^aaad`\^b\]`\]kz[JC>;=?GCFEDEEDFFDDEEBABDBCGE=873;<:<>==627@A>:41?KA8747018>?@A=;:>FQQQXZWUVZ][Y[__ZVUZ^\XYYXYY[[YVUWZUWUTVTQSYWXYVVXUKIIIHEGLIIObgH&# !%&$%('&'&'&'&#+5/"%6@@Nc_KJI6)*1'0CLLLKEAKSPOKD>=><8@;64651-+(&*4CQZfglpeK1"'(*$6sz|tsufbliXPV^RKLNHA=CE@;=:694;?>>BDE9;ELMPM@<@?BKRRRONWRFbkT^cc`TNUZ][H>SYTXVSVXW]^__\htqpuz{{|qR\hNOo "#'(%%$ )1'/Mbbdiq|T?Skffeb_ZVTX^hqvvtrkaRC:78:>KRPT\[QTZ[VRSTTQSTY^\XZvszuuwut|zzrhe][^]Z^^ZZ^^Z]ZURX_UB3322100/32.,,//.-,,-.0001//0/038;BIJGEHLHHE@>@A@CA><<>ACBEGJKIJPQPQSRPQUSTTTUWVTUWXVTSRPLNPRRQONNRXXV]b\\`I!#4bri`QUktljjehiihjmli~z]SKELHFFINQNKNOIELQRSQTSPPSRNRQTWURQQOSQORPLLKFHGCFHB=?@=HQHAJLLKIDCJFDEGHFFHHDEJG=67=A>=GNNMK>8;=><607LNB>7154685563:6635=?BEOPLMOOQXUXR9$ %.;826O]UKGLUK<799DOOQTWXX[ZVYY[^_]WQRZ]ZYWVWTUWXXYYZVXTSWVQPXVWXUUWTQOMLHEGLJFOca? $ !&'$$)&'&'&'&'&/3.'$(02F`\GJO>+;A'9NNJLFBFGJPTJBA?;;@85236886;91&#+6;NKThulTA% &',U~}tokqgN>GD=77;946?JKHPQE>BABLTUTNOXREckYacb_UQYW_YD9?JOL[a__cdf\Z]YYZUZ``gv_TejTO`~t %#""$)"/65AUpjb]UHAEJEBGK@558.4?LTUPKD>758=><>KVXVWWUPTVUW[YRRSTTSRQP]}y~ttvy{|slkb]d_QXX\Y_c\^RWWQSa[C63+,52*/-11--00-/.'050867424;@CHMLNNHHJIGFHF?>BABA;9?BACCDFILMNNPQQRSSSRSSTUVVWYVSSUURONNPQPPPOMLRXWVX[[bhO$!2Wmh`VReqliihflojhklj}{`EFGAAKNNJIKLKPNJGNX[XQVZZXVVXTUXXURPLRONLJKJFF=9999::AGMLJNPB87855873JbRDF;9;;:;?A@C=;<>EJJMZ[]XR\\UQXQ5%%!! )' 5[`PJLTUK>98>JNNMMSVSV[YWW[ZTTUPTZ]ZXXXUWXWVWXWUXWVWYXTQTTVWUTUTMIIKIEFKJFVdO-!" !$%$$'$$&(&##'&/.'&*+-9J_\NOH5<9EPZ[YZYWSWYZ]a_YWWXXWVTSWizustzztkg`S[g^ccgbafelcee``kjY=3,-3872/.-,+,.16BIW[X_^SJEKPOPUMKMMHGIHHFCABA?>?==>>BEFEHKNOPQRQONQUVQMRTUVWWVUVSOLNRROORTRPOLJOTWVUW[]]fV,!.QljaOSfngjmkdgjkkmprl\KCA?@FEIKJJNPPOOKGHNPNPQPOQVWUXYZZXXXXWXVSRSRPQMLLFEO[bjkr~uYGPRMILKIMPJGJLKJJJHFFB>=@?ACHLJGKI=578657:6G\SJOI=>>?DJKHAAEIEEKP[hqkb_]]Thw_0%% "!5Y\NJOTSG?>; ,LSKLNHDDDITKHFCA@>;>>?@;8;A@CO\]UB..,(-B]mohV8*)'Aqwnnsronlmlpvslnxvt~yw~}v|x{}UGPONLFAACKNLIJMJD;@BEOTSQQQUNHfiW`cdaTNVVWSE88:59BA==>?><>A?@CDGGEHJLMMOQTUSPQTVVUY[YTSVVSQPPSSSSUSRPMLOPOMSWYXY[^Ycc@">dpcOQdnjhggjedhihjobMGIBBHEEHJKLNOPMOMHDDDCEFJKFBISNOPUXQFBFDIMOVZT]fg_WVapt|v^LAFFFILKHIDFJLKGC@B??CDB?>@BDIMJHKD:0---/20'1SYBIUULIPUUUVTUUUVVTPUVY[[YXY\WTUXWTRYWUUVVUTWVWXUTRNJIIJIFFIGNaZ2"%$##&'&')(''(%#$(22.*(# #-M^_LFSE5C>#2RNPMNJDCGJLPHCFF?>HLLMNMKMRRPPMHKNKJH@3')9J\hnfJ*3Zjikjgkqpqssrtwuqtz{wy|wzsqqrpsszpTGRNMPOKKOOQQMLPMB>@?CMRRTQMQOId|hUde]ZVRTVSPI?>@?98666:=??==A@A?>?DHIFHKJDF^|vtldx"%!"  #'6>;9:788BDBQ\PGY_[U?(!%(*$"(&$!$)))*(%')'&'-1004997?@CFGKNKHKH@8455799:>NUG>?AEGDB@>;=;>?>?BADLPW[[ef]d`B% &&"?[ZQX_]QABID;?ISXNIOVUUWVTVWUWWQTWY[[[ZYSVYXWVUUWUTSTTTSUSUWVUSOJIJJHFFHEP_P*##%#$''&')(&&'%#%*0.+*)$$,-4F_TGTL887$3QOMPOMHBBJPKNKE@95;A?ACDCFKLHKNLJKJNPOG:016>O\ikI.7SONOQWZVWXZ[\\\\_dfceikhkrvqnrtpmorxxiVNQOPRQLJKJLPMIMMD>?>CMQQTQOSNFa|lY_`a^QMXRNNKD@BA<==::;AEHEDEG9:axncYQIDBEGHOdqvodv\$%###!!%3:5..39?B@?EFFIIINLKJJMPSTNRVVTSUXTUTPOPPNORTUVWTPRQONOQOLNQUXWX\b`adL .Xn`HUikfggghiihhkmmXHADEBDHDFIMNMOQQNKIIIFDHJJHHJHCMW\[TGBMQ@BPRUUJ^zUIJNIJKDFJKGCDHKHECFGFGIPRRTVQLOOMJIIJIHMKBCC833-4::7777448=?EJIKRQ[[Vflo|mB###C_]U[a]PCBHD=AHPVPKPWVSUYUWWSVYSPU[]\[[ZUXZVSTVWTVWVTSTUUSUWVURMMLJHEDEGCT\C!#$'%%''%$&'%%&&%(-0*$&(#%06%-RUEMPA2)$9SOINRL>>PP>7MN>::4/1//3457:;56;9:?A@CEFC=3+-9BK[f\KNIJNNOPLNOPSROOSQRSTRQRUPRVVV]^W``_``YSTQOMKJJIGEFMPJKKD;=>FPQOQRQVOEboV^bc`SLUTOOSMFB??AA@=?DIIJHEL??r~xoe`[]gx~cmxiy]Y"#!$$"" *---'-@LHFFGILMU_F&%& &#(,+(&!&&)(#$''(%%(/3310.1421>PPLKNNLMPSPQVXTQQPQSUWXXWQX]WTf{}{{{wutppqniihaYmu^t̯i\NKPMKMMJJFDFDAADBBBBCCBBFHHHHKONNNOOOQRTTUVXXVTRQONNOQRSUVUVWURMOPOPQQPPRUWWVZ__`eT)*Rk]NZjhadfhgjkjijll[IAFEBEIDEINOMNQRNKJKKKJKIIIFDHOU\]XPA?NSDBNUPJH`ȣ^FUNLVRUXYVVZ]]YXYZXUTVVWWXZTORQQQPQPNLKF;95*)+-4;<===;A?CGFGKKJSWUXboswfM7(!#Da`V[`[NCCIE>BHNTRLMSTRTVVYYVVVRSV[^]YXXXVTSTVWWTVXXVTTVVTUWWUQKHIJIHGFFGWS3$%'%%'&$$&%$$&&&*0/(#%% &8E3(8IHGLH4#ARNKMQB(/QR14KJ97;5.-**,.-.051354574232/39817,/7;IYY>:>EDCFIKKMONLLNONPRPLNSUQSSQSSJKOQSVQNSVSNOYaZMKDHLHHKF<<>FPQORQRVNFekRae_\VOQUPSWQGCBGD@<;?FKMOOFLD>f|smpz|wmlsvljv~feO[ "# $-*(*#(@SHDP[XRVO+9D2)53,-0,*,-&'+)"#)*&,48854361145:Phkecghfgknjlruropmnqtvwxx|z{|}{tngbchfabd`\[ZQ[pqgyǽl[OJKJMOIJKFDGE@ADFCADDBEIGIJGILKLORRQPQRTUUUUTTSONOPQRSUTTTUSPQTMQSOLMPRPRWZXUW[\_gZ/(Oi\OWeifeeidgjjiikmXGADADJIFFJOOLLPQNKLMOPQPQQMHGKOLQPONDCQRHBNYNELgwbiiehicddbdeb\[XVSNJLPOQQTVQMOKKIGEEDB8=86433.39>ACDA=@?DF@BTeSNTRYd_aZ[N7%$ "DaaVZ]YLCCJF?AGNUTLHNRRVVYYVUTTUWUW\^ZY[XVVXXUSRSTUVUTSRSQSVWWSMLLKHEEEFMXJ'&$$"#&'%&($##%&',2*'&)'"0IMH.!L\ih\V]\RNRRLBIC>CB@GA63985796789;;;:==@DC@CJJEINMQSLJOOQVSR[\YWc{sWKBFOLJIB>=?;76637?@>?==A:8CECIJIMV`b[S@?DJMOJ@9=@FPSPPPPQKHdxcU```]QNTVOQH?bZQ`jieccghhhkigkhWJGFEHGFKHHMRSOLMNKIMONMOQQRUWZ`^`bcgnrrty~z{xpnpnijje]WWTQRQPONMLLKJIJLNMLMKKKIFGJMEDNSOKMJKLMMKJH=93/-,*)').6989<@DJOPNLLPQX_]\hzlE% "I`[TZ]VHCCHH?=ELQRSOKRWTUVXWUVVTUTX[ZY[[YZZYWVVVTTSSTSRPRPPTSNJJKMJFDEEFNK3! &$&$#%&&'(($$&'.2-"#("!>YWDR`D#8PGJ:!%FOIVM18@5FWC/88422.-1//641Fbsyz}~~}ryzjemk\W`cXOPRLEEE>=>;89:<86:@B?;69AHKMI@8;>EPTRSRRSMJfzdXbaa]OLQRUVD7FQOPPQQQRUWU[[[VLPdmdYV]ebZ[aZ`pijώQ__Na #"!""!)2+%-#4r]tH"2TF3/563521/-,,,,*)'$%)06987544442230J~v±bHFHGIGFHIDADECB@ADGIIHGJJLPPNOQSUUVVVURONMKMPSSRRUUQRUUVTVXTSTQPQRQOMNPNPTWVSY``X^bG8[ZTdmidcdfeffihgjfTHGFFGDCEDDHPUSNKNMLPTUVUWVVWUUY^`abdghfeeffa\Z\]YVWYXXY\]\XX[\YWVVUTTSSTUXZ[WRNTQNLHEGKQCBS^WOOPOMID?;820....--.-3@EB@B>EHGHLMISRW^`^epjO6(! $G_]U[[VLFEGF@@HMOQXSFLWTSUYYWWVRUSX^]ZZ[\\\[ZXVTWVUUTSRPPQSTTSSSNLIFFHIINA) !$$#%&$%&()'%$$(01)%&"1VfXCOfY((JID>,1LQKUA.=6RlP8:6<21./2127:6G]k{yqoqk`htrgn{wim{~uu}|{{oo{d@;B>977998;>DGHLMG<>?DMQPQPPRKHdxbV```]PNTQSS?7ITQ[]``^[XWYVTVWVY^pw}}̂W_]L[~!"!! $.5///>a{+WM2-4026,********)&$'-6;>:86424567706_~yvvxry¹QEHKGHGGDFDBDEBAEDDFJMLLLJMSSPRWUUUTSRPONNMOQTSRUSPQTQPURNQSORURQQQPOONONORVWWY]_X\eP%2S\WdjhghfdcdehghjeQGFD>;5406=?DPA?OYRHFCCBA?<989876530///4<>:79)2JQQS8'@@PnlM83=3101212697DXct~xyjcc``dbbafg`bkm\^kpffpsslnwunp{|oszwD8C@=9779:FGIHGMNG;<=BLQRTOOQKGcv`T__`]QOUWTRB5:BFPSX[^`bdheff__^X[fw^[bMMr!! ! (:/#.3 EhACF-34220+(()*++*('$'-4;=<:87533467:26S~~xx{||~|wqlnnqgCEHFFECDBFFCEDDDGHJIJKPSMPSSRSTTVUSQNMLLLOSTTSTUSSRRUTPQTSVURVZTQQQPPNOOLOTWYWXZ^Y\dX-+L]]aeilhdefggiijkdQHG?3*#"!(17AJPPNPKB?>@DADB@EHJKKNTWY\afhmoorurkjlcWZgg[^_\URPMHLLKIHGFECBA?ACGIHFFFDBCFKDCMVSMJEGIJIEA>973/,*++)-268940*++***-./6CLPOPPQRMIdv^Vaaa]PMSONMA762259=ACDFGJOVTJN]eillkjkf_[accb[X^dYNL`d!"215/+",hw]j/7>0-/0,'*+$'&$$%'*,:;=;9789777765327Ad||{|zyxzzxvonquutw|wxyyyxxx{vwy}ytvtonprq^^d^ZVhLFHC@CFEBDGEBEGHIKJJLPQPNVPNQQMMQNMNPPPRVWSRSUSRQUTTSQUWSRTVUUVURQQQPONOOPRUXYYXW^`[^_<!;UfiheecbcaddfgjjaN>70/5:>?91+).;GMLFBDFEEFDCGLHFJLJJMMJHIMGHQSOPWV^^VVbkkpsspnnkgdcb`_]\[VVUQMKKLIFDB?=?CHKNOMF?:531/.../147899;<:@JSWWWYYXY[\]aeddefkrx{uN%#!.Tc^Z_YPHDFIFBFMLIQUNFIRVUVXXVWXVSTY]\[ZYWXYYXXYZYXWVVTRQVVVTPMMNLFGFCFID1! !"&##)&%%&%%%%%$*1*#%,")')-+&%()&$#'.6;=:8788658999740.Lm{|{|}{wx|y{|}||}~}ytuxyncf]^]cRCA@@ACFBEHFFJLJIIKLMLMNPONMNOPOMNNPQQOPTSRSUSPORTQOSXTQTTTSSUTTUSPNOQQOLPTXYWVXZ^bZ\`@!5Plgcee``hfhghhji^I:68>EEEDDB;.',7LKC;;<<>BB?@DFFHKJLOOOU]`dmx~tvslfb\USRPNKIGFACEDB@ABC?:8547=><97545712468:;<>?@@@BFIHIQ^da_bbcda]^djlmkgfkqtwP."!!/We]Z`YOEAFJGBFLLHNWQCHUVTVXXVWXVUSW\[XY[\YWWYZYWUTTTUUTSRNPUSKIMIFGFCIH=-!$$%!%(#!$&$$%%$%-1' '9KH@NULIC:+,75* .J:,01;==<<85:=9844ALHDFJDHMJILJLMPQMMONRPJFMROJFMPORUTRWUZ`]_fjfmhentoknoqqpptxrttpmsskgoqwzcHEEEGIJIFCFMQMJNJ=7;>EOQNNPQSNJcu]Wbaa]PMRUUTJB>9<6654358:>;:<=<<@?<>EJLOTWZVW_\TUJGMftebw " !+<3-,2)6uk_rgA4,&*(%$$#$&)''+39<:==<=<?>@GE>CNMJKPPIJOOMHLPLLOOLLJJNNLMNQPPTURQVVY^a``cljkonklqoutswnUBNOKGJIINHHKNNOKA<<=DMNMPPMPKJgu^Raa_ZNMRRPRNF?8:<;9524;BCBDC;6@MWPJIHEDE?@CFHHGECUnud\{!$0:50.2-!%6IXe`S:$''()(%$%($)18;<:9;;;:987676;;/2Y~{y{zxwurqnkkostwy}~~}yuszysqtÛfQCA>>BBGGHHIJJJKMNNLMPSNQSQONPQQRSSQQRSQPQQPORUUUUTSTVWXUTSQSTSSSRPOOONORVXXXYZ[^\adN'.Rnrh`acc\bfgfdllY@>FIFIHDGQRNJ7'-HJFHNLIFMMLMQSSU[XTSSUUTVUSSQMIFLKKLKIGHGEDFEBBE?ABBBCCBEDCDEFDBHGDCBCCDDDGGBBEECEFHHDBDCDFFFFGHFDM\```_```aabeikkkjgirzoA# " 5\dYX]SJIECFGACKMKLQMACOUPUXYXXWUSUXYVXYWYYXXWWWWWUSTUTRRSSSSPLKKJHFEFI@/"!"$$##$##$&&&$#%%//)/:FLNF?CFB>AB=-$.6/!&)(,/787@CD?;8/.982266215759<9<@?ADBBEFEFIKKLMMMOLNOPRSQPQQQRQOMMNNOQQQRTTWXXXVOLKMHEHGCBGGIJJKI@9;>FOPLMPMPJJgu^U`^][QMPPPVTKA8:888768>DADFKH8Bf{pfdd_WRTNGCFLQS_qt\S{€#&7:66761,$2LQXX7!*%)'! &&!/5;<:778<:7558;==78=Kn}zspqtuu|}ʲpUE>AFFEFGHIJKLLLMNOOPPNRUSQRRQQRTTTSUVRPPRTSQOSTTTTSTUUUTRQSVUWRNORROLQTXYXWXY\_]]dV0)Mpre[]`dc`eggfljT==?@A5&&/1'!! ##(+.114@B=>;<:69932<916844668:=?AAABFIKKLMJLMNNNNLSSSQQQQONMPPMMRTONGFT\WOGEGGGJH@9;>FPOLMOMPJIhv]U`^][QMPRRVOE=9=<=;879>BBCDKI><978968:;;9756=Tqymllsz~||ʽ`B?EDBCDFGIJKJJKNQQMJMQSSRRRQVUTTTTSRPRUVURQPRSTTSTUVSTTTTVVUSQQRQOPSQTWYXWWY\`][c`;$Ffia`b^bjc_aihgdV>=@?>5&!+)&($!&(*--7AKJ2 &$%).;RZO5+Dah|yqvwkbhh]R^]OGJIC=::=;>>;647;746678;=?@@@CEEEHMLJQOQSQUWSZ[W`v~lZDCFHIKIA;<OixqmlkjmrqmhghjkjladΠp#"0<<787585,&%$IvW#$)(*(&*5AFB@=<=;8587555689So|uw{rtvmmpkflqkheeffc`achpzWCBB@ABCFGHIIJLNPOLJNORTTRRSUTSSTTTSOQSSSSTTSTTSSSUWVUUVYWUSSQQRQOORORVWXWXY]`^ZcfE >Yffghbbif^_ijfdX@<@CEIGHNHKLSK>LcQABFDGFABA@A@@ABCCCCCCDDEEDFGFCHFFFFDDEIFGIJGFFGIIGGGHGJHHIKKJHKJIFDCDEEEBBGFDCAEEDFGEBEDCBDFHIGBFTbc_][_ccfhhdhgknhcod2"!$C\^UTVQQNHDHKDCKOOKJHHKOPQQRTVVVVVTSRSVYWSTUVVUTSRPQSUTRQNQQLIIIGFCBDE;,"!$#!"&'%&')(&%%%#4QYOA@A?>=0#"*! /1& %'%)(.AWV;! %%';Yg[7Ah|wsqwsgdih]VZ\QIKKDA@CA;8;<:88;<:7?<::>DFBEGFNbfXI>?EJKMI?<<=CMNMPNMQHHhv[Raa_ZNMRNTR=5BKNFKMKKMLJFDKOG>J\d]VSSU]fec`]\\^`[~qX#!)7::6:6244/-!#V[!$8#&+9WsviaZPKIHDA@=;>@CEGMMJOQPI?D_TGFGEFFCFGEDBCFHGEEEEFFGHFDDGFDDCCEEEFHFEDFGGFEHHFFHKKIJJJJJJIHJJIGDDFHFBBEFHHDBEDCFHHGDDEFGGECECES`b^_\^aegigdiikmihs^/#&HbaVTRNOKGCHLEBHKPPONLLORSRRTTSRRQRTURUVUUUTSRRSSSSTUUTSTOPOKHHHHEDBEB5&"###!"%''()('$$'*BtrL?AAJE==A>:=DA?;8;3"&%&0/,#!'$*3B\[; "*,(9YlZWg|uy||}zpowtgckd[UVVOIHMH<9><798857>A?A:=CIJKF<9;>FOPLMMMQGGhv[U`^][QMPPUP:4HSWX^`][ZUNKNNRRFPrwttstvpomihp~ӌY^! !/8899798448:#,oZ"$<,+Fx~}}~vvx|zNFICGLJHKMKNPRTVY[cgmqsrpnsuu~~gLDDEEFGGGJLNLJILOONOQRSTTQRSSRRSTRVXWUUUUVWWVVUUVUVTTWVTURSRONNNMNQVWXXYZ^_`^dkQ& -PjkejiaYbddffibI9C@=98>=1" 1@A%-,/EXX6'#+%+?Qfnhq~y~}~{jlt[???<8:<417=?DHIJE;9;>FPOLMLNQGFhvZU`^][QMPSTM84AJLV`impsnfZ_[[\U]wΐYQe"'156;8+1<7366#1\$!/)W}u|{~oyIIWKIQRRVYRVZ\\^bfafnv~bthOHHHHHHHHIIJJJJJJLNNMORSRRQQRSTTSSSRQRVWURSUVUTTTTWVTUTQRRSQOOQQOOSVXXXYZ^^`_dkS* (Wg_Ybea^`bcede[@6=EFHKGHIPNJJGB=DOMJKHEEGJJIIHHJHFDEGHFCGDCEGGGIIGFGFEFHGFFFEDFIGHIIIJHEJIIIJJIHHFEEDC@>?BBABAADECBEGDBDCEGFDB@@CCEQ_`]a_]^eihghgiiggmu{U,!"  .R[ZUXUQQJDGOMBBKOOQQQRRPQOMNRTSQPQPQRRUURUTRQRSUVWTSTUTSSUQOMIDDGEFEE>,!%%! #$$&+('%&(*)(3kM;::;?<;B1  8F=$ #!(58BZM4#%,,;Yqpr|v@8D917?95=CCFHHIF<<<=DMNMPLNQFFhvZRaa_ZNMRPOK<4866FLPQZgnmlopkdb^Z_bkx̐WYfT$-/-/2-..58464,>m-3Dd~~ymuvusQIPQSRRRVXXUUXYXY^ahr{uwwrvoSkDZwOKMKFFIF?BEHJKKLMNLNNKMSTQQTURSWWUWVSRSUUSUSRTSQRVUSSVUVXUSPNOQPMLNTXWXZYZ]`^Zf^.)Ne][b^bc`babg`K98EGDHJHJOSOJLI?9EONLMKIGGHHIIIIIIFFGGDCEEFGHHGFECEGGFEEEFFFIKHDDGIJJIIIJHFIMJFHKGGHFBBEF@CDDEEDABBDGGGEBAACECDGFDFDK]b^`_^`effjonefmcfxsG& # $ =[]VXZTQPHCFOPFBINONQSQRRPQSUTRQRTSRRTUSTWPRUUSPOOUQPSTSSVOQNHDEGFDCFA/""# "%&&'),&!-'#)%)C~Y4>6<@?=A@=?>==8:<:A9" 3HA5! #$ +=FLC+%#!-6HclovB5@???=9:89COOMQLOSEGktXV_^^ZLJPOPKC=:859;>?@ELRV[SMXgmnnj^YadbgvlW[bZK(-+(,0/-,48455.3Z@hxy|}yyukktzuFCOOOLIHJLNJJNPQUZijkhfgkpdebkor{uu~WKLF;35=DLIGGIKMMJIKLJMRSQRRTUVUVWWWWVUSSVRPPRTTSVVSRTUUVTSPNORQNNRVXXWWXZ]^\Ye`5%Ea_\^[`a]abbaVC7CKHCEFFKJPMHIJA9BMKHIHIJIJKJGFFFGEEGGEEFDDDEFGHHGGGGFFGHGFDEHGEFHIIJJJIIJGGIHHJJHHIGCBCBCBBBAAABA@BEDCDEDDB@@ADFDFCK[a]__aehhghiogiofj{tC% # &!#Fa[WXWRRPGDGQTGAJSNNRSQQSQQSUUUUVXVSQTVVVVSSTUUTRQRUUQPSUSQOKHGHFDDDE>,!"#!$%&(*-(")*)\aC><=;B?>:;<9A:$!"0F?:5 #'/9GI;(! ,-:XkrvA5??@>:9AKIHHFFKG;99:BMQNOJMSGJnrUV`^^ZLJPNOH=9<:4679;=@ABCACDCKYb`fffifbea`_bfhge`Z]hfXRV$.-'%)/2-*38456,FQt{qosw{yvw~ǫ|tsqicqyGAEFGFEFIKMKNTYZ^chlppoqy}{}_GGE>3.5>@@ACFGGFKKNNLJKJMQQQUTRSTPNPQOOPQORSQRUTRTUTVVTRQRRQRQNJKRTUXVTW[\\ZWdc?!<^e`^[`a^acc`RB?@BGDFDIX^]^_cghhiihmgiogm{o<# ! !% .SfYYYTPSQFEFQTH@JTPPQRPQTTRSTUVWXXSRSTUSTUUUVUUTSRVTRQSTSPPMJJJGEDEFD7' "$$%&&&&'((%'),$%Eyk7:C2).<=$ /1%'35?A6*$%(#.PmwwD;E=7;CFHIIKLGFJE58;>9;8558;<;B?BA9=EBBJPU]][]fjnnkiiknie\IDd*3-'&(,4-)39567%V~dc~tostspmntz㿡zpqpjao|TDKMOPQTW[_^afgdceorvwvwy{xsw{tsvmp¯{dJFFHE=9::>BEFFGHHHIKIIIHKOQQRQQTRPMNNNNOPNNPQRRPVVSRSTSSTSPMLNMKKPSTUTSU[[YWVadI3Yfb_^`^___a`TFB@DCEGGHMOONMKF>:@KLJKHFEGHHHFFGHFFFFFFFFDEFGGFDCEEEFHGECIHGHIGEGHHHIJJIHEGJJFGHGGEFGEEEB>BB??CC@ACCA@EFBCEDCCBBDDFCGT]\]`dgfgkmljeglfmxf4!  $!>afX[YQOTQFFGORFBJQQOPRQSTRSSRSTUUUTUVWWVUUQSTTRSTVTUSOQWUNMLLMIDCFEF@0""%$%%&''()&('&*"-[q43E:3('4<=A<548=;7C="&/"9?B8"(8.%-6<4$ -,5?<-###+QqzuD>B==CHHHIBGIGDIG=9;;BMQNNONOBHotVWb`^YMKOMSRH>>??@<87:=??>=?>86@MGD@AFIFEFP[a``di_ZNBEc.2,('&'3/,59677-BSl}wso{umebbef¥xrssk_kzXFTUVTRRTVRPQVXX[_voga^\[YYUYXPMKFhíyiQIFKNJED:=ADDCCBEEDDFHJKJKNOLMPQNPRQRRSSUTRQTTTTQQQRUTSSQRQOOONKOQVWTSSRZZVUU]cQ$*M`^]]\W^\ZZZOFDEEEEGGHKKNOLJHC<@MNLKHGHIGFEGGGGGGGFFEEDCDFHHHGGGGGHIIGEFGFHIHGJGHIIIHIIFFIJGEGHJGGFDDCADAADEBBFD@ACBACEDDEDBBCBCEDEQ[^]`ehggklkifhkfou\,! ! &#Pk`Y]YPPTPHGJRPECLOPNOSTUSNSRQQRSTTWSPQVWTORSTSRQSTPTURQSSRLKLLIDCFED:*"%%%%&'(((''%&'"1  /WsyqB>FA@FKJHGBEIFABEB<9:CONLOLMQELpsSVca]XMKNKOQME>>@===>@AAAC@?D=,IwcUMHCBAABFGGFGIKUt .0)(&"#0117:797:.Cyibdgvoe_]^^^~{wzvk]iuwMCLMLJFEFHGDDJQXaiaXMHHIHE==B?=>:>¬yiOKHJMMMMBBCDFGGEDFEGKMKMMKMNMORPPSTSQTUUTVTRSRQURQOPSTTWTTQNNQSSNOTWRRTRYYUUTY`U)%E[^]_ZU]]XSOIDFEGIJKKJIIPQJEIE9:ILJHEFHJGEEGHGFGHHFEFEDFFFFGGHHHGFFGGGGEFEFGGGLFHIJHHHJJGGHFGHHHGGGDCDBEA@BB?@DAACEA>@B@>AB@BFEBDCEN[`_bfjkjigejjklhqsS&!"""&.bpZ[_XPRTPLHOWRCCLPSOMOPSTQQQQQRSUVYVRQSWVSVVTSRQQQRQRTSQQSNKHIIGEDE@2% "%('&&'('&*$#)()PqB6<;87-/=+ $2;4,83$8/+A>&9B% .IP>-&'35(")5WrvtFFOD=CKMIEIIKH?>@?<99DONKOMNQEInsUVca\WMJLMLNQNHDDA@??@ACDKLGIE4Qrne\USUXRf!./))% #-46::9;7.2f~z~}|~vaX^bkhdaabccǥ~~xh`l|rtE>EGGFEFIKLIIMQTY`OKHHJKJGINSNV[Vcwp`JJIGHLNMNJDACGKMEHHIOPNOPPQRSTUTYXWUTTVVXVTUWWURVWVVXVTSSTUSRRRQJMRTSRSSWXTUTV]V0#@Z^^^YWYXPGFDCEAEJLNPOJLPSMFGD9=IKJJHHHLJGFFFFEFHGEEFFDGGFFFFFFFFFFEEFGGHHHHEDHGHIIIHIIIHHGFHIGEEHHEDDCCCCBABBA?BDA>@DC?ADEDBA?ACCDM\cabeimkgedjkkkgqnJ#"$""% ?poX^_XRUTOPKPXSDAKPRONMLOSTRRRRQQTVTUURPQSSSSSSSSSSSSRQSURNNLIFFGGDE<+!"!!%%$$&)++*)$'+);il:2A@13<1 ",+$3;.",)$76$)@@'%A>! )CWTF3/2)$.;9Tq{uJLIFFHHFHLGHJGDFE><:;CNOLNMORCGltXUc`ZULIKMLNQSQOMMICABDEDBFDEE>V#02,+%!&*5:<;:<7/L~jbgihiiigffgѭxeepvr}G=BDEEEFILONQVWSRSVWWY[]^__edYabXjicUNLE?BKOMROJDABFIONGDGIHKJOQQSQPSTSSVYYYZZWXXUWXWWZZY[\ZXUWVSQQPNLRTTVSQTVWTUTSZV5:V\ZYUVQNB:>BA?BEHHJPQLNMPQKGE@FLGEIJIGHGFFFHIJEGGEEGGEBCEGGGFEEGIIHGGGBEHJLIGJHGGHIJIGEILIEHIFEFJJEBBA?>@CDA@ABBDEBABA?CCCECBD@CCDL\ecccfjjggjhiihdniB!#%" "$%LxnX``WSWSNRNOTREAIMLMRRNNQRSTTRONQSRUVUSSROPRSTRRSUTSSSSRPOKMKECEGGE9' #"!%'%$&)*)(&',*)L}e>4999:>=, .!&6;*#" %59))>=!$F?! %9S\UG=+(;J=QtpDGGCCJNKHFGIKHGMI;:<ADFBFHILLLOOMLKKID@FIIIJHEGGEGIEDFFDFDDFFEFFFDBDHIHBEGGGHGDCDIJEBFIFFHIHFFGIHIKJGFFGGGFEDCC@?@BDDA>ABDDBCDCCACEA@CDECDEIV``fhgiookjhijegr]. # $)3iwcXd`SQROPNJQSLECHPMNOOPPPPPRRONPRSTQUUPRVTVRPQSSSSSQQSTRPOIJKJGDDF?/$  %&&%$%'('&",-"/cS936;96;;5/$,067&"",130/<<$9. ##3HTUXE4KS;Pw~sLGG@>??@>8=A@<=@?:9;>K`}~ݽbOTVSVXVVXYXWY[[ZY\\\\\\\\^]_Za^ZzRGJIFFJJIJMRPPTVSOMFJJJOUSKGIJIFEFGGJJGFHJIOQSUVVUTY_a^\\ZW[UNKKLKJJPWZVRRUVUPMNLOY?!,GWWZ__mbG5;A> ".82*" 5JS\QCRN;Os~oKJUOMONONGJIECGKE:<<;BLNLLJPVFInqNR__^XJJRSJHMPRRRPSSQSZ^^_^[\UEY!/5474.,+168::60RN?DD@AC@CB@BAACDCDDHUacfjihjgdgijhksf@"#"'#/Z{^SnjYUXURNPNMMJDFNQNNQROOSRQRSSSSTVWTTWRPWWTSSSQQRURPRSQNLJIGDCBA@3(""!#&&('&')*)(,/(+NzvH449?:6<=:9,/=3 ($%-374/63,AICGF4#""!0DWVINI?<?CDBCCA@DCDDGTaeekmllhehjjdjs]3#" #&";BKKINLJKEPrqRYb\Z^ZTOMNPPHEGIJJHDFOZ`^`^`WCS$0436622++/5:71,7DD:9:7554?==aeOWXTUWTUWTWZYXXZ[YYYYYYYYX\Z[]QbdJLMKMNNOONMNNOOQRRQRWXVVUOFFC??BGKMMMNOPPSVRRRPNKHFIFDFFDCCBDFDA>==8::7:AGHLMKMRTZfc9 JjbZ]T=3>648;BMOKJOQTRPLLLJLKIIGFGIGFDDGIEBEFEFEEIGDBDDCDEFEEEDDEEHFEFIHFEGFFGHGGHGEFGGFHJIHGEDA@?@?>>?AABAAA??CD@>@DDBCDACCEDFSbffjjhhghmjibgoR,!# "# &NxrZ[|y`QTRONPKKPNCERQQPPPQQRQPRRQOQUTQSSPRTPPMMPTTRQSRQQOLKLMLGCEE?7& ###''&%$%'('&/)&?l`606<<52:;7<7)+5'"-$(1334/-%!4CDEFKRXYQB2%")"?U]SCRo~nOV[WXXVWXTQVYUTRKAD>;CLKINKPQCMrqNSc`Y]`b`\YZZXVXXYYYXZ\]\X[[`ZHY".1/21--+),496/+<>;79=;6;7>@9=bbJUVQSTRSUUXYWVXWVXXXXWXXX[]\^[Rl~cJIOLOQPPQOLNLIJNRPMRRRSW[XRSLDABEFEEDFHHHKNOOPPPOOOQNOSRNPVQOLHFDBAIIGHLTY[^_]^badnf? :[YURC1?ADE@@EDABD@@CC?@DCCCDDESbhcggfhgeijichhI($"#"2aqWX}eTVTRSSOLPNEDMQQQOPQQRQQSUUUTUTPORQOOPURQSTSPOTRPPOMKKLKFCEFEKNUSQQURA0*!"'9NRBNnnMS[XYZWXYVTWWUUVMBB?=CLLJMGORDMopTUe`X\^^Y\^`^[^^Y_[XY\^^\\^^a[HY$16574-++')395-*EJB8865:<:99=5EbHUVRTUSUWUVXXWWXXXXXXXXXXYZZ^XTv|r^KFKMQSQQSPLOMKILOOKOOQTUVWZUTTTSPJFGEDEC@AELKIHHIJKOMMNLHJNMMLKJIIHMMPQQNMMQSRSUPOW`D1P\WI60?DDKF4*.5@AA??AD@ACCBCCAECBBBCCCBCECDRcjdgggkhcbjihkcB&"!#!"Cq~~qVUu}hWYUTVRRQOMGFKRQPPPOPRQSTTTSRPSQORUSRUTSSTSQRSTRPPPNMMHHEBEE8'"" !%'('&')*)(,&8^x~Q0564:32>=597 ""(0/%+2.2>6#&>GC>RoqNRYWYYVVXVUUSQSTK???>CMOKKHKLDQqpW^f]X]^^\WW]a]Z]aa^\]`_[WZ\[_ZI\.=DEF@62+&(394,)BSG4;>68=:8;D87hŪcKTUQRTRTVWY[[XUVZYYYYYYYYVUW[TT{vdWROPMRTRRSQLMONJGHJJEJMMOVZZYYYYYXWVWSQPLFEICDEGGHGGHHILMLHFGIKIGGKOLJIJJKQZVWTUXX[ffP*-G[V?2:<@GH>1+,4:AHJJMNMLNOOLIIJKIFIKHDFEFGDBFFGEDFEDEEFEDDFFDFFFCCEFEFHFEHIEDJGEEFEEEJFCEFFFGJJHFDBA@@AABAABB@@AA?@BB?CDCC?AHBCECDRckllgfjhddijmo`>$ # ,QyyxrWTkxgWZVSVQQQSNEFPSPNPPNOSQTUSQRSSRQPPRSRRLORTQOPSQPPPMKKNDEB@DD4!"!%(&%$%'('&,&?iz{H*486:44A?698!&.$./#-701?8%,?AFMKB5%"!$,;Zr{sRWYXZ[WWYXXYVSSTLA=??DMPLJLOK?QwqMVa_^^WW\Z[]ZVY^^_\[]``]Z^_]_YHZHpA3'.-/4'(CRK<6:A:4::1;?8LĭbFPURQQPRRRSUVVVWXZXVWYYWUTZ[`SUt\NMMRRTVUPLKKMLKKKJGEFEGLOPSV\\[[[Z[[ZZYVSPPQPMKJJJHEGIHFGKNMLKLLNQSUWWXY\_bdfhgdejmlpY,>aV80?@BJL<+**2?EJKLMQNN>MypEPc_Y]YX\[YX]c^Z\\\^^ZWX]`]\`XH\gĻR.%0..21FYSFA>;3;==5;=6B{ɻaIQSQRTRTURTUVVWWXYXWWXXWUWYX^SXnYPTPMRSQNIGGIJMPOJHILFEGKMNORXXYZ[[[[YZ[[ZZ[\XWUUTRNKIKJHHJKIEGIMOQRRWWWXY[\\`cdbceebaS.E`K/3>ADJI9**+0:CHJLOONOQQNMNIHIJIGGJIIHFEFHGEEEFFFEEGFEEFFB=DBACEEB@DCDFECEIGFEFIJHDGGFGIJIEFFFHHDAA@??ACCA=@AAA@ABCDAEF@CGBAACCGWfkhgghecdhihir^4"!##"$#HtsxqYRZ[[[YVSQSQQRNDFPURQRRQPPTSVWQORRRNOSSQRSSTTSQPQQTQOONLJJFGFC?7+! ###$$%%%&0UwzwzB-437:62:<;<0$)<3-,"06/0<9("3DA," '-..16:<@PbZ<&#5]jMU[VX[WVWWUUUQUUJED?>ELOMMOOPAMwlBH^^X]]\\[XXYWZ\Y_\[\\[]``^^c[K^c_+ 'CU<=WSHG@>?<9;9;>9;\ĺ_JRSPRURTUTUVVUTTUWXXWVUVVYWV\R[oZOTSOQRSQMIGGCFHHGFGIKHFFGILPSUWYZZYYXY[[ZZ[\Z[\]^\YVRTTQPPNKNNNNPRTU][XVWY]`^aa][YUPTO2>R;(4=AGJE4)*+.8CJKLPPMLORPNNNIKMGFHFEIIFFGGGFHHEDEDABCDDDEEDCEEEEECAACFFEEFFGEEHJJGEGGFGIKJGGFFGFCBBAA?>@CC@CCCBAABBBFEACDCCA@CEIXglffhihddgjfgqZ/ "#$!"(Ry}r{v^UWWWVVUUTRQQSOGFNVQNNPQRRSQSSOOTUSQRRPOPQQRSSSRSTUQNMLJIIGECA;0&  ####$$%%&:c|wvwA,5459529<;1" &('&%$&+/1159BQXG0! +5hxjMW[VXZWUWWSTVSXVICC=;BKNLJHKPAOvmFB\_\a`[W_]][YZ_^_\Z\^\YX[YZ`ZJ\et3@17URJJ?;E?8;>8GZDSTPQSRRQPRSTSTTUUWYWUSUWVTTZO`si\PSSRPQSSQNKJJJJIHFFEJIHIHHHHPRTVWWVVWWVVTUVYYZ[]^_^]Z\\ZXXURSRQRTX\__]YXX\`cZ\[WTSPKa_A,:2)1/(*+-6ELLLQPIGLQROLHGIIEHKFFHGEGECEDEDBCFEAEEECBCEGHFDDHJGABEFEDFGFGFFIIGGHGGFGHIIHHFFGDDCB@BCCBCBBCCCA?>??BABCABCBA@DHLXflefhkiedglfhqV+ %!##".[~v|nWPVUTSRSTUSPPRRJGLTPMNOQRSRSSSPMLNOPTSONQSSQPQTVTRRPONKHHJIEA?7+"  #####$#$(EmzotwA,554741:<<;/);>%,(#45-3;2$,5.%-5692/064-,2*/335@MV9'#=|~jNYZVWZWUWVRTVUXWHBE?>EMPNMFJOBOvnLPa^X[\\Z]\Z]_[Y^`]Z[]_^][Z\c\L__hl~)9PNMSE:?F>@C>?@6Ap\HRUQORSTPQRTSSRSSTVXVTRTVQQUXMgd\XTUTPPONNOPQQKLNMKJLNJKLKJHGEJLORTVVVWVURRSWZZZYXXYZ[Z]^\[\ZVSTUWZ\^_[[\\\[ZZTWVTUZ\ZfgL!  ./-1;FF;6;@??C>635417CNB8$H|hMYZUWYWUVVUUUQVUJEA=>CIKKLLORBOtmOYb^[\Z\^ZXW[a\X[[ZYXY[\[\Z\b\L_Z}o@NUINYODB?;;@==D<:c]LOUROQUVRPQSTTUVWUUVUTSSTNPUVKpuZUWPQRROOPRUWVUONMLLLLLMONMKKIGEGIMQTWYZYVROPRUTTSRRTWZY[\[[]]ZZYXXZ]_aaa_^[YWVXZYWY_cb[aO)"./.4=CJE4(%&)/;GKKLQLJJMMMNQJIGHIGEGFGFCFIHFEEDEEEEFDCCDCBCDFDAACFFDFFFGGFFFIFDEFGGHHGFFFFFGFEHHDED@B@?AA@AC@AAA@@AB?EB?DFCDDBGNQXchffhjiffjkhllH$#&! #".b~urxfXWXVTQPPPQPOQTQGFOTSSTSRQSTPPQMNSTQPQSSSRPSQPPRRQONLKMLJGFIE?9/&"" ####""!!0Zvuqw~~K.562310:=<;-#0@<""&:7,7<-!$6C@9=HTMB;72-<4Y|ZHNTROQSUSSSUTTSTTUTSSTTSRPQUSLykTUTJLRSPPRWZ[XURPNNOQPPNPRQPOMIFFGILQUW\[YVQNMMKLMMNOSVUWWUVZ[ZYWUTW\bfgd_ZXWWX[]]ZZ^^\ZaU1"+,/7>BIC1&%#'1?HJILPNJHJMMMNKIGHJHFHHHHGFFFCEDDEFFGHDDEDA@CGDDEDCBDGDFGFFFEAGFEDEGGGHGFGGFGIHFHFBDFA>?ABBA@@BBBBAABCCABBBDEBFDIQRWafgfgihfhlggkf? ""!!# '[xop^TQVUTRQPPPMRSQMHHNURPQRRTVRNPROPTTNRTQPTVQOPPQQQRSOLKKJHFFDC=3)$"! ###""!!!?hxqvyxR0462200:><;- 0G@%&;6*8;)!0@A96>FKSXOA;BJTTV\^WI>56:>:&/^eHVYTVYVTVURTVUYWHAB>=CJLLLJMNATwmQX_\]_]__^Z[\[^_\Z\\ZY]``c_]`XJ_\phQ5.*!'( !" &!$%$125U`KOTQPPOQQRSTTSRRSVTRRTUSQSSUPMjQNNHPSMQPOQTVVTSUVRMKMQRSRPOQQOKIHHILPRY[\[WSQPHJLLJJKMNOOMNRTTSSRRTWZ\`^[WUTUUTXZYZ\ZW[aW4 "(08?BIB/%%"&3BIHILPQGAEMOKGJJIGHIGEJDEHDADFHECCEEEDFDCDDDEHEDDEDBBEECBCDDEGDFGEDFGFHFFGHGIKFEHHCEE?>BDA?@BBCCCB@?@ACB?AEA>CHEJRSW_dhgghgfindfja9!"%"#!GzzmjZSOTTSSRQQPPPPQNGGNVROOQQPPNNNOPQSVSSSRQSTPPQQQQQRSONLKHEEGAB;/%""  ###"!! NtxkvyvV2463100:><:-0I?% %;5)7:'-8<>@98GPMPWSD8578IF-;wdHVYTVYVTUUUUURVVIDD>>88+0OA%(&*D2'89+%09<@;6EKPRPRVSL>33::AQ]Yc^HLYE*\~fLU\WXZVTUUTTTRVUIDB=>EKJJKJLNBVwjRY[X\`^\Y]^[Y^_]\^^_`_\[]^[ZbXHdRgD0(#  !!"""#$%'(())+:g`JOROPROOONOOORTSPSRQRTUTSOSSMViPPPJKKPLLORSTX]YTUWSPRTRRSSQPQROOJFHHFJPPSW[\[Z\WROLIIKGEFIIGFHFJLLLOSVVTQONNOPTSUWWUUXRST> (49>DD8)  *7>CLPLIJ>>FFEGGHKLJHHHGCEGGFFGICEFECCEHGFDDEEDBDCBCFFDACDEFFEDEGFFFFGFEIGEGIJIGIGEEDBAA@B?=AB@@C??B@@BCBDCAACCACKPPQX`e_`ejjeejjmnU."$ ! F[N@DOXTTROOQSRQMKOOHFLQSROORRPMOPQQSVYVTRQQQRSOPPPOMMLJLJGHJHEA7+(&!  !!! "# (#:gwsrrp{l:.974.0==:9*0M=#)*(=3.4-#,::;;:BMOQSUWTQSF>8.-5JfKUZUVZVUWVRUXVXUIDC>>FLLKKJLNCVwjRY^ZZ]]]Y[[Z\__]]_\[]^^]\c`]cWE_F|YC2$#%# !!""!#&()(&$'/ChbJOQNPQNNNRRQNNPQPSQPQSTSRQSRL\iPNQMLJMPNNOPRTXUUZ\XWWUQRTTPLOTTSNKMLHHIHLUZZZ\b]YXWSPONLJIHEEFFEFIJLNPPLIKLKOTVQPUZYTQNOVH#(49DDGPOIJH?AGFCEDHIIHFGFDDFHHFEFHFEEDDDDDBEGGFCCDCDCCEHFCFDCDEDEFEFFFFFFGHEEHIFGHEFGFFEDBCA?@CA@BB?@A@@BBABCBBCCBGNRRQW_dhddggfhmemlN+$'!!! 0?;6ARWSRRSRPPQPPLLPLGJQOOQQNOQURPPRSSRUUPOSPMPOPPQRPNLNLKKJIGD;0(%#!  !" !"&%JotqtuqyvB.77303<<<;( 6N9!)(.@4##'<=::>EMNLKILODXxjQX`]Z[\]Z\Z[]\ZZZ[\]]^_aa[[[bWIeIt~bC.$%  ! ! !!!!! !$&'(''())=cy|~{dJLRONOPPMNQSSRQPPOQRRPOQRRSOJ`fPLONMIHKJJKNPSTQWXW[[VTXXWWTQQSUURQQOKJFEHOUX[]Z[[\]^\ZWURPMKJJMHGJKIHJJGFGHIKNMORSOJKPPTaX0'498BB3$'6CLHFKLEDND@ELIFDDIJJHHIGEEEEFFEEDIEBCEGDAADGHEDDECEFEDEFEFDDFGFEECEGFEDFHEEFGFEFHHIFBADC@C@@CB>?EB@ACABDC>@BBBCBBJOSSRV_fhggfegheeqhD% "" ! #%/43CTURPQUTPNPMPLLPLEIQQPOMOPQRRQRQRRRUVSRWTNORQPQRQNLIFFIIHEB7-&# !  ! #%.YsrrwuovL,37324<:=<'";P7!*'1D4 ":A869CSQNLMPRSPSWXUTSP?;9.5V`UGMueKUXTUYVUWWRRSSWVICE>]qutu{}fJKQONPQRNNPSTRPPQOPQQONOQRRNHfeTLLMNKGEIMMKLOSWZWUZ^[XZZYWVVUSQRTSPNNMLKGEIPVW[_`ZZ^`^\[YVTRQOQLILMJGGGIJIIKKIDFJJEBGOZ_nh<&4::@=-%1ERJCFLIDHNAAHMKFFGHIIHHIHFEEEEEEDDDDDDEEEEECCCGGECEEFFECDFEEEDFGFCDDEEEFFGCFHFDEGGGGEBDGGC?@A?>?@@@?ABABDB=?ABBACDLORSRV_headgeefcfqc;#""" " +32?SVUQOQPNOSRPMQVLFMRSRONNPPKOSSQPSURSRRTTPNSQONNMKIFGHFEHE:3'#$"!"##""!  %$7erntxsmtX..7324;:<;&@?@A@=>@A?BC@>?ABABDIMORSRT^hlbelfadego[5&(&$!! $17FUUSQPQPOOPPMIOTLDHRQRTQNMPNOOQQRQQTSSSSUUQTRPNMLKJKIJHDE@4)&$$#"""!""##"!! &$?kpktxpnth8,6312<:98%!BQ2#/(CB?AB<=@@@BC@AABCCCHNQQRSQR[fgdii__d_lqU0&'#" ! ! -;LVRNQRRQQOLNMMORMGFNRTQOPPNNNONOPQRRPQQPQRQRSRQOMLLHCEFB@;/%)(" !  !"#$$$ !&$!ElpgrsnrvyG/44.0<:65%&HR. .'79##'-%'/.1@A<>A@=>AAADEADCCEDEJQSRSSPOXc]abRH[jaijL*&($$!"!"+6EOQQSRMMQQNONOTSLHIOPQQONOROOMMMOQSQPQRRPPPMPQQNLJJJHIF@A:)%(&! ! "$%#! "&%#Ilpdnpnvx}T235,.<:43%&HP*/*#7. "$% '+(1014;BIMOVURUWPO[JAXv|cKVVRTWUTVVWUTRTRE?A=>FLMMNIKL@TuiQY`]Z[]`^[\ZZ]\[_]\\_`^]]W^aaSHg%*)'#"& ''$'&%#(% ""(1BQO@77?atqqtuxkt}\INQMNQQPNOOONNOQRPPONMMLLJRFScNKQOLKPSOJHIKKKNLLNPQTW[[\]^]\ZYWTTUWXXXUTUTQQTOKIKKKNSXWXZ\[XU[XXYYWX[``acgkoqqlkopmklqa[`P% !-<85,'4JODELPNONB38ISOHDA@HJIGFFECCDEEEDEEEECCEEDECEB@BBACFFFEEDCBGFDCCDEEDCEHGEDFGFEFGHHGEDDEGFDBB??ABBBC>;=BB@?=@@BA=?DCB?=?FNUXT]X97]menj@"$#"'$ !""'7IRQORRQPRQNRMLRSIEITPMOSROONNLLNMOUSQQQQQRSQQQNLJKLFIKHB:0)$#"!!!"#"##!"%#%$(#&Mpllmoopxc(*2-//+0.!+@F' 0(#/()!" !%(,))-/4<=@IHRUSUSSZJ=YyzfNUZYXVUSRRTRNOSPFAC=:DMMMKDJNCWufOX]Y_`\X[[_^ZYYZ]]\]^][[]a^\aTIh*+#"&%$!)/(#)++-+'%&+5?EKJ@:>@?^{~}}VDMPKKMNONNOPQONMMPOONMLLKKQDRfPIMNPNNONMLLLMMLKKNPQSVSW[___\[\[ZZZZXWXZZXUTUWTQNMJIIJRUXZZWUS[bfffijjdfilnonnolkllife]TU_S+ ,<>5+)7LOBEPQLLE:4DNQKGECBFHIHGFECHFDEEFCAFFDCEECCDFDCDDBEDDCCCDEFEFFGFFDCFFHIEBCFHGGIHEDEGGGHHGFEC@>>=;:::898679844895687:88?ISVVYVN:=[f`lc:!$!$# ""(8IRQNQRPPRQOOQRQMIHKQMJMPPNPLQQONNMOSUTQQSROTRPOMLKKKGCA?8.&'!!#"!# "" !"#%$(#'Lollmonows5&0,,-,++(*00!$-&/3&")"#*..**-049=>EOTQORUG@YxybLUYXWVUTSSSSQTXTJC@<;EMLNLFJJ?VveM[^VZ][Y^]\\\\^^ZY\]]]]]]_]]bTHh+) ('#,2.&%*,+).:FNZszI0?C2:W~~~}{xusqpmmnnnprs|~}ywR@LOLLONOMNOPONMMMOONMMLKKMOBSeSMLKNOPMNPOMKLMQNKJKMQTYZZZ[\_b____^[VSSZ^[XXYXWWVRQONLKQW[[YYYU]fkorrnlkkklmoqsrmhd`YSQNQXO. *:;/#(>NKBHROJI<17KOLGFECBDHJIGFECGHIGECDEDEDDGGEFEFFDFDCCEEFFGEEDDDDDEEEEFGHHFDGJIDBDC=<>@ABA@>>>;878988936:97:=;=;:967::646?KQPLLHD9@\gejY2 %"! !")9IRQMPQPQSSPPOOSTMHJSQOQSQORQPMJNPPOURRTUQPPQOLKLKJHGCA?:1($%%#  !#! !"!#$$(%(MnllmonowG$+*)+,''.(*')+!,?+)--.-1.2:=68:6587?:DQTTVRBB\v^JUWWWVVUUTTTRSVQF@@>=GLJKJILL@WwhPY`Z]\YW\]\][YZ^^Y\^^]]]\[[^cSFf%&-CU[cmnhdWKXknrsI63Bj|xzxrrxpooonmmllkkkjjiimjghlopoz}xwTBJOLMONNLLMNNMMMNNNMMKKJJLNBUeSNMJMOQNPPOLJIIONMMMMNORSTWY\^```__]YTPMTY\[\ZYX[[YWXVRPSWYYXWW[UT[djmpqnjhhjnqfe`XTSOHFEBC>+ (76*.KODDMNJIF5/<667878::;:;@EHD@8?D8=[kkhO*!%! !!+8HQQLOPPQTTQPQQPPONNQQQSTPNPQPOONMMQOQRSSTRPMLJJJJIIFFE?7/)&&$""  """!"###$#)%)LmllmomnuY'")**,&%/)+&"*')A,&)'>DFJBAKI?:<=>>;;38FMS]dJ>Wxy_JUVVVVUVUVXWSRTOFA=;/3BIHFDDA=>HJJHGGHHFEDCEEEECDCCEDBBEEFFEDCBFFEDEDEECBB@@?>>=;978998952269;;;<<;989:9655755663588897<@@>>?@@AAB@?<;:5@I<:TeieF&#&   !,9GPPLOPPQTTQUPJKRURPNNORSPNQNNQRPMNPLORRRRRPNNNLJHIJGE>4-,)$%  "! !"#"$%###)'*Lkmlmnmmu~i-)-)*(%+-&!,#,6)"$+?BCH@BNWPC843;GBA=:9;PjYCVxaLVVVVVVUVUUURRTPHDA=;FNKIFEKOCXvgP^bY]``[ZZ[YWYXXZ]^^\[[\\YZ^cREf3>PsErM62;CFVj}xvsrpnjhihgggda_d_^bdccfeggffiihfhfehgfhlllmmmnndhkjhhmqyx}wWEIMKJKJLLMNOOMLJJMLLKJJIIILE\fQKMMMLMPQQRQPPPLNPPPQPNNNORSUVV`_```]YVSLHMQTUXUWYZ[\\ZWTPPRUVVVWURSZ`bccb_[UPLOMGCBCCA=A@AD9!-,,IKFFDH@CD4-;IKIEDD@>CJKIGGHHGHIHFCCDFCDBBEEDFDCDEBCCA>???=<:9:976679:9;;::<==9<=<;<>?;;:876777447899;<9=CDCA>HOMC@CDCBCC@<99;;@H@>Qbj_?"#$  !,7ENNMPQPQSSPPNNRTQPSRQQRSQPTNOOOMPRQONOSSPNPOPOLHEEFDE>0)*)$""! !"! !""#%##"*(,Ljmlmnlls}z;)-)'(())#!*%($$!3@@CFAEQPWWQI916CKMMI>>BEEEEDD@>A7 ()*@@@A>;<;99:8;:999:;;:;<=>===9:;9:;:868;:7347;;::<=>?A?>?AAABDABBAABAAOPDADDABBB@><;;:@H??VgnX8 !   !,7CMMNQRPPRQOORPMQUTPURPQRPOSNQTQMMPNQRQPQSPKNMLIGFEE??8,%%%"$!!$"!! !"#$#"!)),Lhmlmnlks|R'(*(#%*,%" $(#%:@?CGDHNSONRTL@92:HX`YJ?@Rirz]JVYXWVUTSSTVVVVPGCB>;DKJJGHMNCZyiPU`\\[\]b^XW[]^^Z]^^[[\]]_\[_QHk)&>ļlV²mC70'.31,)))''.02E^htmijhb_`[Z]`^Z[``^^`bcbaec``acdcbddb_`ceab`^aefbabcdddca^ca`gfdighhijkllgiklkigfr~t{z]LHMKJJILLLMNMLLLMLKKJIIHHMJA\fTKFFMOLJQWXSOOPQTTQQRQNINQOJJOUVZ_ehe_[Z[XQNLJFJGFHHEEHFHHGFEGIKJKOPPRVWUPLHGGGHFIPTPMNMH<8<8'%5JQMI>:FEEF;0<989:<;768::<:89:8:=<;;<<<<;;::::9876899:=A>9A?@C@=>DJHEDEEEDECACCCBC@BDC@BECAQQA=BDACBAAA?<96EN=0'&)'"#" !""!"#"!!#$##"!**-Lhmlmnlkr{f1&&'!,/')&"#+!*A@=BEDJNQNRWSPRP913G`yxZHVZYXWUTRRPQPPRNGEA>;DJHJJFLMAUugQZ`WY\_[ZZ[[]^[Y\]YZ_^Z\ab]Y]QJl.12e]P[;9>822001-'&'%%'()/6LYca[[]^W\ab_^_b_abb_]]^ba`__acddcba_^]\^`ba__accdec`^^_bbbbbcccaceecdhlghc_aca`l}swz_FFMLKKJMNLKJLNNMKJJJIIHHHKLD`gVMKLNJJLTUPNSUQPSSTUQQUPPQQNKNRUZ`cfgeaYXXVQKJMKGEFEA?@=AB@>?@?BDGHIKOQUXXUUZ]]b[TYdaZ\_J=;;;/ (Hb^ME>AILA>95DQJE<:?BCDDB?==>;9;9;949=9;;9<@=;;;=>;:<;898:97:;98;9;AGHGFEEGGJMJLHHHGDAACGIIGFGEAEDBBBCCCCBCEFDDEDNNC@CB>C@@B?:8;=EE>>SlmP+"!! ,4?MHMPQPNNQUOQRPMLNQSRSTQRTSQRPPQOMPPQQPPOOONMJFEGFCF=+!')$ " $#"!!$$ #&'#&+%1Riiioqmlvu?",-!(3)"+%.1%&:B=?FFAFQPRSRQPQSUP>49`jbRGFMXZQ52100.*&!"#%%$'*1CV]\]\Z\^^]\^]\\]_bdd`\\]_abba`^^^^___`b`^^__]Z_`bb``ac]]\\\\[[UWYYXZ^abca`chhgow{dJHOMLKIKKQOLLLMMLJJJIIHHHGHB`hWPMLMKMLNOQRSUVRTTVUQOSVQMKJJLOPSW\bhhea^[ZVRRSROMLKGECEBACB>=>A@CGGFIOOW^beiiggc\^ffbd\K@;9<0 'IdZEA@DKF@=:>OVJGABFC=989<<;;><9<=?==;8?<:;>=<;;;989768;@CDDIKKKOOQV[][WLHMPPNLRJIJID?>AKNOKGFGIDFGFCAABCABGIGDCFNMFCDEG@ACC=78=>FE?>TllK%!" #!-0:KMLORQOOQTRQOOOONMQOOUXURSRSRSTQOPPQQONOPOMMJGFHGDE:*$((#! """ "##""$$"&)#2Tjkikoopv||B'4;.,+1%$-%67'#(B?===<;<<;:;9;94787679754428?<??<98>FF??VniF#!#%#" #./6HOKNRSQPQRRRRPNNPQQTSQSQQUUUTTSPNOLNOMMPOMLLJGGIHEB6)&*&!!!%$"" !"#""#%!&'!4Wmkijmopu|S+0?B?/)!$.$>;% #9F<<>BCCIQRQPPRSRPUOSWOID87MdqgJHf]LWYTVXUSTTVURQRNFCC>?GIEFLHKI?Zt`OZ]XVXX\]XZ\Z\][VY\]]`]Z\ZX\bPEg$-38COXQPTK7=NH>IVYTQUYF2/61/0)%('$#%%#$%&.BY`\WXZ\\\YWXZ\\ZXXYYXWWWYZ\WWVUUTTSPRUVTUWZ[]^_]]_afgghijkkmnnnoprtsrqoorrmtzgFGLIIIJMMJJKMMMLLIIIIHHGGJGAdlWPOLLLMMONMPUTPPRTXYVRSNMMOOMLLNNNRX`dghd_ZXXZ\\^]ZYXUPQRQPOPMIJFDFEBDIFJMQV]bfdjhbbhifZSLBA>;;=<>CF?DGCDJB:KF9222141/;C;FG@?Yoe=##""%##"026EMJMQSRQQQSRPQQRQQQSQQSTRRSRRRROMNLNOPRRQNKLJHHJHE>1')*$!"" #"""####$&! &%!8\nfkmomlr~c0(:FE0($#.& C; $$2CA<=?ABCIOPQRRPPQRWNPSQSRG<7BWgdUMa]LXXTUXTSTTVUSTWSHAB;;EKJIKJLI@Zt_N[`\ZYZ^_\]\ZZ]^^[\[\__\[][^aPIl~!).4?O[ONXL-+:9CRTOLJC?>=>@?7:>=?JSRKW`VQSK>KE4(+,,243=A69EDMTWUTSTWRPPUZ\[ZNHOQIFGIHIMOKDCFNLKLKHGIHIIHGFFGKKJGFGJLRWO@=BC??@@>=;:9>EG??[p`3" $"#263@JILPRRQQPNOQRSRQPSOQUTQPPSPOONNPQNMNQRPNMJKJHHIHD;-'*)!"#"#! !"###$%%!!&$%@amekmmnkq|h5-;C=.1.%%1+$F?#*6?C==>@BBBGMOQRQPOPRNOQQNPSSJ6?EFcp`N`XGSWRTWTRTTRSSUYSHA>9;FNLIIIKH?Yp\L[`][[Y\\UX[[ZZ[\XY]\YZ]^[[_cRLotpu .12fsTJMLMLINIJPRPPTUVW[^\XYYVTUUQNMJKLNMOU[\bggc_\ZVWWX[^_][[\]YSQRQRRQPNIE@ADIKMPT\bhgekkdbZTMHMH3:iiG8;>997=JTPJIHA<>?CD@05=ACNVUPUYRKLC9>?1(/1/7:6<=6=IIOSTTUUSSURRX]^]\TMOPNMKIFLRQKEFKIJKLJHGILIGFHIIIFIJKKMNNNRMEAB@>A?<<>@;6>]p[/ #$&% #460I@,19.(41(JG&5HE?@?>@CCBGNQPOOQRQOMPPPSRRWE=[fJAY{hQ_YGTVRSWTSTTORSSTNFC@<=DIHHKILJBYp\NY^ZYYWXWXZ\\ZZ\_]WVY[^_[[[]^MImzx$00/8IVPKWG-6JUROKOUQLQW@028622,)'()'&%&'(&%%,3P[`\[_`]a``beggfefijkkjiklllmnnnsrqswyxwz|p{lIFJGGJJKIGILLLKKLHHHGGFFFGAAmtTLOLLMKNJKQQLNTQQSW]][]ZWTSRONOKKMONLMPW]cdeeca^[YYZZZ[][]^\VSSVRPPQOMMNJIIJKQXVY`bcknhfZSPMPJ74epP77?:;7?NRJCDF?;78963)63)LL) 9KFAD@=?EEBGQQPOOQQPOSSRRROPXB=a~eNSypOb[KWVQSVTRUTPRRRRLECC;:BKLHEHLLD[p_T\_ZXZZ\ZY[][YY[\[[]]ZWZ^]\\[IEi .348BI\OQ<*>PTGNLGKMKLWF2.7:415.)))(),*'%&''')?]qkchmjmmmkiknrooopruxzxyz}~{uoqpghg[bo}wu{eDFJGGJJKIJKKIGGIJHHGGFFFFFAFuuVORLLNNNOPPMKLNMNOV^`adba^ZTPRVSPMMKIJNTWYZ^dedc^ZZYWXZWYYYYYUPZTQVXTSVXSOMKKPWWV]bfoupgXQPOQJ80asV54A=@?ENNID?@<;=@ED?6:A@BMLHHA:?F9.441;>5499=EHLTXVSQQTSTUW[]ZURMLLNMKOPTUPLJHEIMOMLLMLLKIIIJJJINPMHC>:@LMA;?BA??=;=?<6;CF==^pU&!&& -61;KJIIJMOPQRRRSTSOMNOOQSQPTPMORQPRQPNLMOOMKJKJGFFC>4)&& "%# "%$! "&'$"$#"$&5Yndcmkjokm~v<*;G;'2>3)51(MJ,#7DFEE?;>EFCHSOPQQONPQSMKPRONRD9\zvSUomXnYJWUQSVTSTTSTRSTNE@?;=>DOJ=?DB>BA:8;;77;GH?=]nH & ($ &+ )11>IHIJKLLLKLORRSTTTRKKRTOOTSNPSOLNPPNMMOOMJLJJHEEA9/+'$#""! !#&! "'$!##(#&?\ijiffijlry~~s;%9I;(,<-(5,,SJ" >CDAELOOPQPONNOPPPOOQSF=YvecpoezZLYURTURSTPQRPOROF?@;?GHEHKFLG?]u\M[`^[YX][Z\\ZYXXWY\[[^\XZ]Z[aSHa~}|||~| (028D@CUS6/JWNMQTUY\YTdbA*3<:6=91)')+,-+)''()*.0Cl}xwxkifc^[XVTTSRQPPOQRRSTUVVUUWURUVQQkwuw_FHJGINLJPJJJIJJHFGGFFFFFFI?Dq_LIGLNQPPONMNNNMOPORX]_aacec_\[VWVQMLMNSPPSVXZ\___^Z[]\ZXUTVXYXY\`dgijjeilljhfdgeccinlfe]XRJHE<""Otb:2?A;FPPH@AJFKNGERSG=COPHLURLORHEMKCGO?.44/5<=<89>DGTUTTUSPQRMMU[ZVTMOOIHLNT[^XOKE??EIKKKLKHNLJJLPOLEB@A@>==CHEA@??BCA<:=<88>HF==]i>#$%%+6'#(/.9EJKLLLLKKGJLOQSRPOLMQPLMSUQRTPMNOONMMMLJIMIGGGHA6,'$&$  $ "%# ! #%#"$'$*E`hgggijiir|~}v=(>J;)/=@EDBHRSOLLNOOOOPPPOOQSHA^xxokrWJWWTTROQUTOOQUUKBAC9:FMMHDELG@^u\MZ\XWXZ]ZZXX\]\YY[[YZ]]\[]]^_OHd|z}}~~||}~{z|}#,07B<=WZ43[dZZZXVX[[XXhU/,88<;83.)%&(+++)'(+.000M}{zupkhifa\TTUSPNNOOONLKIHHPPQQRRSSSSTTUUVVVUVURTVRH`urqs]DEKHDIKHIIJIGGIIGGGFEECCBI>BtbOLLNNOOONMMMNNMOQNMPW\`__abaac]YUTSPNLRMKMQUY]VY][XY\ZZ[YXZ\ZVVXYYY\aeifddefggllkiknmilf\NCA?8%!JsmF38:9ELHDBELGOVJGOKG@AJKEIOHHKPHDGB9@I;+01-4>>;78>DFLNPRUTQRUOOV[YWXIIMKHIKO[\UNMICCIHFDCCCD@BEHLKE>>=>@B@?>BKLB=@A>A@<;><76ED@=<>CEA=EPOQQOLLNQOPPOOOQSB>_z}|rozSIWSSWVQPSSORRQQLDAA9=GLJHFFOMB[nYO]]YZ^]\VTUVVXY[[\YXYZ\]\]_a^KHj|z{{|~|%*4@:4LR.9hjcbdfjptts~w>(6889765/'%'#(,+)(+/,2.1Uzvtromfd`\WTRQNNLKHGFFJKKKKKKLLLLMMMMNNNNNMMMMQRRSSUUUVTVVRTURH[qrqs^FBJMHEJJEIJKKJHEBFDB?;8548+1kmSKKKKNQQPPOPPQOQQOKMQVYZ]abaadea^]YTRSQPPQQRV[VX[[XY\[[\\[[\\Z^_^[WX]bmifgjliegjkiilmkqiXF<>>9)DqtQ:99@HMHCEHECKMCAD>>>?ED@IMCEKQJFJD9=F;-22-0;;735=DFLSTQQTUUWRPV[\ZYLGNPJHIM\\TNQOJHNJECA>>?A@?@ABB@>?@BBB?>BKNE>::=;438FF==^g8!"#"$<@)!*+8FLLMNOONNMNNMLOQRRPOPQRQPMQSQMNPRPOONNLKJGGIJFE>3($"$#  ! "%%#" &&'8Wigbfgjkhiq}z{p9+@D4(02,++7H6!*?FGDA9=BD@>EOSQONQSQMOPPPOOQS@=]y{|~QJXPRYYRPRRRTQORPG@C;]u^R\[WZ\[YSVZ[ZXZ[Z[[[ZZ[]^]]a_JHq$/?;8EF6[U)085:875.&$&%'))(()+0/205Nc_[WSRRQOLLKJHIHIJIIJJKKLMMLLLLLKJONNMMLLKKKLMOPQQTTTTTTTTWSUVTSUSHVmrqq]EAEIIGHIIKC=??;87321/.,,+3&)ftUKMKKMONNMNNOORSSQOMNPVW[__^`ddddd^VSTOPSUVTTVYVWYXWZYXXZ\[YZ]XZ[XUTX\afihikhbehiedeedi_N@?BA>-:iu[EA=AA=7FINUURRTTTVQORY]ZVNIMMIIKNWYTSYYRPPJEDC@@B??@BCB@>??@?@?><>GLB;=@===:9=:42>0)03,*&8M9"*>EEDC;BGNQQOQSRNOPPOOOQSF?\v{PLZSSWVOOSUSRQUXPEAA;>FIGHJFIE?`v^Q\[UVYY\[]ZZ\^\[[\_^\^\\_\Y]`JIw!,<:7:5?~e0,97:9972*%#-)&%')('73591-9IPNMMNNMLNMKKJJKKMIGIMPMJIHGIKLLJMMMMMNNNLLMOPQRSVVUUUUTTWSTWTSTSHRiqonZCCIFCHIGII8+-2312/00001115+.h~uULQNLNMMLLLMNOTSRSRPNOUTVXYZ^bdfiie^YWSQPSTUUWXTTWUUVWUTVYXVW[VXZYWUUV]gkd_beeadda_``^]UHBED><12`vcK@:BKSL@EKFAC99<>@BA>=?@???@?=?JKA:=?=?>:9<;54DHD=AalA  #'#!?I/ %'4DHJLNPRSSPPNMMNNMMOPOOPQPPUVRNMNPOMLLNNLIJFDDBA8+%'### !""##!$%  "'%,Daicbifeijmry}z~zb60>:3/3/,+)8K9%.AD@:677787888\y}xyOMYRRWXRQSRQTTUUNFC>:?IJGGIELKF`rYN^]YZZX\]\[YYXYYZWZXW[YUX]V[`JJz"#*97<@4E|vmfd`]ZJ.*74466430+&+)'&')*+66541*/?OONMKKLMMLKJJKLLMIFHLMKGNLJJLMKIGGIJKLNNMMMMMNNNLMNOQSTUXRTWTRTTLRjutr_HFLIDHJHHG8-.4432100.-,++..3i||{TGNKLOQQQPQRSSSQQSSQQSRQRW[\\\]bgfb_]\]XSRRQRTVUXYWWYXXUSSTVWXVXYYWTQOV_eb]]`b^``^_aa_UOFEG@9<4 *XxiJ80@IJDCHIF@??B@:=FECJOKNURIPUKFLD8;A7,.0*)4;=65=FIPRRSUTRTTQPSZ]ZUNFFMQLHPW[Z[_\PKHECBB@=;??@CC@>??@@?@AA>EKIB>;;@@>97;955CHE>>\oO$ !"$&% "AL4 "$#$1BIJLNPRRRTTROPQQPPONNOQPNRQNNSTQOONMMMMKILGCA?=5("#!!!!#"$%# $(#'%.Hbfbehfhjjjr{|{z~O*(0*())+...))8D4'2>;60)+,-/13446:>AKTRJOPPOOOQS<;^zytwOMVPQWZVTRMPUTQQNGBB9;FMLHEHIDAcx]O\\Y\[WXX[^^ZWWXWY[ZZ\[Y[^W\_IL{ &%*74400/..01368;=>>CGstsySIQNMPMMLLMNOORPPRRQTXXSQV]a`_c`]]bgd][ZXWTQQTPTYYUVXVWVUUY[WQTTUUUSOLQUZ_`acd``_^_ba^NIBDG>:D6! OxoK4-7GIDHHBA7=IE=85;>ERSILQMEHLD@FB9?D:--.,-6>@84;CEIQUSSSPOSPPTY[XVNJFEKMHLZ\ZX\XNJKGB@AAA?=?BC@<=A>@@@@A?<>LI;:>>=?=758744;EH@9Vr]2 #%'(!'EM6&"%"$1BJKLNPQQQSTUSQPPOQRSQPPPPPRPOQPNOPPOOMLKJKHEA;83)   #%# !!#!&%/Iadafegjkhhq}}~y{zO..4,/1.-1../1;B3)14,--)+-//023374..=PVQOPPPOOQS@?`y~vNKSTRTVTTSOQPORUND?B;=FIHHILJB>aw_R]\X\]Z\\\YXYZ[[\Z[^^\\^__Z^^HM{%&'--,458>21IPHJKHGJJFBAEA<:2!!"&!'0+%*2=75784/-9INLMLKNMMMKJIIJJJKKHKNMKMLKLKKNKLKJMNMPMNONMNOPSQRUVSRUSSSSTVUQHN`rvq`KAEGFDEHIFD>25FLMPQSUVWWW[WWyj|ZHNLLNTLIOSPOQSQPPQUVQSRQTY_a``ca_accdb][][VSUPSQOUZYVXUVZZWVXZVRRSSPLOW[\aa__[]`_`daWHIHDFBFDJRVTUUPMQOQUXWUUMEEEEHGHUYWUVWPGFGD@>=<>=?=BD=648:71=EE?CTgqF!#'(,)EN6'$$'(%)5>IGMQMPVTQSWVQQSQMORQLKMOSQTUPNPPORQMKMLIIHFB<5-& "!#'$" &%%;)+1,..*./111.,*2)&'):MRTNMPJIPMA=\x|vMLVOUUPOSQLRUVWTJA?@;>FIHHHINE?`rZO`_WWZXZ]Y[YWXXY]\[]YY\ZZ\[][ILw1<-$/55892-9JNIJFFFHIIFCBBAA;+!#)0-*.0./59731.9GNJHKLHHIJJJJILJJKKIJNKLJJKKKLMLILPNLNNNNNNNOPQSUUTSTUSUVVVUQNIK^qup]E>CFFEGIJFDA;E[ba]\[Z[]_a[WWyh}_KPQOORMLONIIOVXZVOQWXQPQSWZ]__cdcffdcb`\XUUUUTTSTWUTVTUVUUXZZ[XVVWUPLU\_`aa_`_]^_]ZTMHGFEHD;AA*ArzW8003:FMG=:@JJA45BE?9:;>GKIELPLCBB>EOI525..47833>GDIPROPQOMURQTXYXXLGECEHHN][XYZSHDBCCBA=<>?A@AED?;<<=9669724;?GEJJOQNOTRSLNUTQPQMMNNKLOPMPSRPRRPNPPMLMLIJHFC<1'"! "&# !'$ %=[u|}vLN]LRSPQUUPQSSTRJA?A6' ",0+(,)'*-.--,.CRKEIHLJIIJJJIEKIFIKKLNLKKLMMKMMLMPNLLPONNNOOOOSUSRSTTTVWUTSRPIFUion_HCEFCCEIJGD@;H[_ZYYZZZZYY[VWzgnlNIILQOMNQOKMTSUYXSSUTPNORTTW\`ddcghgfbb`\YWVUUYXTUUTTRPTYYTV[[XUUUTPMQX\^```aa^_`\VOIDBDGMG>BI25j|^;.33:GMB9:FFA;44:6:9=EHFGEEJQRRRNFENH3/1,,15712?HDMSSOQSRQSOOTZZWTMKGCEFFP`]YWTI?>@AA@A?>@BB@ACB>?>=ALJ?>?=;<5378314>GF;>Tioc;#" $'"*ENC2%/-(("&;KLPROORQMPUTOMPSSQQPMLMLPPPPRQPQNNNNMLJIHHHF:*! !$!$'#!%>Zc_`cdfghjmrvx}|{~O)#%%&'"%#""'%"("$)')363/.,*)'&&!(&&+$';NSMKPRSN?A_v}oJMYVXVQORQNQQPQQIA?>:=FIHGFEKDCfv[M[\Y[]WVUZYZZXY\\XXZY[]\\WW\\KMvB?AC8.18BGDGHGMNKNQONROFBJF3,1-,05722@HDMSSOPSSRTPPW]^XUJIEBHJHSac\SNG@@CEB?????ECA@BB@?>?@?>?>=COMA>A><::96310/;FH>>UgihH"! (*#.FGB9&/0/-%);IKOQPPQQKMPQNNPRPOQQNMMLLMMPSQNPONMNMKHHGHKI:& !%(!!'A]d]^ddeghjmrv}z~}uF" !"#$"$$""&&&'$"%&&.62*#$%&'&%$$)$%-'"/FTOIPRRP@B`xrQOPPSTRSVVSQPNOPJB@>:?HKIHFAIEDdqWLZ[X\]ZZYYZXWYYXZZZ[ZYZZ\XX]^KLs*56.+9B?)(>KGFIIHHIIGHJEDIKGD>3&  (,((('((&$% )>KIIKIJKKKJJJKIJLKKLLJHKLJKOMJNOKKNPMNMMMOPPPOPPPPQRQOSWVTSRPKDSimfYF>BFFGHGECBA=GY[V][YXWWXYYRVzjpxWJNONOMMNMLLOSSXZXX[[UNLOQOORUZ^_egffjghjib`b`]WUWWUWUVWUSSVYVVWXYWSPOPU[`a^ZZ[\[UQKC?@HKJC=AJ;'VxjH7.3BRN82>?:GJ5'1;JPGBIMNEEOWQJIF==FE2*-*)15834@HDLRTQSTSQUQPU\]ZWNKGCJJFM`bZPOIAADDA@A@?AAA@>?BA>>?@>>??=CPN@<>=<88742100;EI>>SgfnV+#(#)==<5&(./("+;EIMPQQQRSQOPSRPNMNQRPPPPRSOKORRSQNMNMJHHHHIH;(""%& )E_c]^dceghjnsw||~n?"%%%&')(&#''$))%&&))$ !#$%%%$%!!('$&>SQJMOPQ??]v~~rOPVQUWWVUQMQPNOQKCA?;>GIIHGHNFCbpYR\[Z]]XXUY[YXYXWXY[][YWY\ZZ_^KJq'.:9-2B>0-=JHEJEDFJJIKPMLOPOPPO:." ".&((&&&%#(%#2GKGKFHHHGGGHHGIIGKLGIJKLJKMNMKKLKLNNLLLMOPQRPPQRPPRTQTUTRRQOJCNdkcUH?CEGGHFCDEEAIZ]ZXXWVVUUUWPWyqky|aHINNLNOOMLORSSX[ZY[[XRNPQPOOQVWY^cdeefhijjfbhe^Y[[XWVTUYXTU[VUUUVVTSPNQY^_[V^YYYPF@=ACJJE@$$NvpM6.6GUK3.=>:JF(%59NTF;CMJAEKNICFF@7@C4*.,)24766?FEJRTSTUSQROOV[\XULIEDJLHM]^UQSL@@HB>AB>>DBBA?@DEB>?@??@?>DPNA<==>;4144004?EG=;Qfgn^4" #"4BB91%"'&$+2BHLNQPOROPPPQQPRNNPPMMNNMNMMNNKJQNLMMJIHLGDC;- )'##,I`b]_ccdfhjnswy}f6&-+,+,1./.30))% ##&&##"$#"!!#%&%$##$'((;RSLLLNQ??]w~~qGL`TUTSSSSSQQPRRKCA@:;BEFHHILB@aoWO[XVZYWZX[YZZYYZXVZ\^ZWY[[[^^JKr *0?C0/A<23AKIGJOMJGHLU[][UNKJJLKKJD;.!'(*($"#" $%!*BMLMGGGGHIJLJGHKLJJJKLKJLKLMNJKNLJLMMNNMMMNOPPQQPPRUQRRPQRROMHPgqdOCDEEDDFECEEE@HY\YVWXYYXVUTOWwxmp~mQORNLMNNOQSUVRU\_`_[ZVSRQQPORTSRX]adbbcfjkhdilg``b^YZZXUTWYZWWXYZYVURNQX]_^ZVQQSMEBB>AGDABABFC+BptQ4+2@KB-.@;>L?',AC@>@@??@@>DNMB?><=8436427@@CF?;OdfjfB$!$$ 1FE>>0&+0+',AHKMQNKOSPNRUONUQPRRPPPOMJJMMKLLQNKKLLKKLHD?7+&/-!"0Na`^`abdfhjntxyX/)300..519<;/+(#&&**')$#" !"'&&(((+/=PSOMLOP?B`v~yKL\VTRQQRTUOQRSRJA@A;>EHHIIHKDDdqXP^ZVXVUYWZWXZYZ[XXZY[ZW[\[Z]\JLv!)1CI82;;16GMJIJLMJB;::9582+-.**.258@GB7*%*,*('$"$!&:HHEIIHIIIHGJLJILKHGLMIHKJHIKKNOJIKKMNNMLLMNPOOOPPQRLNPQSUSOJCH^maJ@ACDDFGFDCCB=FX[XWWVUUVWXSNWw}chvQJPPPMJKNRPLSNOV[^`_ZZWTQPQPPSRRW[]`f^\bhiijikjihd``_[XYYVUXYYXWWWWVRMOUXZ\ZUSQNF@=9?AEA?CCAGG07iuS4/4?H>,/BCED518;BGHB@@>>>@@??@@?AJHA?<9971-0;EHG>AFB>ObdkpQ%'&&!.?:9?@;<;*#2AILMPMHLMQPNQRRSSRSUTSQMOMMLILOMPMJJKMNMIJG>1%*30$ 1P`__a`bdfhjntx~}~yJ-*511/.612;3!%'#(*/,*.)""""""##(&$(,))0AOSQONPO;A_r|ynJLWQRTWVSPOMPRTQH@><9>GJHFDEJDCanYT\YX\ZXZVVWVW[[YZXYX\\YZYYX[[JNz!05?J7)9>59FKJJII:,&$$%&$%&%$$&('(" &,2;H:( &))'"%#%*?MEHGGIIFEEJIHIIIHHKIJMLIJNJKLKKJKLLKNQOMNNQQRPORRLNRTQPQRPLCJ\i_JADEDDFEDGBBCEKUYSWXUPPUXVWLSzy\crMIPTPOMLMNPRLNRW[_ab\XXWUUTPSPOPRSW\_\^_`ekjgihcchhd\[XVUVWXVVTTWWVVQNKLRYYURRSLA>@==?@33@A=@948=BIF=9;:8346?GMOECBD?9G^gam`- "#$%(.6<3@=<@/!,BGKLNONJMOPPPQRROOPRRQNLKMOONOOOLMMMLKJJKJF:)+4:9'$#2Va\\]achhinpsx~~qA&+*&&+.-.2/#$))&*01/14.-+(&'((&'('(,01LROKKNRL>Ebt|~oJNZTSSRQQPPOPPRRJ@=:9?GIHIJFHAEgnUT_ZVVYZZ\ZYUVZVTZ[WX\[[]]\UYYFN"+:F@0/=?08KLJH@) !"!**)'&%&&$(('($)8D@.%&$  # $7JFEHJIHJJIIHHIJJJJLJKLKIJLJKKLKKLMOOPPMMNMMONNQRPPSSRQRRPMMHKWfaJ=DC@AEFCCD@=@IVXQWXVTSUVU\PSx}^\zvSJNQUTRMJJMPSSTWZ_cf`][ZVVVTTSQOOQSUX[YZbgedbcdehhc]_^[YYWUUUVUUXVSRVLHOVVTTMLJHECABCCA?>ACCAE8)_{`<:@=:978688867=;::;;;BDAINC7GVJ;6:EC3),,&340*0@IIKSSRRSUONRVXZ^^[VNE@@GMNXYPIKH@>BCB?>=<=>??C>??FD;8:979?DKLMNF@EG=7J`dbke9!###'08>?9>AD@@A@><=A>BDAAA@=@A?9648?MOONJJNIBBE?:I_fdhlK"# ")/=936<835>FLLKKLLIJLOOMMOPOONONMLNONLMNONNKKMMKKMMH=.!'7BC:'(+!5<% 6Yc^]_baefgkmpul6 #%'&&"#.588610674101110486596;KLNLMNNRP;A_s{vnLP\TTSSRRRQQQPSTL@;97?KOLIFFIBDemSP]ZZ[\ZXW]^YTX[ZZZ[\ZXY[ZY\a[GR.2+*.4<7;AIMD.$%'(&"!#&"" #0;B7*!!'$#" 'BGFDDGIIIIKKJJJKJIHKLJIJJHJKLLLLMNKKLKJMPPPNORQRSQSRSTSOORKGEPgdI>BDDBEEDDDCBBHSZWXUVZZWVYYTWwgTkxYIMOMOQQPOPQQQRTW\`bbaba[YYXSTRONNONKMRUTY\Yabdfecccc`b`WVXVXXVTVVUWQKIOSSSTKIA>>@EMIFCDEC@=?HD*N{f4#368;7326;ADOD4:6??4*((%'+.19AECMUSQSRTTPTTZfcUNVQDAGIFFGHFFGC??@=?A?>?=>=?BA=>BBB>99866:;95325777@KSMNKNNMQP>?]wx{qKOZUUTSRQQPMPQSRJBA<:?GGFGHLLCGgnTT\XTVWWTSTXYYYWVX^ZWXZ\ZW\Z^^IQ~%%'.+,;59DNJ3 &*,5FI;.(&$!$-BIOD6.%# ! $>FECBEHHHHIIIIJLLJIKLJIKLKIJLLLLNOLJLMLKMNMLNOPQRORQRTROORIHENffK<@EECDEDDFB@CLY\UWTTXYXY]WTVsiVivZINONNNPRRPNQRUW\_bdabeea^][XVTTRPOOKJOSSSVVY]`aabcdcb`^ZWVTRTTSTSRTSMJLPQPOGDB@@EJIHGDBABBBAGE.Dvm<7C?8:94,6;6135/,,*+*+/5:>C@<=?@ABCBE>;>@=:9NK>>HOE=BFJEE:3AH<9@<60-,*(,)(*1:BFJRPSN?DZZVUY]aefUJ@CGC>?AJMGDDA:==>>?@;30003579;?@AC?:9<>BFJLJFEAFHKGFIC?BFA>LQLMICGHEIMF?HHA9::7@A@91;E@@@:4111005999BPYY\abdaZ[]LLS]cca_PE@DFDB@DLOGACB;?BDD>53841021-.1.12248:9:LVQJHIJGHGHEFIBA@DD?J_ichgmZ-!! "($ "&-6=ELLLMONNPQQPOOOLNPPONMMPQQOOOPOQOMMNLJHB?.(7@HI;(-GD%3O_J%"%:Zb\]`eeiiimnou~T*(/+23(&+$'(!%(3<7+$"&,-+.5;?HKHFCJJBEflSS]XUWZZXVYXWWXZ[ZV\ZUX]\YW\`UBT"&+,11.9MTI@:7-,3/&)./1112573/00-0424467640.-.)/F: " ,CKFCEGGEEEGGFGKKHIKLMLJIILMMLJIIJKLNMKMOMNJKPONOOMQRPOPPOMIFHZfWD>GJFDEEEE@=>HX^ZUZ\YXXTNYYUh~|||aZjbLJQJJKMNOPPOPQSUY\]_^bgikjf`bdbbb`]\VUTQRTSPOQUVW_ic`dbXW[XVWVSRQPSOIGNTSJCDFGMSOFDMHFHGCDH>FXS+)_rTCDCIG<;@@,073345=9.,>B7=E:0+9GCDCBBFHIKNNURKQbjebimmlodF>BFTef]\TGAAABD@FMOF?AC>?BDDA813:<>=;::;<=<941/.4;=:>DC@HFBEIMPF>BGA98?HHFEFDJCDeoTO]ZXWWWZ]YVX\ZVVXYZYY[YX\YZ_ZFP~"#!!""%&+,14-.$  !""! """!!!#$!).(&12# ,>IDEGEGIHBHIHJKKLIHJLKIIKHIKMLJKNLKMNJJJHIKNMLKMNQNMQRNLMKKIEUgX@>CDBCCBBFB?@HV][WXWTUXWRQTYgvx|gYfbMKVPMNOKLOLNJJQUWZ^^_abcfikfecbaabb]ZWVVVSPQRTW[^`_acfe`[ZZWWVRRRQMNHCHTUKEGGNWVLFGJHLMIGE@BK]\3YsN0;C6.5:=C=4>A/0HM819NM9@F:/0:A?9CBGJC<::AFKKXkplhdhqmYG@A?CGN[_VL?=BA?CFFMKFJMGA@A>:9=??<99<>==?FCBCA==@<:6322343:7?IGECAGJA=SfabecllD!'* $#+/!,;>GJ@.!";K=% ;R\J)#!5T_]``^`hljkpspK&-+(,*(.*&*% *3:?8,  $%(/8HUPJOIONKGKSNCFFFGD@KCEejSS]YXZYXYXWWXZ[ZXWZZZYY[[Z]^]RAP{" %"$)+*! ,FJFFGDEJKHHJLJHIKHJKJIJJJIJLLJILQIJKKKLLNHKNPONNPNNOPPONOIIIESfZD/PS73CFHGMHIMGEHPOPLNI75D>22>B>>DB?CFENTVYTHB<::?EHC<@CFFCA@?@DIJE@@DCDEFEB?=68675387VjgbjgeldF..! !" +=DIMPNLONOPPQRRQTSSSSSRQRQLNSRNPRPMNMLKG<+.Ebs~z~jGLVSPSTPNONONNQSLA=:CECCCBCDA==ET[XWUVXVSTWWQP_mjoo[^\NMTPNNNLMOMJKLMORTT^]][]`eihfeghhfchie^\]\Y\YWVW[_cfhgcbcb][ZWTSSSQKHJRXQIIKNZ`UIFFJJLLHEDAIRaa=JeJ=JRHDIHGGGDFC>ADNGA@B?86@?30=A=?BH>/18=B:9/18=B:@YgYGEKMFCGHCCFAA>@CAAGL^bb_YK>;BE>>The\ceciqoe]I+#(&!$5>DINPKKRQPOOPRRQPQRQPOPQNORRQOOOOLKLLKMH8&$1?FGC4$*?G7"(BUZF% 7V^[]_bdimkmruswxA!)+',)(,)$&!'.2*" (.=D>1"!'0?WcZOLJNKINF:Cbu{~lJPZNOSSOPRQSQPRQF<;><@EGHGCGL@AdiQP\WWYWWXWUUXZZWVW[VUWZXXZX[^VEV2SA!$%',08<=;547=?>855-"!&)&,AFCCGGIJJFGHGGIJGIKKLKKKLIHIJKKJIJMMMMLJLMMMMLMNOOPPPNOOOLJIFN`\F=ABABB@@A@@AIVZTVWUTUXWSVTT_helr[WULKPPOONMMNNSPMMOQRRSVZ^afilkjigedddcffdccb`a^YVUVZ^adedbc_ZTXYXWXTNIGJQSMGHMR[YJEHIHMQOHDEGNWceE 8VM9BKD=DFE<@?;22<;::;=5-39B@609CC?EM;0@E98MG9BGCLJIRUMFHHFEGEEHKHEC@@CITbkikeWH<=Rechihebdo{sE"!"=J=+(6@EIMOLLRROMPPOORPRRQPOPRORVVQSRNQOMOLLNI6#!*5BIG?-0AC1 .GWYC#7U\Y\_ebgjkmrrpyq;$/+'+(&,'##" ##11)&)#"-:IL4  )1BZjdOFLILTH6Bbv{gGOXKPTQOQSRMMNPNG@A<9#&)+05;>>?DHFD>3($)/+/.09954**+-,)3GFDEFHHIHGHFEHJIGFGHHHGHHGJKJHILOIILNLLNLLMNNNMMMPNNOPPPPNIGFL^^F:BEB@?@CDB?=EU[VUVUSUXWSTUU^gek{v_SQKLNPPONNNNOKLORNHEGNPTW[^bdiiiihhhhffgigdce`][ZXW[__aefea]ZRRRRRQOLJDHSSHDIOW]THGMLLOOKFDHNT[chQ%-CD;C@6=?429?EE<JMDALRMF=58FKA=D<286/1;71H^`^N>HOKFEFELNHDH>7>GEBAHQZ^d\NDFKLOC83=KOF;5;FNNGA>LLKLSZXQ=AL[gfZNLOIMURPLDCC>>Sfddie_dinv~tM2;:'$7Y`F/,6BHIINPOOPNOQQNOQTRPPRSRQNRRPQOMNNLKKJHKH8%&0:DIG<(4B?+2KYW@"&?Z^[\^efghhlpsswk5#.*&+'&*&!$! *:2''-$"*?UN3!"&+Bay^FIMOSL9Car}~hGMSPUVQOONMMPQPME?=>:>DHGFCBJAEegPQZVVXWWWWUZ[XY[ZTZUUZ]YXZYY]SA[1K9$.120//149=BEGCDEFEB=:)($ !+78=>?A??CFEEECCDFIHEFKJHJIGHLKHHLIKLKJKMMJILMJLOMMMMNNOOPPNMOPQQQNGDEJ]bJ=CECCBBC>???GUXPTSTWUSTXTSQ[hhfo}{eRNMOOPQOMONMPPMMPPKKOOOPQTZafhgfhihd`gccffccffcbb_[\`bcfheb^]YRNQOKJNGFLUQHEGNY[QIJKJNNIFFGJNY^aj[06FG83?OM<:KLPUTTVOLMPTVUU[TIB;6?E<57<;0.11./*8MVO81CPMHIJHGME@GC77EHB==<50<84ALT]dbYOHYfc]XPXcJFD?>Sdcfke_dhjruP7;;6>>G_^A25?DIHGMQPOPQRQPQRSRQOOPPPPSRRUSOMQNOKLLKML=,%+5?FGB7%#8C:%6NZU>!(@Y\[]^eghhkorttvk5$,*&*'$)% ! "5;/%*2, *DYN5 ,Aiq^KJLOM9Cbs|}lKMPRTSQRQNMMPPNKGA?97=HJHFDDKADcfPU]YXZXXXWX]]XUY[ZUYZYWYZ[UW^UB];P4(0;CGGFFFFFEEEEECDDEA8+! !  !->FCEHE@EEDDDBCEJHGHIIHJHJKJJJKJHIIJLNMJKKLLJJKLNMLKLMOPQRQNNOPNNGEFHZcL?CDCEECBCCA?FV[TTSTVURTXUQP^lf_eu{fNIJMLPROMPOMPNKKORQONNOQQSUY[``bfijgdeaadgfefdcdeb^_c`beffc]XUPOQPJGHEIRTLGGHTYSGEFHLKNKFEFKT]__ke85PXLMRUVSRNOJHINQHDDGDDA?NXLGC7146=:<<=FQTIE91154,:FLGGLJCLRNCFJ@13<=9<<50'%!%-.+,3IO=147?EOR>;DEW[RML=((67<99DJJNOJIGC?=BKIC=?DA97ANOHF@2)5859??>;- $!!"+/+,6DKBINJEFKO@:9?FHJLEA?CJMIDHMWdebbWJDB?BViid`fjdgoosrW3)/7>47VbK=?DFIJKLJMVPMMQRQQSTQONPRQPRVTOOQONLPJHLJIMH;23;EH@5- #/;B3#9Q[S;!1GZ\^`^dgeeimprrwza.")*&*&$($3B@.&),/.%#!/LT:% !;MjrUEMG;Fdsz{gJPVTQLOVSMOMMNRRH?>>;>EDBEGHLAFhhNO[VVWVUVUTWXTRTVUTXZYZ\[XYX]R=[  F8/ADEKE?>ABA>@ABCCBBB<4-#!#" ,CK@AJHCGHFEGGFCIKHHJJGIHIKKJIJIJLLLLLLMKIJLMMKKNMMONLNMLNLLNPWUKDDFWbN=>DDCC@BBA@?FRUMTWSNQTUXTRTfn_ZbjoiUHJONOOMNQMKPQMNRQLKNMNNMQX]]accabffb\_a__beeda^`a]Z\`bdb`a_YSPMKLMLKEHRRHHPTUPJDCDGIMNNLJLU_`^`muM#/@BJ\R:>KSL=?J=0=D><8@F;5HTKEB+#3/0687DMEED><:7=CEQL>?D@8819EGFD<=?A@>;99;<4*$$$ !&*089579;@=BHKJEA>GLOIAAKVhndTUWNGBBD?=SihcfigcdltusM$#059;BOYMAFIIKMNMLLLSSQOQRPNOQQQRSQMRXUOQSPOSQLKNMJKIF?@ADI?4+  '1?A/&DU_U:";U\Y\_bchifdjttnv~[*%.(()%(*  4@?0).1.,/%%9OR4$ !!(6XtgONK8Cdt{xcCMURPOPQPNKRQNOPC;A;6;DGIHBAGAHdePT^VUZXTTWYWYXTWZUSUUUY\[[U[aPCf*1%*BKGAFC@ACDCA@BDCA=;:.'!" !#"'9FFGIDBFHECEFFFHJJIHGGHJIFEIKKLJHHJKJILKJJKKKKKJLMKLMKRONKLMMSVJCEDR_M<>EDBB@C?<<>FQUOTURSVUQRTQRgp^TZekgUJKOOLOMMPPOQQRPLKOPOMPQPQUZ[b_`fgb_a[\[YY\_`aca^^_^_a`ab`_[UPOOOONLJLLRRKLTVUOIEEFJMOLKKMQX_b`bozX""0?BFJE@B@99EEKXM57@A@HH@>ED969:=GMC5??<=<<<2;=<91%&" !"$'&$'05:>@@ABKVUJMajdh_PFHHECDCE?=SgecfifcdlsrI''--33?BFJJJHIKMNMLLMPPOPSSQQTSRSSQOMTSTTQMOQPNLLLMMJKIDFEFG;-%%-7>>- ,DS\Q7&@X]YZ\aecilggnsst|V% *(()%(*  3@<0)-1/.0-  ):PF."*CfkWNL=EbuxcEPVQOOPQQOMRSNMMD=A<8?FEFFBBE@JgeOSZXWUSWZXWWUWZWTVZURVYVV\Y[_O@b",?EBHHBBAABBA@CA?@A<0''!  #,8CHFBCGIFDEGHFIJHHIIFFGIJJIIIIJJIGGHJIIJKJJJKMKLMKLNMMLMMPPMRYJDGCM\MEC3,48;EMC3413@CBE?78@HI@EEE>>Tfcdfhfcdkr|wO)/>;5679?AFNLIKMMLLLMMORSTPQTROPTURRTUSQQQQOMMNNKINNFKLJLJGE8)!")4;?;,$5ERVJ3,I[\X[]bedijghnsst|R&())$()"2<:0),11003''CP@$#/MfcUH8De|ueFOVPONNPQPPNQPOOF==;8?FFEFBED=IhdNTZXWTTXZVXYVV\YTW[YWWYWWYYY^PA^%6CAAHBAAA?>==>?AA;2*%$(/5@FDCCFGECDFFDHIGGJJGIHHLLJHIJJJJKJIHIJKKKJIIKMKJLLKNMMMLOOMS[MFHCL[O=?DDAB@C@==@GRXUSWY]_ZUVXQOl~hTW`ebVLKLJMQOMOQOPLMQTTQNLLMMLMQW[\`cehlkhgd^XVWUSYYZ_a]\aa`bda_ZUSQNKJLPSILSXWWXVMIECFJMNMHFMV]`agkos}m7(GVUZRQ[VNLPVLEH@7=BCOQ?9EMDFQME;$=:&.;73AALL<7D?DCFHD@?D64238A@6.'$#!#""""  #%%+7?@ADBEIFCLPEBED=@VfcdfgecekqupR;AFJNKJKMMLLLMOONQUURRTQQUTQORSRPQSMJPNNOONPOJKLKMJGF9*! %.7<@9*)@ADF;A@?><<=>><7-$ "6BBEGEEEEEDDEFFFEFHIHIJKKIFHJJGJJJKLLKJLLLLKJIHLNLKMMLOKNNLONKS\OGFBKZO@?CBBC@A??>=COUPS^eeda_b^TPokSU^b_UMKKJOONORONROMOSTPOQOMJHJNSW[]afjnnlhfa\ZXUPRUVW[\[[^ad`\YWQOOMLKLNPLMRWZZXTJHFEHMOMMKLS\behiourwn?!=H?@COWG;@IKD7=KB0=22;?6=B0,5/5E>746;<9>51/+)("  ! "  !"#!!##!+&+105>?=DD:6=EGGHCIG@@?>>===<;5.'##!",AJFDDDGGFDDEFGFGGFGHHHHKIGFGIHGHIJJHIJLJJJKLLLKJIJJIJLJJNOMPNJR]REDBJUOB?AABD@@@A@>BOUPU`b]Y\bjj_[|qST`b^TLJJIRQOPRNLPOOPQQRRSTOJHIJMPU\achnmgffd`\ZUPSRPRVXX[[_b^WWUPLLKKKKKKKLOUYWPJKIHJNPOLNOSZ`dhlkqwponH%19?AE?7=ND0:GGPOKERG4J^]MKSb\OD4*0+$,68:7=IHIJ><@A>@HD5.169779;=7=F;4''6CQQG3!,CUYY_a`defghhjnqsuyJ '&!+)!"(2:>4,-11/.-5<9)!0IE+%2Tl_=C`t~}]CPTRRQOMMNOOKMSOD=>:9AGDDFCEF?HgdNRXVWXTSVYUUWYVTTWXVUZ[VU[XX]PA_=EBAA=><==<==81+!#$# !!!#!(@DEBABDFEDCCCDEFFGGEGIIGIFFHHGHJKIGGIJJIGHIJJKMNJIKLJKLJOQNJNLHR`UDCFHQPB@BABC@A@@??DQXVTZYSQS\gmddwSS]^ZSMLNNNPOORQPSNPQPQSTRTPNLJHJMPY^^cmmfkmkfa\WRTQOQUSV\]^_[URQMLJHHHIIHGKPTUQJFMHHOSPLKLOU[_bfknpulkpR'8HNUQ99UeZBJ_]YYXY\ZVVVUPTOMRTK9//'$-//677HLJJFMJE?;;:;FB90$!0?FQOA-3NZZY^_\cfggghknqswwJ%$ ,* (4=>2*.20/1028;3# $;G8#!)>\fBAax^BNSKOSUSOLKMKNSOE><:9AGDCFDEE?KhbLSWVWWTUWVVWWXXUTXWZZXYYYYVY^NAc =A?>;:=8=<<<:1#!! !#!  (>NE@DHGECFEDDDDFHGDDGIIIJCGJJIIJKIJJJIHHHHIJIHHJMIKKKMLJKLNKIPOJSdXEEIHOQA@CBAB@B<;;>DOXXQUWWVSV`f_cwOO``[RLKLNOSQNPQOOOOPQRSTUQPPPLIJNRTW\cimnjmnkgdb`QSSRSUVZZXWVRONLKHFFHHGEHNTTQMLONFFRVNHIQTZadfjnporjjtY/$NcVMWSPWSMPWSJHIF@ICBLOG<=0 +$"3.!9MJD>IOKMNHIRVWSTQGGIA:53;29M7+5.+=E?A928@>>9, ,("'/*+,+))*,&$""" !"!!$&6IL>?TdggfdcceiktrT87;9?NHD=4,"#6EIPL>*!9Ua\W\]\bechllhhpxyxM$# ,*!(4=>0)/4103334660$#-FFFHDGE>Lh`KV[UTXYWTPVXWUUVVVW[YTTY[\RX_PCh(?A=9::9<;84-#!!!" "! !#$" '#"!"5FFDEFGEEFEDGGCFJFHHHHHIIIHHHIJJIGHHIKJGGHLLIIKKJMMJIKMMKKKLLJNMKSeYFFKILL=ADDBAABA<CUL>=ELE9CJA7;E?67HN@2@RM@C3 '%0'+GME>6EFKSULIRPI>55995011-/@JF@ELEFH><561/7>@A8% *&%) +, $/2.*//++(&& !&%"!!6EH>BYijceecbelqxsM*/<>@L>9@HKLQRMJLONLJNOMOUUONRSRRQQRSSQPNKKNNNLKKMNMLLOOLJJJJE>99>CB;/$ !&:JHPR>%$1@O[YZ^a``adehkoquxuL !(# #'2=81-/341/12873;8#$%,=>, $.AXTD^~aCMSMQQMMQQLQONQPG>><=ADDEEA?C>Lg_MUVUY\ZWWVQTVVWXVSVUVZZVUV[XZLBf*=<;=>76?95,# #"$"!#&'$!$% *;DHGGECBCDEFCBEGECCHGFEEEEFIHFFGHIHJHFHJJIIHJKHFJLHJNKFHOPKJLLKNMIPi\ECJIKJ?@BBABDF@;;=BOZYUVX]^XZei]bXM[aWMLLMLORPPRRPRSTRPPRSSWTRQPMLNHKOSX]cgmoponnljg`XSQQU[][VSPNLJIIFFHHFGHMQOGEHLMPONOLKQSYagijmqrpmlkxm=1>34??BA7:G@/8ECHTMGQJ@L[]XPVd^HF>&"(1+,026BE:3DONLJ@2>@<2/598;ACAACHLDGRF8>>5BXhgfggdacinusT20;?BBAAFMIFMTOKLMMMNIOQOPSROPOOQTUTRQRSQLKNPNMLLMNMMMOOMKLKJLC?CC?:6-!$+:IJPS@)(6HW\ZY[]]_acfijlptwvP "'#"%'0:9/),1222*286497.  ''1;7#"!)>NTKa~cAKVPRQOMNMLJLORNB:<89?EEEDADI@JgcNPZVVWUTUTTWXXXWWVWVVYZWWYVY]MBg.@<9=?96;)&!  #%""""!"$%$#"+>JJHEFECCDDBBFDCEDBEEEGGGFFEJHFEEEFFJIHHGFGHIHIIFGJIKIIKLKLNJLNLNMHNe]GCLKKFBAA@@ABCA=;CZ_ZZ[\YRS[ZQTWOQWO@A4'(*462+-:CB2;@FMH:14=A?BJKE>JMIF?:=5>QNFX_P?IK?0*097@B<+%*"$# *'$# "$ "#!'+*.429HJ?AWfffggdbcintqW83IfcORWSSVVVWUXWWVUUVXZXWXXVVXTX]NFn1D?87;;5.  "" "$&!"""""#$"#0BIHGFBDDDCEDCBDDEIKGBHHFEDDEFHHIIJJJJJKKKHGGHGLKGIKJHIJJJIKLNLMOMNNKOgdLBHIHD@@???@??C?;:APXUVXWRPValh\c^Ocj]QNJJKNOOQRSSUPQPOQRQSTSSSQONOOOOOQU[_ccgnponpomicZUX_^][YWVTSRPOQQLKRXWTNGIOQVTQOOPRVZdmqstrommkkjsoN3QYTV^bXVYRUWSRMFHEBCACNK?EJ=;FIA@:%&)+540.027??=59IJDFKJFDHKC65@A;>FHGMU`^[eaKIEA>80**29AWffbdeecfkpvoT85?B@JF53EMMPNOPQOLLNLKMMLPRNNPPONMPRRPPPOOPNOMKLMNLIOOMMMONLIKLKIG=0'!$/8CJJKRL?2-5:513/563,#&"!*>A*#" +8GR`q{s]COTPPPONNNMOMMPPF<;96;DEDDFEE>>>@@A@A>::CRZYY]]UT^jnn^``MamaQMJKMMMOPQRTTQPRSSRSQSUUSQPPOONMLMPUY_^bilkknmnplcZ[`_ab^YVVWWUTUUOOUYWQLKLS\YRPSSSV[djoprttqlnnomnkQ#*L\QFIMEOMB@DIMA@ANK+'GPIMONONLILONPSQMOSRPPPQQQPOOPQPLLOPNMNNONMLLMNNPQOKLOOJIJA0(',4?JNJNRNDCIRXXY[[]_ce`gkgflrtz|l)%&&,&"/A>3/6:537065&'&!#2C5&!#!'4=M`p}u[BOSORRMMOOKKMOQMB;=;8=EFCEFIFA@??>?AB@=:=FPVWUZac`cecaVZ|eMbuhOIKMNPOQSRTVUSORXVSSSTWWTQRQPMMMMMPTWZ[^befhilorqi`]`cghd\WWZ[YUWYVTW^YNKNQU_[RQVXY^dlmnnopqpnqqsoliW+1A;2?G;CF@DH?7:LHOMBLSDENVWZ_a_RDFA*#*11.*&+25*-@F75@AKE?;8;EOWZbd][\VC<>C>HSF<>DOO<;JLMMMNNOMJJMONOPNMOQPQRQONNOONONLMOONPRRONMNMNMMNPPOPNLJIJC8-05LULQQLLRRMNNNOMC=>:9@HGEDCHG?@A>;=@BABCCHJHHID>@RuiP[tjPHHIKOMOQPQTROQTTTUURSWYXUSQONNOONORTVXYZ]befikpqmedfmoojc][\YXVVYYWYUSOJKQ\g_USWY`jnmnqttpkinqpplhh^5,@=5ANM:29R`F9QZZTVa_WYWXWUSQMIPJHF4!#+0,/.%%18%0MF56@>CILKKPVVUQKFECA<:9>>I\XMW`S:;JNFLNIK]Q*#2+()"/:'(,$5DG=@WffcfhgedhksrP7ACAB@>>??<:=>9667688587.4@WufQ[rhQJHLUPNPRPRTROPPRURPSXYXVRQRSMMNMKJJKSUVUX`dddgkonjjmvvtpid`^X\\Z\]]`ZTPLMYegaYUVYfruxsopommolokkfcgb<0EHKQTZ[NQW[]SMSTMHID>A?IOG?AB=>HHLB)!%#$..,1)#(>OG::3:CIKIB;HJ>3:CB>IUUX[WWPHITVHDMSLJH@IRTQQPMKMRXZVY]_\Z\^__afknoqs}~}?($&/+#!$'$"!&7C4" &($"")67' &*.0RnxS>MQOOOOONNOKLLNK@:<8:?BBDEBEB:Mk_JSXUUVTTVXVWUSTVVTYXVUUVWXVWXICl$%000.&%*/21230+!!# $%"!"$,6?HMJCHGFEEDDDAABCCCCCCCBBBDFGGFEEEEFIFFFFFGGFHHHIIGGFFGHGHJJJIIJKMNLKKKMIHKLNdmX@FQNC:?@?AAAA><:7<<;:86549BRm~xiXWloYLKHKRPNORROLOQONQSTVSUXXURQQOPOLKMLHNPOMPX^^a`dkomlmuropqng`\XVXZ[^b_WRSW`hjeZT[fnppsojiousnmngadefjG8_^WZMEMJLMH??BNIB?<55>ECLI>AC>1;PME@,!-.#$*/15+5LI@A5+4CHD7-:<=>FQY[a]ZVJ;7@UficfgebchmxnN;DG@BKC16JNJJJNPOOONLLMNQRSRQSPPOLLMLMLNRPKLQQQPNLKLMOQNJLMNQOMNMKJJIECGMMPRNKNQRRSVX\]\\^^]_fceknmnrz}}J""*0'$252.*%7VI !"'# %).-%#+/+FyQ=MUJMPQOMMNMKMNJA=>87@FDCFDII=Lh]KP^WTTRSVVUWWUUVVTTXXVVVWYUW\L@f+#!../-"'/430.05:<83*!!!! "&)+5A@>?@@A@=<<<;:;;=@?=;<=DGMaqqgYYou[GILPPOOPPRRSPMNPQPRUUVWXYXUROPNMLLKIJLLKMSUUUVZadglrqnkklmlj_][ZXX\bd_XRTakl`[Y]ekllmmllnqplljc_a`ahM 7YI6@=4CA>SX>7EMM?7BH<2HB/0GVN;6F[WHF:',+"%+/9@07QF39B.+4=DA?CDLY`_XUEA?A?;@UfhfggeccgjtmM9GO=/::/6INLMNPPOPQOLLLMORTTTMNOPQPOONPQRPNNPMMMMMLLKOQNJKMNQQNLKIKNOLHIQTPNRQOMLOTX[[]]]__^_bdinppop|R%#  $1AG<970.,"+T[3"''&#&..,.4/?myGLg]LRVSUXWVUSUVWXXWWWUTYZVVYXRTXJ@g'1!$,02. !/242248;@DC@;5.)%#$'()/:BDEIKIGGHGJHEDEECACCCCBBAACBBBCCDDDBCEFCCEGFGGGGGHFGGFFHIHKJJJIGGHLIHKKIHJKMPLLNIF`m]CDRQC>AA?@?>>A=;;=>?A?ACEEDDDHMS`jiaTQes`JHLPQRSQPOSVSQQNLQVSTUUVWXUPQOOONLKLKMNMNOPNPQUXY[biiknprqpoib\YYZ\][\\Z\dhefc_\_ejkjorqoppnnibacacmV%0TI2684BBLER]HPRFGRZZJBJVSDCD/! )+('+37=,6QA*3@@6/:>=NOQROOLD944:CHDBCBCCDHHJNOLMJIRI,#+.!$)&++/>+2;#5EI>@VghhgfdddfhtoTBHF72=?69JMKIQQPOPRQNLLLLOSSPPRQPRPMOJKNOOOPRQPOOONMKMPPNNOOPMLNONNNLPPNSYSNTPNKMRWYZZ]]^a`_`acefhmrv{~Z '&%,,,.6B@:621=A5'FW@ &0* !072++2B`vwyFAPTMOQQPONNLKNQMC==;7=EFFGBCF>Mh[MVXTUWVTTSVVWYXUTUUXWVXUSYSVZK@d.1&,/2+ (/2005=@??CCCCA?<977:<>BHKLHJJIFGGHHFCDEGECBBCCCCBAEDCBBCDEDEEDEFFDFGHGFHHHFHGEEHHGJIGFHLIDIJIGHKLLHFILMNJJ]l^B@PSD=AA@@?==@<9<@A@?>;9:==848HUadb_VQ\mgOFGKQSTTQQSUROOPNNPPORSSTVWUSQQSQNNQNOONNONNNPRSRRUYdgjllmnpof\YZ\\ZYWY^bgjhffa]aipqkpsqooonle_bfber_, +QU?765=II;-A_\>31:BAGE<:AD@B?>AA>;<>CEHMKIOK>85=LH4-43,(%#)/+$%%.=-+8%6EH>@WgghfdddfghwrW?=83A@2"$5<808EA* !,.)(3:2)%,4BV[`xvDDPTOPQQPOONNLMOKB>?86@@=>??@@;8;?>;86201441-:O]a_^a\SXhcOFJNMORTUUUUPQPQRQPRPRSRSVYZVUVWUQRVQONMMMNPLLMMNOQQTY`cddgilh`ZXY[\^XX\bjoohigehnpnkmljjkkha\Y^dadqe4"HZOGHIHQZKCNRQFNMGF>9?@?@?OQ=BD@7DJEI;!&.)$(,0<7.>M:).*(?937;@ACB?;;<43566457BIGCKMF?91-(6PJ3,3113(")*,+(#!)/&!.&7EH=AWgfedccehjkxrN.0744DA67FOQMJNPNNPSTOLJIMQQONNQSQQPMQONOMIIKOOPOONOOOONLKNQQOMMMLMONNORRRVUNNPQSTVZ]Z]^_bbabcdhlkilrv|i+%$":73.+)./2AJRcwc;EPRPPPPPONNNLMOKB=>76>ECBEBCC:Mi[LWZUTVVVYYWVTTVYXVSSVWVWYWXYZL?Z-1)(10+ "/0/6=>@DEDBC@ACDEEEDGDDHHFFJIFEIJIEDFGGFCBCDCBBBBCCCABCDDDCCGCBEFEDFFFGHGEFIHHGGFFFGIHGHIJIHHHIJJKMOLMNIIMJHXjeKBQWG?@?@@<9;=<:977643468F\fd\Z\UMUbZHHLNLNPTUVTRQURNOPNNTTSSSUWYXYZZWUVYWTRSRQRUNNLJKOPOPV^ehikljf_YWYZZ[YXW[flionkijkkilkighkhd\[[`gefof= ?ddPMZN=GFBJC<;BOL:;DB78GBIP=>IJ@DHJRK)!/1/-013-=L7&-'%$)6<7>@@@A?:426421/147@E:-6IE3499'2SI+/3131%"'&&)*())$%$"",- 7EG=AYgfccccdgkmwnJ/4833?=49FNPMFLPOMOSURQLHJPQOQPRROOOLNONMLMMLKMOPONMNLLLJIKONJJMOOPPOOKOTSTUQNRVXWXY[\^]^`a`b`_agiinvwp4!>?;9;NUE8BNM?<:8650146762//3.$/GI72MfYJTXTVXWUURRSRRSTVWYUWWTVYVUTUKARu*5/-2%)53493ABDFGGGGFHGDDGHFIGHIGDFKEEDDDDEEEDCBAAAAABDEFFFECBBEGFCBEEFHFDEIHEFJJGGIDIKFDHIGFJKHGJJHHJMJIKJKWhfOANXJ<@?=>??@@;8;??:6/.,,.4;@Vss^W\ZQORNIJIJJKMOQSTTSQRQNQSQXTTVUQPRV[]YVWYYZXY\ZURSSZ_\YXXVSSSTX^cfddaYTTVVSRTTRWag_cilhbdicegffe`ZW^`bfe``dH)WhH0351EH9D@:7@DVE+'0*+2002.275.()--(-'(3108EF=ADCC@BBAADEDEDEIHEEHFEDCEIIFDHHGHIIKIHIIFGIFGHKLKHIKJKLKLMJGXhiWAJ\M<=A>=@>?@<79@<1-/-.5>@EP]uxaVSNLMMLIGIJJKMQSSTUVSOPSPNPSUUVWTRVSV[]][XV[_^ZZZYZ^\[\[ZY[YWVVXZ]^adc^YVTSRVWPNW``]`cb`bcabegfda]Y^bdcbb`\fT((GaV@=::;CH:2>7FUB*0/+)++)(+/1002.'#(+))/227FJ>=UhgddcbbfkpxoK1:A95.'&4HNLNMNNNNNNORUOJMLKNNPTTOMQSSMLNNNMKLKJJJKLMMMNNLJKNNOONNPSUWSQTZ][XXYZZZZ\^]\]`cdca`cgjlmoquq1 ;E-&50$/5/'-0% +0/04@NM>@OLGF7KsqZ>@<;??A=<89<945138DTYVVe~cUQLJJJIIHIJJKMQSSTUVSQPPTVSSSSSSRRUVUX]\XX\XY]_\[]_\\]^\Z[^_\YWWXZ\_`^XSQRRTSTQLOZb_]_b``cffffc^[[\`cecbb_[eX,#E`[JHGIGDEGJNLPQKJLMPPLIIKNLILIRSKIEB6*%! "%0/86%"*-.2('-$#5D@3--.21*/-+12+&&.735HH,5;?9@L<$01/--,*+(,/.+***,,++.2317EJ>=ThgbcdddfjmuoN4:=0)$"$2GQNMLLLLMMMMURNMMKLQPNORPNOPMOSSNNOOJMNMIIJMLMNNMLKJIKLMORTTWUSUY\]\ZYXXY[]_^^^^_`abbehijmquvp/5@2++  $/:1$&)"'7842106==9CBDE14Xz~|H?ONMSPQQOMLMNNLMNJA<=88>DDDEFCD>Sj[LV^SV[XROUTTUVVTQRWVTSTVWYVT[ZCBf!02 "''5604889>BBAABBCC?@BBDFCFEFGGEEFHIHGFFECDCBCDDB@CBCCABC@CCCCCCCCBDEEEEFEEFHJHEEFGFIJFDHIGIHEGIHFHGHIJHHHIJKJKKJFTek[CGYM<=A>=@>?<;99<=?DCDKTTFCNlhUQMPNLKJIHGLLNRTUUVUQQTTTUTTUWWVVWUUWZYZZYWXZ\[[__YWY]``_`bbb`^][[Zba[SNOTWQPSTMKU^`^adbacdgec_YVZ`befcba^Zd_5>VVONID@?=;;<>E@;DMNMTNHFJJE@?FK;*+4=9.+%$,:8"%.23,-()=??/(043.&'&)12+%"1;55DE4-0;>DM?()+,+-,-/*,.-++/4.+*,.-,,8DI>=SggaceffgikupO354%#$(5JUROKLLLMMMNRQPLJMOLONPRONOPPPPOQUSLLLMLKJJIJMNKIIKKLLOSRNQWTUXZ[\\\YWVVY[\\\^`a`_`aecdfloqpxm,2?5,"$0:1##'!5C7002-'(),SlZIPUSXWWYUPVVTSWTPRTVSRUUVYTTZXH@["0/!&05313448>@??BCA@BBCECEFFEEFGFEDEFHHGFFEBDEEEDCBCBDB@@@?BC??BB?@DFEEFEBBEGGEBBEHHGHFGHGEHIHDFJIFGGHIKJIHJIJIIIKIFUgo`EFVL;=A?>@>><968=CJRPKIPNBAQmnYTOKKJIIIIILLNRTTUVWTQSUSQUQSVWUWWRWUUZ][WVUXYYYXX]]\]_``abbddca`][[\ZTOOQRMOSSNMU[Z]`bbefdba_[XX]cdgfcaa^Zdg?5GLIF?<:>A<64=>64@NPFCLHAA:,')68)!"'68/00&+3'$+0104(3@@;-*2550""#*00*&!155==796IKHKK>0:Lf{|B?QMPQLLOQPMKKMNLLNI@<=68?FFDDCGEA=<@>@?;::;=DIMPNLQTY_pu^WNGIJIHHJLJJMQSSSUVWURTTRSVUVUTVYVVRSX]\ZYX[]\\Z[ac`^]^^addfgcaa`_[\[VQPQQQOPQNOX_Z[]^_dge_^[XY^bdegfcaa_\ahI/AIE=:BAQegeeeccejmxqK'$$*&'4FKKNLLMNNOPPOOPPQNLMLJKMOQQNOSTQOOQRNNNLKKMNKLNNNLNQNOONQVVRWWYYYYWWTUWXYY[[__`aacdeabdeimswyg'.B- ,92# $!!  >@>>@=<<;DS_Z[XVVUW]u}fZKLMMKHGHJKLNRTTUVVTVXUUUSXVTSRUWVSUWY[][XXY[\[]^[^_`a_^_cfjkhddb``a]VQQTUTMNSQOWa_\^bccdd`]YW[beeegfbaa`^^eO!+AIB;57:7/++-117B94;.76*.02D6/.&&:?5.-3301)"!&/53%8,%9B=;3+)3:-$,0,,-,/14:CJKIHHEENL7.+/022.))$&)*,---/--01/+*8BF?>QdhgfdbadinupM)$#*&'6HMNTJJKLMNOOOSROPRPONIJPQNOQQSRRSSPPRQONNOPPMJIKMMMOOMOTSPRYWWXXYWURUVWXXY[^]^_abcdd`bcegjnqxe&+?- '61$"&"1IJ@;4379?GKIIJIFEFF>537DRcg?FLPQPRQPNLMOPMJMLNQLB<<::@DCBCCFB:QgVGUTSURRYVQSQTUQPQQOQVXTSSQZXXUTHCe1.,10121/2578:>@ADDDDBDDCBACEEEGGFEGIHHGFFGHG@CEDA@AC>=@CBABAACB@@DECBFFCBFGFEDFHIHHJGFHHGGIHHCDGIGHHIFEGIIHGJJIHIJJGR`ibJHYT:0'(16-%*1/*/77PchefedcehltqO,%###&4FLMPJJKLMNOORNOQPNOPMLORPMORNRRRSQPRTQNNQQPMNOOMKKOSOPPRVYYWUVXYXVSQUUVWXZ[\\_bccbcd_`bejllkv}e&'7( &41&$($0JRNKMEFLMLKGIKIDDHKJ@=;0387DTbdm}}c?ANMNQNOOLMMLKKKMMLMOJA<=88>DDDEFE@9Rm[LXXQTVTRPSRQSUUSRRSUWWUUWXWTYVXL>T /++22252142/39@DCA@ACEBBBACEDDEEEDCEHFEEFEDCDACEDBABC<=<>A@@CCBBDDCBCBCCCCDFGFFEDGJIFIIFGJHFHGGJIDEGFJHGGHHIKKKIHIKJHR_gaIFWR<=A>>@>><8:<:;??FC=98?WsuzjRJIIIIJJJIJLPRRSTSVUTVTST[[XX[XUXZXWXZ[ZXWVTV]^\\Z[]aba`abghdabbaab`YSRRRSTUPJNY`a``__bb]YWY`fhggdfeaaccadf^4!/%$;1&5(1?.0--?:'.2&)-0/-3=3-;<) *9/*//+-.20% "$)-/'49*,AEFHC92'!),)',.,+)*/-,.1/)')$,0/.10.8AE?>PchdeffeegivsQ-$"#'+7GPNNKLLMOPPQPNRSOQSMMNONMOOLLPPOPPPSQQPPONMLOQQNLMOORSTVXYWUVWWWVUTTWWX[]^]\_bdda`cgbdfijloqu|d'"-!(84% $#!$DTKELJFINJEHLHGEHKJGHIDI;0644-887BFCFWaafzwuw{{`9ADBAEC@CCCDEDDFIHGGGGGFDJKIGIHGHJHGGFEEFIJJGHKKHGKKHGJIESflfPFTO<;?>=@????88>4##3YtrpTMLLKIIHFCGMPQRUWVVUTTUWYYTRTUUW[WWYXRRWYXXWVWYZYXY[\]_abefeccddbda]XUUTNWQLMQQV^ac`[^`][YZ^cddehfccffa`bacbA '3$"9/.2&9;)**.?6'10*,%//(5:.*/94%%20(+20-1442+$ "%'(4@7%(9@8..1+(.4=>><989;CHKT\ekwyQ:=IMNNMLLMNOMLLMMMLLLNMGA>?99?DCBBBHB8SkVITVRWZVWWQOOUVSSRSRRUURRUUTWWTTUK=l"0, .4122222333:@B@@ABCCBBDCBCEEDDEGIIIHFDCDDCACCCBA@@@>@BBAABD@BBBBDCBCBDGGFDCAFJJGFHLIKIHIHEFIFEFGGFEJIGHHHHHGJKHGJIFSaghTHTR>;?@?A?@?;46<0 &A_sq~w]PLIIJJIGHJLMNPSVVWXXWWXZTSSVXVUUW^ZSUWWZ[YXXXWX[^\ZZ\_abefgfdcdfc_^]VSTVRUVVTPT`b^]]\\ZTUZ`cddddegihc__b_cdG  ,!1,/3(56*'-180)2*00!*5,.3%)+)%%-5.+-/-/32032*$$&("%28/-9A:48??>><999878:;;7200/+)**'$&*,*(.-*+,)(+%&--&'.0,(&&')+6AC?>LbcbbdfdcgmtpO/)$" #*9JOOQPNLKMOPPRONNNQRONMMKKMONTQOPSTRPPNQTSRQNQPPRQPQTROOTURQRSVURSTTTYXWXZ\_`_`aabdcagddhjkmmv~Z#):5&#*)4HMLLKIJJJKJKJJJJJJIIIIKKIHGFEDCBBAA@@@<;:9888958;=AFHHQ\hjiuzq^RC=@GKLMMMMLLMNLNPPNLLNGILMHA==67=CCCDDGB8SiVLZYQSVSTXVTUXTRTSQUVSSWWSTSWWTUVMASu!.)!06444333455:?BBB@@CCCDEEDEGDDDEFGGFEDDFGGEC@AAA@@@A@@@AAAA@@BCAACEBFEDFGFDCGGHIJJHFFGHGIGFGKHFFGFEDFEEFGHHIILMKKMMJW`ejYGPP>8=?>>=?=:6<@/ $''''0B\oq}hUMGGKLJIIJKMOPRSUWYXUSSTYWUTTTSS\_XV]]Y\]ZYYYXWYZZ[^`a_]ccdedbcgfbb_WSUXUXYXUQU_ebcb]YWSV]defffdafigdcef\bhO!&#!,#0/,;9.,15;/'/+67!%5) '#43 4=3+-/,+14/0671+)(%# %,/8@A?:9::Ncdda`abbfkytT2,)%)'*.:IMKMNLJIKLMMMNPOMOSSMNMNPONPLORQONQSQSSPOTUOQPQRRPRUSSUTQTWSSTUTQRSSWVX[]\^```_^adfegbcgjkort}~Z# '43%"+-"-IULJNMIHIKLLKJHJJJJIIIIJJJIIHHHIIIIHHHHDEEEDBA@=;;<;99:8:?>9=@:=;:;AEIJLMMLLKMNNMLJKKLLKLMLF?;<66=CDCEEC@;ViRHUXPPTQPRRTRTTSUSSVVWVUUUUSVWTVWQGFd!-'"16534454445;?BDB??C@ACDCCDFBEFHGGGIFEDDDDCABBBA@@?????@BCBBBFDABCABGFFEDDEGGHIJIIGFHJJJIIIJIGGGGFFGGIHFGKKHGJJJJLKIWael[GNO>6;@=<;>?=7;=/&/6965:GYkn}m[RHGKKJKJIIJNQRSTWYYVTTTSWYVPOTXTW[\ZX[^^][Z\^\XXYZ\^___dddfggghddc^Y[][YZXVTQS[`b`\ZWSTZ`egffgghkjfdec^]biU)$('*$0)+<7*20-0,*2/3;+*80(.5=?:@:7:;;<89?EDCDDFA:UfRHUXQPRQSWXSRVVUVUTWTUVUWWTSVVUWYTN@ABB>@EABCCDDEFBDFFEDEFGFFEEEDCCCBBAA@@;>AA?>@B@CCBCDEGEDCDFGFCEHKJGEFGKJJHFEGGGEEEEDDFFGGDFIJGFHIHIJJHR`fl\IPSA8>B>=>??=67:0%)/-'):JWck{maYMHIIILJIIKNQQQRUYZXVUTXVRPQTVVTUZZX]_Y]\ZY[_^Z[[[ZY[_bcefghjjigggb\\^ZW[[XUPRXZ\XSUVVV\_cffddfjlkfdda\_biY0!*)&",3,+0,(60#&5;17A<9?>;<>=;:=A<4,-,+,.//,../372)$!!#!#.:CCBCCG@;WjVNXWSRQPQSRQUYROTSNSUTSWVUWSUVUWXVSEBg".&&2322210/269?AABBA@D@AAABCCCFGGGEEDECCDEEECBCBBBBBBA@BDCA@ABEBDE@AEE=BGFCBDGHHGHHHGFLKJHDEHIKHFGHGFEGFEGHIIIHIIJIJIIRbgj[JQR@8>B<=>=>>77>4"3ESbgxme`RIIIHLJJKMPPONRUY\[ZWVQV[\XUTVWY\[X[^^^ZYY[\^a]_`^[Z\_`ehggijjmkki_XWXZ[XVUSRTY[[TNSZ[^`chhfdedikhcaab`ag]7'*&(+02,#%/A=515=@;6:;9:<;62;>9:@;.,,*+-,+0.../11.**#"%# %/881*((#  !"""%((')-.+,-,+,.,()-))//)+1-*+*()-3,(*+'$%3CE>=PfdfcbcddhmvrV5*.-*)116HNJLPNKJKMMMLMKKNPNMLOOOONLLPPOOOOOOPOONNSVSQPPRQPQTTTRRSNMRSRSUSTX[XYYXZ]\Z_``^_bdefhhedkpooz~_'$! (34(!!#2BNPKHLJGJLKIIHJKLJJJJIIIIJJJJJKKKHHHIIIJJKKLLKKJINLKLKIHIJEINLMLGEKPPKIMRLMMMKLLNNNLKJJKKJKMLF>::45;AAABBE>;YjVLTSSTUWWVTQTWRPUWUPTUUSSUXTUUUVWWVL?O|+$(5444431149>@ABCDCBCCCCBDFECCDEDDDFGGGFEDDCCDCBBBB@?@@AAAAAACADC>@EDCBABEHGDEFGGHIKLHGIHDEIIFDBDFFEEIGFFGFFFGGGHHGFFUdfi\JOQ?8?@:=?;@@86<4# &$':HUedtoheVIIJIKMLJKLOQRNORUXYYYSUX\ZWVYZWVXZ[\\^\\]^^`d`aa_[Z[]beijiijlkikjd\ZYZYTRTTQPXXXSNRZ]bbdfhhfefhjhc^^a_^ea@(33:89>:26>><=<:76:99<==<99==95331.,,**-+*/0/01/,**/-'#$%$"&'"!" $$#""#&(*,/-++-/./.+,..*,,*,330/,+)(''((! ! 1BE<;PfcaacdcbfksoX9/54.*437JQMOSQNMNOPOJNOMNNNPSQQQONNNNMMNOOOORKLSSPQTPOOPPOPSQOQWTKLTSTTVWUTWYVSSWZZYZ^```bdecdfffkqqo{a(%!(14' "$/;VdOIRVTRRSRRSROSTSTVYUQSUTVXTUUUUUUVXWF6T+$*5321232249=BCAACDCDDDBBDFEBCEGECCFIDEEEEDCCCBAABBA@?BDB?=>@@CC@>ADDCEFEFHHGGHHHGHKMJIKJFFIIHFFFFEEFHJIFGJJGJIIJIHGGUbbg^MQSA:BB;@B<:=87;2$!()#)@NS]bqz}rigWIJLKKKJJKMPSTPOOQUY[\USUXXTSUUWYYY]_[Z[\[]__]^__^\[[[cbdhihhijjjhec]TQUWUSPLLXTNNUZ]`ccbadgfdc`affcbe^\ddE ,>CB>888:;3<9637>;:=;??;3.41/(!#*)#+-*)-,*..,-11-,.,1/&""" & #')'&&'.+,020./00.+,//,*,.,)%%'&'&" !"!$*-2CF==Rhd^_ceb_cjoseOGJD87?99IOKNROMLLMNNLPONQQPQSNQTQNNMNOOOOOOONRRPSSPNNPPNNPQQOPTSNOSRNPQUYWSVWUVZ]\[[]ab`_`cfabfhgimnq}d,&")14*'*(2AQOMJIMNKIJKLLKJIGHHIJKKLHHHGGFFEHIIJJKKLMLJHIJLMJNMIIMMILPJFMOJHLJHIKMMMMNNMLLMNNOOMKKKLIKMMG@<<;;@ECAA@@;b!+ *000210/26;>BBCDDCBACDEFFECBFFGFECBAEEEDDDCCCCA@AB@=>@A??ADD?@DDACFDBCDEFGHHJJIGEFIKJHHIIHHIMJHGGFGIFEGGEGIEEJKHHJJGRafj_JHM=9>=>C=7;B1#%(AOW]_nwswnlm]JFHILLKKKKOQPVPOTXVWZZ[YXZWSSVPU\ZY^`^^__][[^Z\_a`][Z\`bfkkgfohffb]YVUVVTROMLSNMV]\\a_b`_cffhiecdfea^^bdiP$(AIA85;?<:;?@@>:86510/..+'$$!"&)+&()((+./,01/153--/10-#)(%" !%(('(*.-)%-/.*,21*-/-)),+'$"  ! ##"&#)''*-3;=:@H@:Qhfbdfc__dirncZ^f_PWUKGPPHILMNPPPMJRNJLPMLQPNNQRPMKMNONNOOMNQSSRQSTLKLNNMNPOPPPQTTRPOSVVSU[YTWZUW^^^[`d__dcabdgjkkknvyq4" ""()3-'%&'0@OOKIKJIKKJIGGHIKKIHIIIHIDGJIIIJIHIIJJIIHHNMGHKJILMNMLKLMMLJKLLJHHMNJJMNLLLMNLMMJLOOMMLKMKIKLF><<77:@CCBAD?=ZjSHTSTVUQPSSQSUVTSRSRQRTVUUUVRTXWUTSTTN@;_!*+100432359=?@@@A@@@@ABDEEEDCADGGFDCCEEDDCCCC@@??@BA?>ABB@A@?AABBBDEBDCBEGIGEHHHIJKJIIJIGGHHEEFDEGFDHGGGIIGFGFGHJKJIHO_be_OMN@<@>;=;;A>89?2#$(BPUW]jrosnhi[LIJIIKNMLMLMRQSQOOTVVY[[[[YWWVSUZYX[_`^^``^\\\]^]]\]]]`beijhhieegd^YTVVUROMMMPPRY_]]a]abbefddifefggd`_bbiV*#=H@=>A?859241)&()'(&$#$%%%'%""#&&&&()(),//.00./11-120/0+"'%$%'))(.241-+++,-,+-/,'&&" "%)-)(+)(*++-1112209AG@;Qgfbdec``dirhVKOUVZdXTWQGFJMOOLLOOMMHIOOKMSUPQTQQQNONPONOPNLOPPPRTUPNMNPQPOQPPRRQQSRQRTUVWY[XTUZ^][^bbbea`fdccceiklnx{x:!!""*4.%"#%1AMMJJNMIIIIHHHIJKHGGHJJKKLIHIIIHIIIJKJJJJMMKJKJHIJJJKJKKLLLLMMMJIIJLNLIJMKLMMLIINOLLMLKJIIHLOH?:9779=AAAAE?;VfPIVXTSTTUTPSQRUUQPRSUVSRTUUSPRWWVVUWWTMA>_!)-22255679;=>EDCA@???DDEEEDCCDEGFDBABDDDCCBBB@@@?@@@?=@BBBBA@ABCCABDCCDFFFEFGKLLJGGJLLJIIIHGFHDGHEEGEEGFGJFDGMJHGIIHGTddebTPM>:?=<>>=>>96=3$ANRQVdplirwqde[MJIGGJKHJRQNSTTRONPTVWX[\ZXXVWSRV\\\_][[]`_^\]]\[YYZ[]`bdhhiklhhie_YRVVURNKIIORV[_][]]bccddcchfeegheb`a`h_34?8421,%&,)&" #$"!!$'*+#! $&'&#$$$&*,++,,,,-//--)(.1.*-*'(,/0/-.,)(('#!!!!"!"$%((*,.--.31.,,**--*# %-0.8AG??BAFNOXZQKKHMRPIGMQPOJJMMPTSNLOSRRTRQPPONPQOOOONNPQQOQRQOPQRPOPQRQRSQQPOQTVWZ[YWY\[YZ^`ac``dddddefijny}B !"!&-0,'%&&.::56:@CB@@G?;?>>>=86<20MUVWY^ceforlfg\NFFHKHMMMPPORWSPQOMOUUUX[XXXVXVTWZ[Z]^^^^^^]^__`_][ZZ^bdefffhjgghfc^XRRQOMLLMSXZ\^]ZZ]bdcbcdeeeddegea_`_ie;*0+('($!  &!"$')+,,*'##%'&%$$##&*,**+,-,+-/./,*,//1210/-+)'#&%" !"&'&((),/0/0,,-///,+-3/))++-1&(2316AG?@Hh!'!386789;<>>>=CBBAAAAAGGFFFFFFHEBDHJHECCCBBABABBBAA@AAA@@?@@AACBAABBBCCCDEGHHHHIJKLLIHIJIGHJIFEIHFJHDFHGGEEHLKDGHHKOLDO`aefULJ@<@>;=<;><76=09RPORXY^`cjnj``YNGFGGIMONNQRQTTTQONPPQPUXYZ\YUVYYWV[]``_\[[]_[]^^][ZY[_cehgfhecefec^WVSNIGINQW[\\_^\Z\aeecdeedffdegfb]_aihB"$$#$%%""#!"$%%'*+'),/00/.3.(%&&%#%%%$'+,+&(,..,./,..--)(+$$$$" "!!"!)+,-0465224675311/,+,--,&//(),.10,'0>>QcdbbbaabfhpkI2<:2=ABEKNJIPNNOOLIMSPRPNOPQSORQPQPPTPSOKNONQQNNQRONOSSQNNPQPPRRPPQQORRTUVVWYZVW[\^_^^bdeeeegefgfggikrrzS!#!" !!%2:1)-0.&!"%.:CC@AGIGHKIIHJJJJIKLLKKJHJJIFGHIGKJJIIIIIIKLIHJJIKJIIJJIHKKKJIIKMLKLLJHLRKJLMKIJJKLKJKKIILIKLE=::89<@BBBCA;=ZfQJUSQRSSTUSSUVSRTVVTVVSQRTTUSTWYVUVWXSTXVLB?LYk~"&$7;9;==>@@AAADDEEDDBAGEB?<;:9989>EHGEDDCCBBBBECBCB@?@===?@@@@BBAABABDBCDFGHHIIJLKJIIIKLKJHHGFILHDFFDGFCEIGGHHFFGJLLIGRbaegVJI?;?>;>=>Qada`__`begvpL4<;-/716FNJJONLMQOJKQKOOMPPMLQRSQPPQQPSOKNNMPROMOQOOQORTTRQPPRPOQQPOPSQQTWWVUZYTU[ZVZ_`cc_`baegijhhikqox\*$##" (0--471*+--)$%'-6?A??CEGKLJHHIJIIHIJIIJJJIHIKJIHJHHHHHIJJLIIKKKLKJJIJJKKKIJKKKJKKGJLMMMKJJMMLNNLKLKJLMIGLMJJIB<;<58[ePKWTRRSQRRPRQQRRSTUUTSRRSTTWURSUSSWTURVWRQOL>;ETi#&&:><=@@@@ABCC?ACDDA><0-*&" #)1:CHEDDDDCCCC@@ACBAA@??@A?>=@BBAA?AGCDFGGFHIJIIKNNKHMLLLJFFIHEECADHFKGFFFFHKKJIIIHHHRb^ah\RN<9>=>95<2'HVNNQSSW[^fhc`YQJJLNLLQQMMPRRQTVTRPPQQRRRTTUVZ[ZY]^[Xa_]YY[]][[\[[\^__aabedbcbchjif^TMMLKKMPSTWY[]]]`ccffegiffkkfehgb_Z_dgQ&(&),.00111.+*((+02478778:71+(*+)&&''%%'))&('$##!"!$&&(./11100025992+,33454320/45311320.//.01.(+,.,*++)*61(,,&%2BF=@Q`c_^]]_adennR9@>+ )*)3DJJMOLMPOLLOOQNKNSQKOQRPOPONRQPNMOPONNMNOQRRROLMNOOORONPRQPPUTSSVYYVX[XW\\[a__cb]afcadhijjmolmy}f0$$##  ""09:2/133/)00(!!&/9??=?GJHIIHHHJKJIIHHHGGHJGHIJIGJMKJJIIIJJKGHKKKLMJJKKKKKLKKJJJKLMJMLIHKMLOMJLOMJKKKMNLIJMJHJLE=::89<@@@ACC;=YbMJUWRPRVXVQTTSSSSSSRRUVTQTXTSRTWUTWWTVYTRUVRIED?@LXks~#''<@=?A@??@ABCEED@90'!!0ABB@@B@@BBA??AA??BBBFFEDFHIGFJKLNNMJHIKLJGGHIEFGFFGGEEIHEGGDEGKKHGJJGTc[[e]SL?;?>;=<<<@;4;50KTLKLPQUY]eg`[VOJGIJHMQMJPRQTUVWVUVUSRUTSUUUXRZYW[ZWY\\[XY\]]]]]\[[\]]^]_dfefeehgdaZOMLKJKMQTW[^`a^_ccchjiihbejkecfe`aY\afW* +.+)+0541241*)-0/.157778:5/*)*+($"$$! "$$"%# "''"(++,2789>@A<635940154//475453002133026620100241,)+.--44*3D=,/4,%1BF=@Q_c^][\^acdniQAC9#&$,IOGNONLMNNNNPOPQNNOLNSPLPPOQTPRQLOSNLPPNNPPMONOQPNNPSRONQSPLRTSPQVXVXXVTW[\\_fc^abaffghgfgkoinzzm3%%#$!-1' )6<8,-41-15./*$$(.68?CEGGHMIHHIKJHFGGJLKIHIGIJIIIHFKJIIHHHIJHJKIIKLIJJJHHHIKJJKKLMMOLIJMMLKKKKKJLNLLLMKHKKFJHJKE=;;79>BCAABD=?ZcNJUTRSTSSTRQUVSQSTSUVUSRTUVUTRUYWVYVTYZWTRWQTSKDBCACDHLOW`d$'#,=>D<0%+?BEFEDDD?ACCA???BAA@??@AC@=>???ADEDDHKJHHHKMKLNLJKJHHHHFDFIGEEGFFEFHIHHIGGHJLKIHP]XWe`NLA=>?;:<=:=86:2CEFHFFIKJIHHFFGJIHGIJIFFHHHJIGHIIIJIHJMJJJJIHHHHKKLNMJKJJKKKKKJJKKJJLLJMJLNMLLLJKLLKJIHKJJLE<<:88AECA?@F=?ZbMLXUSQQRSRRTTTTTTSRSVTSTUTUUSSUVTTVRUWVVVUTUSQRTRLFEEEDCCBB"$".>BB?ACDCBBC<@DC@?AEA>=?@?@CC@??@ABCBGFEHIHKLKJJLLJIIIJKJIHGDFHGDEGFDGIFDEHIGIJIHIGDM\VQ`aQJA=@CA>=<;=75;3 #@RNMRRLOSWZ[[[TQMJHHIIMNOOPQTVXURTWZYYUUTRPOTYUVXYZZXTY\^]\\[Zaa`_`ba_]cedddddgkgbdbVLJMNMMORS_b_]afd`befedehjkgeedccd_\\\ff?'00+,36323/*((*))-,)&$"!  ####"$" !&-/.0343355531147:98<>EC8146..16863203446521544332/,/230/0/,,++8AADD@9:-"'3AE?=Ma^[YY[ZY\broG()1-!'HSIHKLOPMJKPQONOOMLMROOOOPNKPTUTROMPNJJOSQMJOMMOONORPQONNOOOQRQOOTWXWYXVXZZ[\`cdbbdfbefeegjlmqx{7'+"!%"/7.$#)485,*./04551(#$',3@EFFHGFIIIHHJKIEFIKIGHIJHHGFGHIKJHFHJJIJNLKJJJJIJKJIKKJLLKKJJJJJIIIJJKKKLLKMNLJLOLKMMKIHIIILG?<636@CBDCBA:@^cKIVSOPRORURSTTUTTSSRRTTSUUSRUWWVUSRTWURU[ZTXXWWUTTTSRPNKIGF #3A@@?@<;@FA3'#2AIBCEECBBC@@@??@CDE@>@@>>@ABBCDEECEFEEGHGFJMMLLJIMKJJKJGEFEFHGEEGFEGGDEFGFDIJGGJIEK]WO`gVHC<<>===;<=64<5 +HRNNRMNOQTVWWWRPNKIGGFHJMNOOPQPTWVTSVWVVVWVUVWUUTUWYXUTX\]^_^\]_`___aa]_`agihfjkhfe^SNJLMNPUY[c`_^adc^aefddfjjia^de`]aa]__gkI%..,,00/1/+%""!!#""!#$&'''('%&'&####(.0/06643.-353248<=<7>HG2$2BE?=L_]ZXXYYW[`jpI"')(%DOLMKLOPNJKNMOQQPNPRSRQONLKLMPQQSQOOKNOLKNPOKMPNMQSMOTSQSSPPORTQRUWUXYURX^_^___acdedeghgfilmoqw|{9 #$+& /5-%$(384-.52-.0:3'"$%)3DGHGIGFIJIIJIGHJHGGGJMLIJIIIGHHHJIIJJIJKLJGGIJJIJKJIJHGGKJIIIJKLLIHKMLLLKMKHJMMNMJHKLIHILLKJF@?838ABADB@@8?^eLKZRQUXTSTPQQRRRRRQTSTSQSUSRVYVUVURUVXVSRUXTVXVTTVYUUUUUUTT&9A<<=B??@9*0?DBCEFFDCDEAAA@>?AC@>;:<@B@ABA??BDCD@CFEILFIJJKMLIJJIHIHGGHFFGGDEFFGEDGHFEEFHHGILKFR`TGXgZLC;9;<<<:==44<6 3NSMOOKOPQRSTUUOPNKGEEGJLOQQPONSSVYVRSXVUVWWWUUUUTTVXYXYZ[Z[]__[^`_]]^``aaceefkfdgh_VRNIILPTX\`caaa]acaaddcdiife^]dd][__^cbclS#"&'%%&%$)(%!"#&&&'*.2589;73.'$&'#"%&&(.1003253&!,5335:>??@=95443/20,+*)''+,,/4434430--/001//0/,,-5DFA?:9?D4!1BE>>L\[XWVXWVY_mn@!*&AONNLLNQNKJMPOOPQQPOQSRPPMMRPPNOOONNOLIIJLMOQNPPLLOMMPQPRRPPPSUUUWVVVYWVZ][Z]_ababdfeffegjkkmpv|z7"++(.5/'#$1;3,085/04<8,$#"(7GJHHIHFILGEIJGGJIIHHIKIGHHJLIIIGIIJJIIJKLJIHIJJILLKJJJKKKJJIIIJJIKNNKIKPOJKNLIJKNMKLKIHKIJIIB<<969BCAB@>A9>]dLGQORUVUROORRRSSSSSQTTTVVTTTXXSPUWVTTRQTWVRQRTVUUTTWWWWWVVV)<@;>@AD>."+02369;>?@AEGBBBBCBA@?@?>@B@=ABA??ABBBBGHDGLIKIKONLJHJKJIHIIHGFFFDDFFFFHHGEEGKIFGJKHDR\PBSbXJB<<@A?=9==44<6 9OOLKNOQQQRRSSSLLLIGFHIHJLOSVWXVSSTUSUWVXYXVUWYTWXXXZ[[VZ]^``^[_^^_`_^]dccdfdejcegcYTQJHGLTXWWZbbfeabfdacb`chifa_aca]\`__cabl[+$''&))*.1,(&)-01-28>=?B?74552/////.,)*++,//.0310/.-.02..20*+13$0CE>>KYYWUUVVTX]ll=!)DRNHLLNPPKJKONNMNNNLRURNPRPPPQQOMMPRNMMMMMORPKNQLLPOOMMNMNQQQQSUVVX[UXZXYXY\\^aabbdfceeegkmmimw~x3#(,-4.'#$1;8..30,18>>2'&$*:IKIGJIGIGEFHJJIHHJJHGGHGIHIJGGIHIGFGIJHFIIJJJJIIJJJJJKKKJJJJJJJIJKLJJKKKLIJNMJJKMNLJKIIJIHIKD;:846?CBB@ABBB:1##'%"$((+.259>BECABCC@==<=@A@><<9<=64<5 !=NONLJMPQRSSSRRMLJIGGGGIIKNQSTSSWXURSUVTVWVUUUUVYZYYZ\][[[[\`aa]]^`_]]^`baafihfggb^[SKIJINW[ZZ\acedbefe`ccaaddb`aba_]_aaa_^an`3)/0/49;=?8.((,02.27::;;=950*&'(' $$"$+/./10044484347=?<654/*010/.-*'*,)'),-.2000/,-/2.,/0..0140%'15<;:-0DE=>JWWUTSUTSV\fl?! (BROIMLNQPLIJLOPONORSRTRQONMNONOOLMOPNOMJKOOLJKOPMOQORNOQNOQQQPQUUTUYZYUTXZ[^]]^adedcdghggilnglv~w1#" '.4,$#'278/.2/,.2?C6))(,:JKHGJJHIFFGIHGHJGGFFHJIGHHHGEEGGHHGGIJIFFHJKKJJJIIKLLKKIIIJKKKJJKMMKJKKJKNMJKKJIKLIHKKHHMJHKF><:34>CCBAC>:>X^LMXTQSTPRUSSSRRRSSSQRTTSTTQTVXWUSQQXUTUUSSUWXWURRUXSSTTUVVV':A@>5%!#%(,15;>AA@>??@>>>?@ABAABBAAACCGEDIJIJKGHKLKJIJKKKKKIHIEEEDCEEGFDCFIHEEFFEFHIGOYOBPaZJ@;:;98;;;=75;3 !?ONPNIIMOQSSRPOLLKJIGFEHHJNQSSRSRSVUQPRWWWVWXWUWYZYXZ\]^]\[]`a`Z\_a^[]`]bcabdefgaZYWMFIMOSX]``^bfd^`dgfbbb```_^ba`_^^`abd`]ald?)12.5>?>>7.(),0225:::9:;84/*$"$% &(%&,/-453113558657<@AA?>98;6.+,./.1330')%!'/22210.,,/1,-//,*.3.0--48@=2/DE=?ITUTRRSSRUZkg;+&# $?PNOMLNQQLIILLLJLOQQOMQTOJMRQNNNLLMJOOPPLHKQONNOMJLPOOPQQQQPRRSUUSSU\ZUTY[WUY]bdcbcedggedfhjhlu|w2"$!-5-#"(262,.4310.;F<+)).;JJGFJJIJKGEGIHGHHHHGHIIGEGHGGGGIHJJIIJJHJJKKLLKJIHHHHJLLKKKJJJJJMLJKMMLJJKKLNLJLMMKJMKGHMJGGB<<946?CBB@A@>AA>@@ACCABE@DFEEFHJQPNNOKJOKJIIIGGGIEDEDCEEDCDFGFEGFKKGDGHGJVM?L_YI?<>A=:;;:=86:2$BNIMOJJKMPSSROMGILLJHGGIHHJNQSTRSTUVVUTSWYXVVVVVXYYZ[ZY]\[[]acc]]^bca`aa_]_bbbfd_]YQNMJMSXZ^b^W]hieghebe`\\^`aae_\]^__aahc__hfI+43-3==8;6/+,.//-27:99:<;4.)""%!#!%/1-2025448996459=@@>=;<<406.133330*(*&"(00+231-+,./-)*--+-35/-"&3@G5+"/EE[bMJURQPPRSRQSSRRQQRRQTSRTUTTRRRSTUUUTTUWVSRRXURTWYVSUUUUVVWW-@?-(:C@=;:975/,'" """%',4;=?C>>>>>?@AB??BBBBBDEFFHIIJKLLLMLLLOMMKGHJJEFFEDEFHFECCDEGGHDFHEHIAMXK=L]YJ?;9;=;:9=?;088)FKJMKKIJMPRRPLHGHIJJIGFIIHHJNTXUVVTVYWRUUTUWUSXUYWX]\X[a^]]^ab_bb`aec````_`dd`]a]ZXSMKLKPW\]]^abegfffeba`^\\^adb`^]^`bab^^ccioN!"/3/2;=;51+)-.---0246:<<=7/'"""%!,1+.25435788315;>=;=9:91/465523411//*%#%*,-/10--..*.-.-)&,500-!$269B<%0EF<;FSUNSTPPTWVgjG((" (AQLLOKHHKMMKKLOQPOPSRPNNNNNONONLKKLLOOOPRRPNKOQPPNMPONOPRTTSTSTVWVTTZYUSVVUW]]]]`efegeeeffjnonux~A# #,4*&4850142...2@A4*&.@KHIIEDGJFHIIGEFFFGHIHHIJHHHIJHGIIEFJJKJFIJIGJMLHIKKJLJHIKKKJIHJNJLJIKJIKJKLLKKLMLJJLKHGHCHKJA::799?BCB@BC8=_cJJSSSSRPPQSSRTTPRTOTTSTTTTSTUVWVUSQUUUUTSSURRSUTSSUTWWTSVWV/:,%5>?????@@A>=83/,)%"%%%+4=>@@@A=@BA?ACCDHIGGKJGIIJLLLLKMNKHHFEGEGHFEFFECEEFEEEFEFEEGIHGNSKELZZJ@=;;<<<;9>;398 -EKMOIIKGNQQPQNIKKKJIGFEIGGIJLQWVTTWYWUVTYYUTUVVZUUZ\[\`^]^_\VYb^^_`]^``\]^_a`^`_[XWRLJLPRV]aca_^fifcdb^]_``^]_ac`aege`][`ccagoV' -313862.)'+.+),,,,.38;;:3,&!#$!" *-',12//34230/38:8775566325751123542+%$&'(*-//-,.-+(+-)%+48/3,#4F8'95"0EF<;FSTQTUQPSUTejB)BRMMOLIJMNMKNMMOPQRRMRSPNPNKKLMMMMMMOPPPPQQQMOOOPONOPQQQRTTSRTVVTSTWVZXTUXXX[\]`bcddfdeikkhfllsxH#!"+1'+784002111.3<<5-*1?KHHHGHHEIIIIJIHGGHJJIIIJHJJHIJIIMIFFFGJKIJLMKJIJIHJKJJKJJJJJHHJLLKJJJLKIJIIKKKKLLMKIILLILKIJC9:;6;A@@C@?@7<]`IJQQPPQRSSTPQSSPRSPQQQQRTUWTPSUQTWUVTRRSUTSSUVTTUUUUUVVVUSS" -8;>>>=<<>@@AA@?=952-(%%&-8?BBCCABDDBCDGHJJKJJKLLLKJJMLIJKHFIGFFGFCCEDEGFDCEFDGECGGEGKLHDHW\M>>==>?=:9;92840FLLMIKMLLORRMJIKKKJHGHHHFFGHHLRVSSVWSRTVWYYUSVX\YWWWY[\Z]]ZZ^]WWZ__^`cbeb_`ca^^`][XTOMOPSX\_aa`geege`[Z]\\\]_``aeggffb^_abeebi]4).*+.-)$'''*,*(-*()09=>;3-)&'&"" !)*''+,)*-.,-)(+1578469:8448872245861*%%&%&(-/.,,./--('))*+($*07C>*#*,%0EG=;ERSQSSPNOQQ^d=$?OKJNLKLOOMJMNOOOQRSSONQRONPNNNNMKKLLMNOPOPPOMLKNNMMPPQRRRSTSRRSTSTVRWWTTWXX]\_bdcdfddgjkiijiiqwT #!!+4,!!*693/./144/35860,4@JIHHHKIBGHIIHHHIGHIIIHGGGIHGHIHHIHHIJHGHJHHJKIIJJIJJIJLKJIIIHGIKLJKKJMMIJJIIKKKJNMKIKLKHGGHJC:;;79@CEDAA@9?]aKMSPOOQSTSRSTTRPPRSPRTUUTTTUTTSSSTTTSRSUWUQUWWVVVVUTVUTUXWS#*/168:<==>>>@AABB@=<;61.,-08ACCACDCDHHHHIKKKLLMMMKKKIGIHBGDCDCAADEFFFDDEFCFGHHHGHLOICGW[M>>=<=>>;==84831ELNOLIIJMNOPPLIIJKJHGHHHIHGEHJLQSTVVWWWVVYXUWZX[]ZWYZZ[ZY^_[]_W\a`_aeddhfddc`^a]^\XSPOORVZ]`cdd_bca\ZZ[\aed`]^a_egcadebbda``akd9(.,,-)$&&&(*)(*1.*+19>?82*('" $&%!!#'('''(+*)(*,,,-/4673498//4543/147:80+'&%##&,-.,,--,+)(-.)(..6DGFAC@9>]_JLSQQQRRRQOPRRPOOQTRRRRQRRRUVUTVVUWUTSSTUUVSSTVVTSTWVUVVUUT"&&'*,/4;?A?>==<;>??>>;999:;?BC@;>?@@B?@DCEEDFHJILKKKLLMNMJJKIHFCFGFCCFGEDEFFGEEDCEGGGEEFIKHCFTXG<==<<>=::;7383%3?GMRNKMJLOONMLKHIJIHGGGFHHEEFHHMQTTVYZXVVYZWXZYZ[Z[\ZXZZTX`_^_^adb_cfdbid__b`\\\_^VRQQOTUX\bfd_^^\\]]\[\`ca][^baaaacffdccdb_^ih>'-+,,)&&''())),---.28;<95+)+##38*%('%&&&&(,00.-),,)+0547784036232-,1485,+*'%#$&*,.-,+*))+'&,,1?FILHEG>$"16/EG=;EPQMMNNNNPS_^:!& %BTOMMMNOPOMLIMPONOPQQSQKJNQQOMLLLKMPMJJKMMNORPOMMMNNLLNOPOQSPPQTUTTTVUVWVVWX\^``abeefccfjklmjkqwi-#! )2,#%.9:5-,14553328;3,6CNIGHGHIHGFFGIJJIHHIIIHGFIHGGIGGIKKHIKIFFEHLLJHJMKKJHIIIJJJJKJIIJIKKIIIGGIKLKJJKLIKLKJJJJFKMJ@9;917@AAC@?B:@^aJLUQRSSQPOOOQSSSQQSUSPPQRRRRPQQQSVUSRTWWTSUUSSUVUUVRRUWWVVW#&%$%')*,17;==?>;99<>==;;?@<:>=:;?A??A?CA?AB@ADDDEGIKMLLKJJKKKLKIKMHEHCDDCCFECFEEFFFEEGJHGGECHKGFFDSZJ:;====;99<846745?GIIMMIJKLORMHHMGHHIIHGFFGHIHGHJLMOSUWWXXUX][VVYXW[]YZ[XU[\Z[Z\`^_ab`cc^`ca^[ZZ]\a`XRRRPPUZ]`b_ZXY\][XY]Y[_cedb``aaacfgegbacb^fiH&*)*+)(#()'),+*'*-/022130&$(!$9>,'-*'))&))).52)(++''/695862033375.-0341()*(&&'(,..,+*+,,*$%-08HMHLQJJF/"1;'/DG==AE=BabJMVPRSSQPPQPPQSRQQQTSRTVVSQRSRSSSRRURRUURRVUTSQRUVVRTUTSSTT$&&&'&)((*/5<=>>?A<9<@@??=@>BKNJLOJFHLONKKLMJIHHHIHFGHJIHGHHJILQVWXYTXZZ[[XUVX\[X^b]^_ZX]ZUW_^__]^`^^`_\]^^]^`_YSPPQRY__]]\YW[\Y[``\Z]`ba`ab`cdbbdeccc_[bdjlR##,./-(%'((*,*)+*,.-+)'%,)%&'#'58.*,,+--+0.''-+!&')('(/55652002464.,0462+*)*)'&',,,+)),.)).346CPHDEJGJL9&,7*!".DG>ED>C^^HKSRRRQQQRRSOPRRSUTPOOQTUTRPTSRVSPRTRRVWTRRVVTRSVWVYUTVVTVZ$()))'((),06;<==<<=<9<<<=<99>=>>:;>><@BAACA?EBBCEGHIJJKMNNMKJKHIKGDEFFECCDEEEDEFFDEFHFBFJFFHF@IH@BRYK;;PIFNNMLKLNOLKLOQPPPPPQQOMLMOONKJLNOOLKMMKLPQOOOMOQQPMLOPNNOMPRSRQTYWWYXSTY[\ZZ^```aeddhjjiiklqx<$ !.70$"+7;5579753.1:;7409MHJMKGHIGHGEFHJJIJIGGHIIIIHGGHGGJLHIJHHIHHKLJIKJHIKJJKJHHJIJLKIHHLHJMKHHIJMMKJLLJHKKIIKJHHJIHB==837>?ADCCD@E^]ILRUSQQQRRQTNPSRSTRQPPPQRRSTTSSSSSTUTTVVTSSUSSUVTSSWUSSSSSS#(*))*,-,-27>=::;<>>=88>?<;>====<=?@A?=?B@?DEEFEDGKLLMKJLKJKKIJKIEEHFFECBDEDCEEEDDFGFGFEEDEHHFFCBJZQ>9=;QIKKKMNNNMLJIIJMOOOQRTSQNLKMONNONMNNMMNOONNRRPPSRPPNQOMQQMKPPNNRSRSX]YTVWWZZYZ^___afeggfhlmnouv|G!"/<." )9:83000-+++/25639FLKIHGFEEFFHHFFHHIEGIHHHFIHGHJHGIIHHJJIIJKIIJJHHIKJHGJKKKKLKIHIKKIJMKGHKKJJJJJJKLGJMKIIIHGGIH@:8858?@AA@BD:C_]KOSPPPPPPQQQRQQRSSQQRSSQOPSURSVVSSVTRRUVTSUUSQTWTQTUUVVSQTX$*),.,)+/3::;<<=<<:<=;?C?>ACDB@CFDDFDDIKKNMIHKLILKHFFEDEFGFCBDFDEEDCDFHDFGGGGFGLFDDAETO9:@;8<@=:973388=CLKLOLJHKKLLJLLHHIGDDGHHJLIHKJGIKJLMIMTUSRRTX]^\[[ZYX]_\\ac`YVX[[\^`bb`_`\[_^[\`ced_ZWURONNSX[ZY[[`babcb_bbbcefffbadca``cccbebcrl>&.-*)*++*)(%$'++.21./0-*+03114-))/1..2."'1(#*,'%$!&4?<50...+&)*)'(./**)*-,'$%$$'**+/353139AEEIECCB?BHE8(/CE=;BLNKMKLMJKRW]B#=QIKKLMNNNMLMJHJNQPOSRQPOONMPNOPNOOLQOMOPONOPPPPOPONQROOPNLOOMPRQRUWWYYXXXXXX\^^]`dhfddfgkmlmosu~R""!3='$23-)%$$####'+1317EJIIHGGGHIGHIIIGDEEHLHDDGIHGHIHGIIHGHHHIJIIJJJJLOIIHHJKIHIIIKKKJJKJJJHIKKHJKKIHJLKLLKIIIJHFHHA;9737?ABB@AC7Aa]GKTQQPPOOOORQOOPRSSRRQQRSSQQUURSVVSSRPSYVQSRSUVUTTSXVSSUUTT$%(**-00.69;<<;<====<<;<<>=<=?@@?>=AB@BFDDBEHEHLJKKMMJJLLJLLGEFFCDCDEFDEGDFHHGFGIBEFGHGFGGDDCBFTM:6?@?;=>=741588;EKKKMJJJKJLKJJLJIIHGGHIJLJJIIIIGIJJIJKMOQRWYUX]]]YUW_c_Z]``\[^]YUZ_```ab`ba]\]abgjh_WTRPOORX]]\\Z]de`]__aabbddddcbbcda___dfba`bolA$/.)**,)))'&(+-+,24/,.0-*/4/&%,,---,--,"%.+"$+0*'$!&4??951,+)%(,0,(*02++-0/($$%"$)../1110,+-/.2124435:5.&/CF=;99=<9:?@?==>???A?>??BCABEDBDHJJHIHHJIGHJIGGEDCDDCBDEDCCEDCCDDDDBDDEEEDEGEBA@FUQ:7<;:;><=96445:CILJKLJHIKNMLLLKNLGGHEEIIHHJKHHIGGIGHLMKMQRTUTW]^ZZZ\^[[c____]YXYWZ[\_cc____^\[_deeaVNKLLKMQVZ][Z]`_^__`dc_acadeb`c^[_dbacedbffbepN"/-*+*+*++'*-,+*(,330212+*22)&+*+.,)+0-.((*&#*(%##"*8B8541*)+*-.01..2621.**,,*%#&/5533010-+,-,+)(+-++++*&/CE>ABB@AB:D_ZIOXQQQQQQPPSPPRTSQPRQQSRRRSQSUUSSTUUSTUSRSTSRUVSQSURVWUSSUW &(-11..4<>=::<9:;::;=>???>>>?@BA?>@@@BDEDCFGHIHJJJKJHJHEDDDABDBDEDCBCBDEEDCBCDCDDDFECDFABEDBNN88A>==>:<:7545ANPMLMOLIILNNLMKKNIDCDBDJKEIJJLIFGFFJKHIMNKOQSUUW[b[UX`_\]\^ab]VVYZ[\^`a_]a_^^`abbb]VPNMMLLOQSY^]Y[^__`__caacbbgjc`dc`dfc`aacbb^\dmW& +.--+('*,++,((*''/43144+&-0-..,++*((*+,'&)#!*%#$%'/;C;874/./.,.2311442/*(+/.*&(/5744521.))*-,*'&'*+,..-).BF>=BLMJKJJLIJP]Y@, 9RMILLLLMMNNNMLKLNPRRSSRQONMLOONOMNQQNMOQPONQPONPPOOOONLOQRQPOMOTUSSUXTOQSTWYZ]]]]affdefhjkjkmov{d)$#  !$&$(')**/=KKJIIHHIIJHGGGHIGEHHGIGFIIGIIGHJHIJJHIIIGJJIIKMKGJKJIKIHHFIKJJLKIKMKGIJJLIIJJKLMNKIIJKIHHFGJIB;9936>@AB@B@7B][HMTRRSSSRRQSOMNPQQRTPOQSSQPVSRRSRRTWSRTTUUSTTTTUTSUVSSVVSSW"+43./4:=<::<:==::>?=;=???>?@>BBBB@?DE@BHFEFFIGHJJKJGHFEDDEDDCDDCCEDCBEGFDCBCDFEFGFDCJFCECBOQ>7;;;;<;;:5459GTVNLMNMKMMKLNKJKKHGFGGFGIKKIIIJHHFGIJHIJKORPNPPT[]][[^_\ZZ^``_^[WXY^dc]]a`ba][^^Z\XSPNMLLOSSTYa`[Y]de`]__^```acccddcccbbecbb`bb^ah\-(.-***&',-*(('*)',22-31+),,*-/.*%%'%"#$&$""&%%&((.7<>?=81-,.,131-142+-021.,,./14543353.*(*)'))*)('()*-*-BF?=BKMJKJJLIIPZ[>#7ROIMLLLLMNNNKIIKNNNSTSRONNNPNNOPRSRNOPPNNNNOOKKNOMMOPQRQOPRQNOQQSROPWVRUURTWXZ^_`bgebcfhlljjkpwzh(&! """""!&'))(2CNHHHHHIJKGFGHGHHGHKHEEHGFHHIIGHIHJJIHGIIIIJKKJIGFKLJJJJIKJIJJIHILKIGIKKKNJIGIKMLKJIHHHHHJFGKJB;9858?@@B@CD8Cc^FHQPPQSSSRRRRSRPOPRQRQPQSRPURRUUSPPRRRRSRQQTVSPSUTSTTTUUUUV &-1159;::;=<<;:<>???>>===>>BBB@BBA@EFDFJHDHKHHKLMLGIHFCDEEDFDBBDEDBFCABEFDBEEEEHGEECBABBAML@:>;>?>78;8425GVTKKLMLJNMJKMKIIGFGHIJIHHMIHHGILJGIGFKJHJPMKNSTTUV[_^\]^\_`bc`ZXYXX[ceb`aba`^ZVVXUTQLJJJJORTTX^_[`_ab`adccaadebbf^]`dcabc\`c__b`anh:&0+%)/)&*.+()()*+-12/31/-+(&%*%#%&$$'"%'$!"#!! "#$)25=A?:60.330.,-00,',10+(+/4201587400.+***(%'*+****(-,!-BF?=BKLJKJJLHIPXX86RPIMLLKLMNOPNMMOPPPTSQONNNNKLPRPOPNPPQQNJLPPOMMNNNNPPQQPNPRSPQRQPPMOSROQSTUWWY]acccfdeghijgiiqx}zh("! ! ! $)&&4ELJIIHGGHHEFHIGGIJIGGHHGGEGIIGHHHHJHHHHHIJFGIIHGGHHJJJKIGGJGHKKHHKIIJKKJKKJKKKKKLMLLLIHHJKLIIG?:9:69@BBB@AB8Dc^FKVOOPPPOMLTSRQRSRPORTRPQSSQUWROPSSPRTTSSTUTUTRTWURSWXTSUUS !$(,05:<<;::>=;::==<=<<>??>=>@B?<@DB@ACBDIKGFIIJKIHIIFCBBBBBCAABBBDFACEEEDDEEEBBEFFGFA@CC?KOB9=>=9;<;=6325DOLEJNNKFJMLKLMJFGIFGGEFHGFHHHIIIKFEEDADIKMHJNPRTRXX[\Y\`a^bd`\[ZWX[^`bdca]]ZVUWVRWTOJLQQMPSWXZ]^^`cc`_]^c_^ac`beddaceccc`a^^_cebdmj>%2+#,.+').-(&*'*,**--%'(%$)+'&&'('&).&)'##%$%$$$$#)/2>>=<<@BA@CDADBABFHHGJIHIJGEHEEDDDEDCA@CFEDDCCDDEEEFFGHEBEFEFIECD@@JOC:9;;=:7;<7/18ISPJIMMKHGLJMPMJJJIIIIHHGFGKKJKLLLHDDEC@CJKMLKLNORSW[ZZ\_`\]``^__[X[]]_aa`X[ZVSTUUTTSQNMMNSVVW[^^]_aec__ba^^bdbbdcaababefb_acccccadjN+/---)%'-/,(++*)*'&('"#&&'))%%$%-31+'+,'&))&'''%*35/6>56A;311/-,*()+,0/)*263101111233.*+./.,)%',)##'&&%-@C=?@@C;F]UELROQPOMOUPOLNQRRSRRRQOPRTSQRUUQQSRTRRTSQQRQPQSSSTVSSUWURRU !"#*0:=99;:9::;;<<=<<===>>?>?@@>?AACDDEEFHIJGHJHFFGHECCCBBCDDCDECBCDCBCEFEDECFIFDFHHFCD@@IND99>=<:;<;9117GQPJKKJJGJLLNNLMMKHHHHHHHHKLMKJJJHIHHHFDDEGIHHMRQORUYZZZ]`]\^]ZY[\Y[]`cc_YVWVTRTWXVVURNMNOPUX[_`^]^bfebbcb`acccdb^_baaega[bcdd`]^bjoT#"-0,+,,)&*32,,*))*+))(&%&$%()+%#(.10/01-'&*)#$')*.9<73:59DA6-.,)&&(**-+*-24432235652021/../,(*'&'('()+'$.AC=>=<<=?=;=?>=@CEDCDFGGGIFHJHFFEFEDCBBA@DECCEDCFFECCDDCADEGGCBEDDBABAAJOA;<;;==;969549GOMLMIMRMJKJLOMKJHJIHGGFGGIIKKIJLJKKKJJHGFGHHJKJJMQRVYYX[`ZZ^`\WWX[\^_ba[TUUVWWVXY]XSQRROLSTTV]a`^`egfdbabbffccddcababe_\baaa`aabagmX)*.--+*)(,032-*)**)($&('$%')+-(%&(*.40+)*+(&&%%',5>=35=BFB4.-,+++,---&-1004521134541.,/0-+*& #&'''(*+*".AD==<<>@A>>B@?ACDCBCEFEDGGGIIGDFBDDCCDCAEDCCDCBDDDDDDDDDEIIFGHGHDC@B@?FKB9:=:::<:56239FQLJJLNLHKKKLKKNOLKJHHGHIIJHKMIKMKJGFIIGFIIIJMLHJRQQTWYZ\^[[_a^ZYWV[_`_]ZVVVW[ZXWYZZXSNKMPPTWZ\]^`cggdca_bcggefda`_ca`b_Z[_aababba`k^2&--.*+(',550-)),+*'$'+(%&('(/+''+00.-(',+%#'%%&-7BA97@LJ5$(1/++.-(&*+.475004:840.//.595-(*($#"%*/5>==>?>;=????>ACEDCCDFFGGGIFEFFDCCCABDCBCDCBCCEEEDDDCCBDEFGDBEFDABB?DID;<=:9:<<75026CNLLKIJJJIKLNNMNMHIIHFGGHHKILMJKNLKHHJJHILJJIIMONOQQSUY]^\\\_`__\WPW_bb_][[XXZ[Z[]`YROOOMJPUZ]_^]_`cdcca`cfc^_dc`_aca_aa`_e`_becaaboh:(-,*+,,*,2543-*,-+)'%(&$(($#)++)*,-+*)(%%&'''(*,1B8)%**.)'('%)/035400:FGE@:762,043,*,,'% #.106F6(0CE>=CLLJJKLKJMQUO7''((!/PTLKNLILMLLLMNOMLNRTTSQQQPNQOMMNLKLMNOPPPOOLNQONNONNOONOPPPSRQSURPSVXVSSSRQXXWX^``abccceghgllmt~k.#$#&(()+-)  &%(/56;BJHGGIIIHFFHHGFFHKIGHJKIGIGHIIFGHIGGIJJIIHHGGKJHGKKJIKIGJHGHIIHILJJIHJIHHGHKLKHHHIKKJJJHHIGJH@:8439A@?ABDB8F_UFOSOSQQRNQTQORTQNOQRQQQRRSSRTTSVSPPRSSSSTTRSRRRRQSUUUTRSVUR"&'$'5=:9;;;::<>;=>?>==>==>@@BB@ACDEDEEFGFGHFFFEDDCCBBBCB@BDCCDCCBBCEFFDDDGGCBDDEEBDDBEJD??<9;<;975116JSPGGMMIGJLJKLKJLMIIHGFFFFIHJLKLOOJKKJIIIHJJHJMMJIPQRTY^_\]]^__`]WV[_abca^_^^_aaaba]WROOOOTUVZ_`]]^acdca`aa^]aec`___^`ba`aaaaca`bgfqmA$,,,-+()*-2794,*+*((''&(+,(%)&%&)*(&&%$$%''')*+-/9BE?2,(%**&..*&&+/1/1007FQVOMJGIIB9364/-.,((,/57-(0:.!0CE>=CLLHIJJIILOWR9)'($/RVNKNKHKNLKPLJORPNQTRQQQQQQQOLMONLMNOPPPOOOOOONLNPPQPNPQOPSRORWSRTTOSSSTTRRVWXZ^__`cefffhigjnnr~k.  ""&'*)'% "!$+0.2;FJIEFLKEHKKIHIIGGIHFHKIDIHGHIIGFJFEGHHIHJIEEGIIJIKJGHIIHKIIJJIIJJJIIKKIIMKIHHIIIKLJHJJHFFEHG@<:72;@?A@;AC9IbWGNPOQMORQROLNRRNOQOMQSQNORSPRRSTTRQRRQRTUTRTTRQSUTRTTUVUSST")'$+7<:;<<;;;<;<<=>>??=>@?=>@?AABDFFFEFEEEDDFE?BCBBDDCECCCCBBCDCBBDDCBDEFDAEHFFFBCEACHG<<>;::>:852-9^hPHQPLLHKLMPOKKLKJIJIIIIIIHIKKKMOMNNLLLIFIJKMNKJLNPRUY\]^b_^^]^^[]]^^beebddccdeeba`\UPNQTPTX\^\\__`cedaabc`aba`acee_\``\]bbba``bdekmK!*-.-*$ (24/681)**(+(&&)*)'#&')+)'')'%'**((+347:>EJI9&"$'**+(**)-44.0-0?R[XPPPLHJPQNIC:1-+(%.8;83)(57/&1DF?=CLLFGHGFHJLWQ9'!.QUKMNKIMNLLMKKNPMMQSQPQRPPQLLMNPONPNNOOOOOPPLMOOPPMNRPMNPONRQOPVUQSTTRPRSQPTWY[]^^`a`cghgjokomqn/#''##)-(!  !%)*0:BJLEEJJEGHGEGIHDFJJGGIIEHHHGHJIEHEGJJIIHIIJJJIJILIIKJIJKIIIJKJIIJJIILLIIDLNHFKLHLKIHJKIIGFIH@<:62;ABEB=EC6E_TFQUNPRQOORPPSSOMOOKPQQOOQRPTSSRQQRRSTSRSVTQRTUSSSSQVTRRSTUU!&*($+;::;<<<;:==<<=>??:<>>:DA4EbWFOSOOTPLQPMNQPPSSPNMQRPMOSVURRSRSSONSUQORSQSQQTVUSSRTUTSSUV !$$$%$'0<:;=>:9<=>>=>@?=????ACB?A@@CDCDEGB@DECBDDCCDEEDC@ACCDDCC@BCCCDDDDDDDDDEEEEBBA?DGE;;<:;<>;;65-;:629>=@A?DB7IbUDJOOMPQMNPMNPQPOPQPOPQOOPQQNORSSUVSTSRQRSTTRRTUTRSTTTTTTUUV! !"#"! #!#&''()6=?;:99<>>>>>>>=?ABA?@??BCBACEDBEDCCAABECCBBBBBCABBCBBAABA@BDDCBCEFDBBEHADB@@CHI@9:;:;<;<=2/1:MRLKKKLKLNKLMMMMLKHJJHHJJIMKJLLJKOLMPOJILLJJLMLJIJMMMNPUY]^_adb\WUYZ\^bhkkffdbcffdfa[WUSRSQSV[_bba]difba`^_^_bcabfcbcedc`_b`^`bda^bbkf9'.*&())((,133>A3)+*$&/1)&,20..014421220.,-.0/10-374,*+--*)(,(%+59AO_RJT^VMPQOOOKKJGNHEIIEEIECA@@?:32,*#-DC>=AMJIIIGFGJMUQ/+JPMMKNOKKNMJMMMPQPPPPQRRQPQPPQQOONLLLMNMNOPOPMJLOONKLNNLNQPRRQSSQRWWWXXWWWXYYXWZ^__`aacdghjmmkss2$"""!%*'#'%*24:DIHHHGGGFEFHHGGHHGHHGGHFEFFGHHFGJKHHHHJHFGGHHIIIIJJIGFJGFHJIIIHHIJIHFEHIJJJJKLJJJIHIHDGGIG>973/:@=@C@A>8J_TFMPQOMMPPONNOPRRQONORSQPRRQSQQQRTUSPRTUTSRSRRRRRQRTUUTSSSRR! !%&$$&&#%'$$'&1:<:;;:=@=;=??=;>>==?BCB@@??ADCA?FGB@CD@BBCA@@ABDB@??ACD@@BCCBCEABCDDCCBDEA@@BGHB;;:8::8:=3/37AKOPMMMJGIONLKKJKLGIJJJJIIKLMLJKLKLLMLJKMJIJLNMKJJLKKLOSWY_`ced_[YUZ]]aghddddcefecgd^VRRRRQSW[_aa`]^bffb``b^^bdabfg``db_``b`_]`cdbc`hjC&-+)**(%$*/654HaTFNRNROMQOMRROMNPOPQOQSRRRRQPRUSPQTTTSRRTTTSQQQSTSQQQSUTQPSU!$&()*))(%$&&.8;:<<:<==>?=;;=@><>@BBBCDCBAABCCACCDBAAADCAAABBBEDCCCCA@FDBCDEEEHE@AA@EHD<;:9<=;<<9426?KLLLMMKIILLLMLLKKHGHHGFGHIHJNKJKLNNLKOPMLJIJLNNLLKLKLMQUX_`aba_\[X[\[^efcbdefdddddc]UORUTSQT\_]]`]^afhfca]`a__bca`bde`_`a``_^_ceed`hoM #++')*)$#'+787?A1&()+65)$+5.-,+((**,*''(+-/12202@D:1/-/32/,)**'0Na\VTTTPOOPLGHIFJLIGIIHGGFF@EDFJFCLLFB6*!0@B?=BLKIIIGFGJMWS0(GOLJLONJJLNLMKLPRPOOPQSRPOPMNOOMMNKNKILOLMTOPQQMOQOQPNMMPPOPOPQQTUTSTVVWWXXZ\]]\\\]ccbcdgjlijhny|w3'-"!#'& #)+/5@IIHGGGEEGIIIIIFGJGFFFIKIGIIHIIIIHFGHIHHHGIKJHHIIHGHIJHKKHGHIHJGFGIJIIEFGHHIJLIGGIGGGGEGKI?9855:>?@@>D@3HbSEPSPSNMTRLOQONOOPPQOOPRQPPRPQSSQSTTUSQRTUTRUSQRSSSTSTTTSSTU!$$"((''++(')8=:;==<<<>>=:;==?A@??>=BABCBAACB@@CECAABBCCCBBACBAABCBBABB@CFFC@BEFEDCCGC?AA@GLF=<;:==;<98428@IPNLKLJHIMKJHHIKLJJKLKHHIKHKNKIJJOPONNLKMMKJLNONNMMMKKMU[\_bcc`\YYZ[]`dfeeeeeddfhg_ZVRQRQSPS[_[[^Y_efc```___accbbdb``cb_]`^_accccffluY#!*,''*(##')1327B;)'*-0/''26((*)(()))))),.12/0203<>3464221/10((*'4KY]_^SJIKHLOLEDILJIHFDEFGGFIFFKFBIIJNIB;0-6@B=:BLLIIIGFGJMXT1&FPLJLNLKJKNLMKKOQOMRPPPQPPQONPQONOOKKLMOKKPNNQOMNQPOQQNNPRSQOQQQRTRTUUUTTTTXZ]^\\]^`abcefhhikkq|}v4'* ! "!"%$ %--4@HHGHHEFHIGFGKJFGHFEGFFIHEGGFFFGHIIGGGHGEDGJIFGHIHHHHHHJJIIKIDHGGGJKJGIIIIIIIIIKJHIIIIJIKG=7863;A>AB?A>7K_QFOQRRPORQMMNOPOOOPPOPPNOOQRRRSQOQUUSSSSSSSTSRSUVUSRTTSTUUUU$'%#&'&(-59:=>=<=<;<>><;<>>??@?=?BCB@ABB@BA?@CCABBAAAABBBA@@BCCB@CC@@DDBEEDEEFFFDD@??BJOG=<=;<:8;:7117@KNNKKNLIJMMMMLKJJIIJLKHGHKKLMKLLJLJLOLLNONLKKKKKLKNOMJMSY[_bdca]YXWZ`deeghfeghhhhd`]YQQROPQTY\\\\`\]dfb^_b\[beb_`eecbb_^ba`aba``beikt`,),(()'#%)*+372:D5$(+**(+3/'(+.-.-++-.0//..)(*.3>?4362---,0.)'))*;RV^^VPNHAGMKEDIKKGBBHIDBEJGFHHHGFHHJD@A>?:?B<9AKLIIIGFGJMWT1%EQLLLJJMMKKKMMLNONMRPNOPPPQNMOQOMNNLQPKJMONONOONOPPLNNNOQQRQQQTURPTTUUUUTTUWWXZZ[\^_`abcefgills}}t4& )2(!%"!))5CIEEHIGGGGGFFFHFFFEEFFHIIHHHGHEFFGGGFFIHGGIIHEHJIGHLKIJJIIHHHGKKJIIKJHIHHIIJJIJLJHHHFHBCGF<7625;@?AA>B>;K]SEKSQRRQNMOPNPPNMOPQNRQMLRSRQSTSOPSSRSSSRSSSTRRSSSSTSTTTSSTU! ")168449<<;9:<>=<<==?><;>A@==@A??@BA@@BCB@?@@ABA@@BCCBBBCCA?CCA?BED@CBBDGGD@CDA?>AJQF::=<;::9>:206ALNMLMNLIHLLLKKKLLKIHGGGILJHHLOMMOQNNOMNNLOMKLLKJJILONLMQUY[^^^_]Z\YY^bceifdehjhgg`aaYOQVTOSVX[^^[]]_ba^]^_abaabba_bc``a`_baa_^^adfjgne5',)(+*'),)*5;45EB+&+0,'+1/))-00121-/2210/0,/8AGLC0-.)(02+((()-+$/GYRMTYPGJIEEFEDGIGBADC?BIJIHFFGFCDDE?<@>?=?C>9AHIIIIGFGJMUQ0%ERMLLIINMJKLMLKNOOOONPQRPONOPRROMNNKLOONNNMLNPPQONNQRPNQQONQQQRTRSXSTUVVVWWVWY[\\]`cccdegijfihmwx}s1&49/ ! &5FIEFHGDGIHFEFGGHHFGHGGHGHIHGHHHIGFHHIKIHHHGFGHKIGFGGHHHIJHGHJKKJIGGGHHKKJIHGFFJIHHGDEGGFHF>::78:?@B?=D?9J_SCKTQPOOOOPPNOPOOOPOOQQNNQSRQQRSSTSOQQSTTSRQQQRTTRRSUTSSSTSS !"$# #&(19995588:<9:<=<<=>=<====?@@A?@AA??ABBBBA@??>ACCA@BDECBBCCCAABAABEFDBCDEEDDCDB?A@?JTJ<9=<:9;49844;GKQKKMJIJFLLMMLKJIGHHIIIJJKKHIMNKLKNMKMNMNRNMOQPNLKLLLKLPTX[]]^`_]][\`bbbdeddfedfijd`YOOSSPSVY\__\\]__``^]_^^`bbceebbb`abaa`^]_cdbjngmj?'-,*,+'+.**1:=9AG8+,1,(045/021//234553/-./35;@CIA/,20+-+')((),,,29FGEINJIURBAHCDJDKGAEFBEDEG@>B@@=>D@;AFFIIIGFGJMRO.%ERNIMKJLKIMNNJIMQQQQPPPONPRPRTRNNNMLIKMKKLJMONKKJKPPSQNOPPROPSTRSVVTUVVVVVWTVZ]\Z\`abcefghhmnlpy{s/(162+  %5EIGIICCFHHGIIIDHGFHHFFGEGIGFGGGHFEHIHJEGJLJGGJGIIGFGHGFJKHILJEIIIIIGHIHJKJIIJKHHIHEGIGGFHG?;:65>>>>>>>????????@@@@@@@@BBBBBBBB>??@ABBC??@ABBCCAABCDDEEEA@@@BMXL=:=;=?:<74535CRMMLLKJIIMMLKKJIIJIGFFGIJKLLLLMMMNNNNNNNNNNNNNNNNNMLKLNQRVX[^___^YZ\^aceedfhjjhfdcdc\TOORQTW[\\[Z\^accb`^bbbbbbbbcccccccc__abdfggdiejpG#/0.+*+++*,)8A9FIOWHEA><=?@B@=;>BB>GGDBCBBDCHE;9@EC=;AB=@JKJHHHGGIMQS3%ESIONLKKLNOJMOOOOQTTQQSRNNPOOOOOOOOLMOPPOMLNNNOOPPPRQPOOPQRTSRQRTWXUUUUUUUUQRUX[^_`bbcdfghhmhjry|t0!+53)"$)'(CKGFEFEFFGGHGFEEFHIIHFEGGGGGGGGGFGFGFGFHHHHHHHHHIJJJJIHFHIKKIHFJJIIIIJJIIJJKLMMLKJHFECCECHG=9:60C>7K\MDOTNOOOOPPPNPQPNNOPPPPPPPPPQQQQQQQQQQQQQQQQRRRRRRRRRSTUUUTT !!489:;;:<::::::::<<<<<<<<>>>>>>>>????????AAAAAAAABBBBBBBB??@AABCCAABBCDDEAAABCDDDEB@@AEOYI;:A>;:5:96206CNNNMLLKJJNMMLKKJJJJIIIIJJLLMMMNNNOOOOOOOOOOOOOOOOONLLMOQRUWZ]_`__]^^_`abbcdfhhgdcffc\TQSVQSW[]^]]Z\_abb`_ccccccccaaaaaaaaaabcdffgjh_esV  -1.,+++)(0138;;@K?.,58CIA)(+12/.0,,,--/013CG>?MSM2)*2128:9525=====;:;>DHH><@B>BHGGHJJGFJPZS0$CQIMLJJIKLMJKLMMNPQSQQQPONOOOOOOOOOJLMNNMLJNNOOOPPPQQPPPPQQRQPOPRTVTTTTTTTTRSUX[\^^bbcefghimhhpv{p0 !'-133390$#@E>9P]IESSNNNOOOPPPOMNPPONOOOOOOOOQQPQPQQQRRRRRRRRSSRSRSRSPQRTUUUU!"0478:867::::::::<<<<<<<<>>>>>>>>@@@@@@@@@@@@@@@@AAAAAAAA???@AABBAABBCDDDBCCCDEEEBA?@AEOXQ>8;97<>8<920:EKLLLKKJJILLLKJJIIHHIIIIHHKKKKLLLMMMMMMMMMMMMMMMMMMLLKLMOPVWZ]_`a`]^_`cdefdeghhgedfeaZSQTWOQUX[\]\Z\^abcbadddddddd________eeffggggabafp[*3:8520,(&/316?:9FC/*-2BK?/,+./-/3421/.---3BE=BVa\B55=;7:>5/+/;BC@E9,%&+0388:;>@BCB>>AB=97=EE=88:=<778614<@??>?A=4OcNBOPOOOOPPPPQONOPQPNPPPPPPPPQQQQQQQQPPPPPPPPTTTTTTTTPPRTUUVV  " #"/469::7899999999<<<<<<<>>>>>>>AAAAAAAAAAAAAAAA@@@AAABBAABBCCCDCCCDDEEEC@<:=EQZJ>9<:9;<9;7./;IOLLLLKKJJLLLKKJJJHIKLLKIHKKKLLLMMMMMMMMMMMMMMMMMMMLKLLMNOWXZ\]_^_\\]_acdeffghhhffgea[TPQRRTVYZ\[\[\^`aa``aaaaaaaabcbcbcbchhhhghggbadhm]2 4CA>:60+(-/29?=:@G<.*6HG8,**+,*+-+++,,./0>C@=GWXL>55;704:;7348;:863-%"$''++*,.379>=?CDC@?>B@:8987438EKJDBDBBA=@FIDGJJECHNRK;(%#! "-AOPKKJJIJJKNMKMOQPNPSRPOQQNPPPPPPPPMMNOONMMLLLLLLLLKLNOONLKPONOOPQRQQQQQQQQWXYZ[[[[\^`dgjlnlijpv{t0#%.5=@>A6'-DGDHJFGIFGHGDEFIEGHJIIFEGGGGGGGGFFFFFFFFGGGGGGGGGGHHHHGGGHIJIIHGIHGGGGHIGHHHHIIJKJIHFDCCEEHG@;836<>>ABAB<5K_RIPROOOPOPPPNPQPONOQPPPPPPPPQQPQPQQQRRRRRRRRSSRSRSRSPPRSSTTS                 -                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}zzzzzzzz{{{{{{{{yyyyyyyyxwxwxwxwwwwwwwwwuuuuuuuuxxxxxxxxvvvvvvvvxwxwxwxwvvvvvvvv{}}zyyyyyyyyyyyyyyyyyyzz{z{z{zzz{z{z{zxxxxxxxxvuuuttssqqrstuuvttttttttyyyyyyyyxy|}}|yxvvvvvvvv~~yyzyzyyyzz{{{{zzyxyxyxyx}}}}}}}}{{{zyxxwvvvvvvvv|}~wwwwwwwwxxxxxxxxyyyyyyyyxxxxxxxxxxxxxxxxwwwwwwww||||||||{{{{{{{{xxxxxxxxwwwwwwwwy|~}zwuuxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwyyyyyyyy||{{zyyyrsstuvwwuuuuuuuuqqqqqqqqsvy{{yvswwwwwwww~~zzzzzzzz}}~~}}}}}}}}}}}}}}}}}}}|{zyxwwuuuuuuuu{|}~zvtuvvuuvwxxxwwvtuvwxxxxvvvvvvvvyyyyyyyyzzyyyyzzyz{||||{{{{{{{{{zz{|}}}}~}|{zzzzy|~~{xvvuwxxwvvwwxxxxxwwwxxxxxxwvvvvvwwwvwwvvvxyyxwvwxyz{{{zzyyy{{{{{{{{yxvvvutrux{|{xvuvwwvttuv|~}{zyyzzyxy}~~||{zywvuuvwwvuuvyz|~~~~~}}zxy{{{{xxyyyyxxxxwwwwvwwwwwwwwwwwwwwwwwvvvvvwxxwwxxxxxxxxxxxxxxwxy{|}~{{{zzz{{{}~~{zz{{|||zyyyxxxyxxxwwxxxxxxwwwwvvvvvvvvuuuvwwwwwvvuuwwwwwwwwyyyyyyyyzyxyz{zy{}~~}{{{yyxxwxxx{~}|{yzzzyxy}~~~~~~~}~{{{zyxvvtuvwvvvwyz|~~}{}|yy{~~}|{zxvuuuvwwwwwwwwwwwwwwwwuuuuvwxxvvwwwvvuwwwwwwwwwwwxyyzzyyxxxxxy{}}|zxyz{{{|}}||zz{{|{{zz{{{{{{zzzyxxvvuvwwvuuvwvvwvwvvvuuvvwwxxvvvvvvvvwvvvxxxw||}|{z{||zxx{{{z{~}}{zyzyxwx|~}}~~~{{zzyxwvtuvvwvwwyz|z{~~~{y{|zy{}~~}{wuttuvuuuuuuuuvvvvvvvvuuuuuvvwvwxyxwutvvvwvwwwxxwwwwxxzyxwwwww}~~}ywvwxxxy{||z{||}|}|||}}}}}}|||{{zyxwyyyywwwxyxutrrrsuvvvvwwwwwwwwwwwyxvwwwutxz{{yxxyzyxyz{{{{~}}|zzzzxvx|}}|~~}~~~~zzzzyxxwtuvwvwwwyz|}~~|y{|yxyyyyz{{{{{{||{ywutuwxvuvuvuvvssssssssuutssssswxz{{ywuwwwwxxxxxxxxxyz{zzyxxxxx}|xvvyxxxzzxwxyzzzzzzz{{{{{{z{{{{{zyxyzzzyxxyyxwvvvxytttttsssttttttttwvuuvvusvy||{ywvvwxwwwyz{~}}{zyzyxwx{~||}~}{{{{||}}~~~}zzzzyxxwvvvvwvvuyz|~zxwwwwwyxxwwwwwwxxvvuwxyxxxxxxxxwwwwwwwwxxvvtutuz{}}{yzzz{{|||yxwvvwxxwwwwwwxx{}~{xvvxxxyyyyxwxxyyyyyxxyyyyxxxyyzyyxwwxyyxwwxxxwwvuttwwvvuutttttttttttsstvwwvtw{|zxutuwwwuuwy{~}{zyyz{yxx|~}{}}|{|{|||||{z{|~~~}{{zzyxwvvvvvvwuuyz|{yxxwvvwxxwwwxyywwwwwwwwyyyyyyyyzzzzzzzzzzyxxxxx{|}}~}}|||||}}~~{ywvuuuvvvvvwwxx{|}|yvuuvvwxxwxxwwxyyyyywxxxxxxwvwxxywvuuvxxwwwwwwwxxxxxxxxxxxxx{{{{{{{{xwwxz{{{vx{{zxwvxxxwwvwx{~}zyxy{{{zy}}|}~|~~~~~~~}{{{|~~}{{{zyxvvvuuvwwvuyz|~~~xwvwwwxzyywvvvvvwwxxxwvuxxxxxxxxwwwwwwwwyyxxxxyyyxxxxyzzyyyzz{{{|{zyxy{|{zzyyyyy|xutuuvwvtsssuvvwwxxxwwwxxwwwwxxyywvutvwxwwwwxwuuuvwxvvvwwxxxxxxxxxxxxwvwxxwv}~}||}|zxxyyxw|~}yxxy{|{{z}}|~~}~~~}}||~~~}||{zywvuuttvwxwvyz|~}|~xnnx}}~}}}}zywuuuuvxwwwwvvvvvvvvvvvvvvvvvvvywuuvwxxwxxxxwwvxxxyyzzz|||{{{{{}zzz~}zxwwxy|{zyxwvvuuuuuuuuwwvvvuuuuuvvwwxxuuttuwz{vvvvvvvvyyxxwwwvvwwuuwvux{~~{xxxxxwwxz|~|~}{zxvvwxzxxyzz{||{{|}}~~~}}zzyyxxwwzzzzzzzz{|}~|{~xpqy|}~|zwussstttsssrrttstststvvvvvvvvvuuvwwwvvvwxxxxwxxxyyyyyxxyyyzzz~|z{|}}{{{}~~~~~~~~~zzzzzzzzzzzzzzzzuuvvwwwxponnoprsssssssssyxxxwwwwwxxwwyywx{~~{yxyvvuuvxyz}~}|zxvuuvwuvvwwxyywwxyyzz{yyyzzzzz||{{zzzz~~~~~~~~{|}}~~|}}~~~}wwzzyzz~~~~~~|{zxwwwwwvvvvuuuttttttttttttttttuwy{zyxwvvwwwwvvwwwvwvvvvwwxyz{{}}~}{z{||{zyyyyzxxyyyyzz{{{{{{{{wxxyyz{{zzz{{{||vvuuuuvwrrrrrrrrtttttsssrttstuvtx{~}{xxxxwvuvwxy}~~}{zxvutuussttuuvvuuvvwxxxyzzz{|||~~~}}}}|||}|xyzyxyxyyxxxyyyyyyyyyzz{{zzzyyyxxxxxxxxuuuuuuuuy|~~|{{||{{zywvvvuuttssuuvvwxxx{}~~|zyy}}|{zzyyzzzyxwwwxxxxxxxxuuvwxyzzxxxxxyyyzzyyxxxxyyyyyyyywwwwwwwwtvvuvxxwuxz{xutuwvttsuuv{~~}}zywvuuuvuuuvvwwwxyyzzzz{}}}~~~~~~~~~~~}}}}}}}}|}}{xxzxxxwwwwwwwxxxxxxxwwvzzzyyyxxzzzzzzzz{z{z{z{{|}~|{{{}|{xwwvvuuutttttttt{}~zwvwwxyzz{{zzzyyxxxwwwwwwwwvvwxyz{{wwwvvvvvyyyyxxwwyyyyyyyyyyyzzzzzxz{y{|}|x{~}{xxxzywvvvxxz}}}|xxwwwxxxyyzzzz{z||}|}}}}~~~}}}||}}}}~}~~|||{|{|||}~{vw{zyyxwwvvvvvwuuvwwwvvyyyxxxwwyyyyyyyy{{{{{{{{zzzyxxxx||}}}}||zzzzyyyyyyxxxxxx|~~zwvuvwxxyxwsttuvwxxttttttttuuvwwxxyxxwwwvvvxxyzzzyxwwwwwwwwxxxxxyyyxzzyy{{z{~~{z{}}{zz{|}{~~}||xyyyzz{{}}}}}}}}}}}~}~}~~~~}||||||}}~~~}~}~}~}}}}~wpr{}||{{{{{wxyzzzyyyyxxxwwwxxxwxxxxwwwwwwwwwwvwwxwwwwxyyyyxzzzz{{{{{{|||||}~~|zzz||}~~|{zvvwxyz{{ttttttttvvvvvvvvuuuttsssrsuwwwvvxxxxxxxxwxxxyyyyxyyxxyzxxz~}{xxxzyxwxxz{|~}||{{||||||~~~~~~~~}}}}}}}}~~~~~~}}}}~~}~~}ypr}~|zxvvvuuuttxxxxxxxxwwwwwwwwwvvwyzyxxxyyyxwwxxxyyzzzxxyyz{||~|zz}xy{}~~~}{{{{{{{{zzzzzzzz{{zzzzyyxxxwvvvupqtvwwvussssssssssssttuuvxxvvwwvy||yyyxwvvvwyz|~~}||~~~~~}||}}}}}}}}}}}}}|||{|||}}~~~~~~~~~~~~}~{}~~~wxyz{|~~~~~~~~~~~~}wwwwwwwwwwvusrqqsssssssswwwwwwwwwwxxxyyyyyyyxxwwyyyzzz{{}~~}}{zyzzzzzzzzyyyxxwww{{{{{{{{{|}|zyz{||||||||xy{}~~~~ttttttttrrrqqqpptrqrtutsvwyzzywvtuvvvwy{|}|{||}}~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}~}xyyyyyyyzzzzzzzzzzzzz{{{xxxxxxxxzzyxwwvvuuuuuuuuzzzzzzzzvwwwxxxxwwvvuuuuxxxyyyyz~~}}{{zzzzzzzz{{{zzzyyxxxxxxxxyz{zxwwyzzzzzzzzwxz||}}|{{{{{{{{{zyywvuuwvuwz|{zuvxyyxvuuvvutuvx|}|zz{{{||}||||||||~~~~~~~~~~~~~~~~~~~zzyxwvuuvvvvvvvvttuuvwxxwwwwwwwwzyzyyyyyyyyyyyyy{{{{{{{{zzz{{|||zzzyyyxx{{{{||||~}|{vuvuvuvuwwwvvuuuyxyxyxyyyz{zxwxyyyyyyyyyxyz{}|||zzzz{zzzwxyy{|}}}|{}{|~~|{{||zyyz{|~~}{{||}}}}~~~~~~~~~~~~~~~~{|}~~~~~{{zywwvuwwwwwwwwtuuuuvvwuuuuuuuuvvvwwxwxyyyyyyyyxyxyxyxx{{|||}}}}}}}|||{{||||}}}}~~~}}|{uututututtstssrrtttuttttuvwvtssuvvvvvvvvtuvxxxwwzzzzzzzzyyyzz{{{|zyyzzxw{|~~|{{||{yyz||~~~~~~~~~~~~~~~~~~~~~~~}~~~~~{{{{{zzzzzzzzzzzzzzyyxxxvvvvvvvvvvvvwwwwuuvuvuuuxxxwxwxxxxxyyyzzzzzyyxxxxxyyzyzz{||||{{zzzzyzyzzyyyxxwwwvvvvvvvvwxywvuvwttttttttopqrsrrqutututuuwwwwxxyyzxvwxxwuz{}~~}{zyzzyxyz||~~~~}}}|~}|{~}}||{{{||~{||}}}~~}}}}}}}}~~}}||{{{{{{{{{{z{zzzzzzyzyzyzyzzzz{z{zzyyyzz{{{yyyxxxwwyyyzz{{{z{{|{{zz{{{{{{{{}}||{{{{}}|}|}|}}~~|{|}yyyyyyyytuvwwvuuqqpqpqqqnooqrstussstxzyyxy{||{yxxxyxwxy{|~{{zzyzyy||{{zzyyz{}|||}}}~~}}}}}}}}~~~}}}}}~~~~~~~~~}||{~~~~~~~~}}}}}}}}|}}}~~~}}|||{{{||||}}}}z{||||{{{{{z{z{{|||{{zzz{z{z{z{{{|}|zyz{}}}}}}}}{|}}}|{{yyyyyyyyuutsrpooooorvxyxtuwxxwutvvvutsuv|~~}}}}}}}}zyzyzyzzzzzzzyyyxwwvuttsxy||||||}}}||||||||{{{|}}~~}}}}}}}}}|{{{{{{{{{{}}}}}}}}|||}}}~~~~~}}}||{{{||||}{||}}}||}}}}}}}}}}||{{{{{{{{{{{{{|}|zyz{{{{{{{{{zz{|{{yy||||||||}}||{{{{yyyz~yz|}}|zy{{{ywvwx|~~~}}|{zzyxxxxxxxxxyyyyyyyy~}{xtqomnnmmkkjjvx{~~}|{z|~~}}}}}}}}}}}}}}}}}}}|}}}}}}}}}}}}~~~~~~~~||||{{{{}}}}}}}}||||||||}}}}}}}}||||||||||}}}}|{{{{|||}}}}}}}}}}}}}}}}}}~~}|||}||{zyxx{{{{{{{{zzzzzzzz}}||||||~}}|||}}~~~}}~~~~~~~~~~~~|zzzzzyxxyz}~{{||}}~~~zpjgdgosqlghnu}~|{~~}|{zyyxxyz{zxu||xuv{~||||||||||||||||}}}}}}}}}}|}|}|}}}}}}}}}}}}}||||}}}~}~}}||||||||}}}}}}}}||||||||z{|}}~}}{{|||}}}}}}}}}}}}}}~}}}}~~~}|||~~}}||{{||||||||||||||||}}|{{{||{{zzzz{{|}}~~}}|||||||||~~~}||~~~~~~}{wtutllv||xpkmu}|zxwzzzzz{{{zzz{|{yw{~}xuw{{{{{{{{{||||||||}}}}}}}}|||{|{||||||||||~~~}}|||~~~~~~~~}|}|}|}}}|}|}|}|||||||||yz|}~~~~||||}}}}~}~}~}~~~~~~~~~~~~}}|||~~~~~~~~}}}}}}}}}}}}}}}}}}||{{||||{{{{||{||}}}||{{{{{{{{~~}~}{ury{tx~wpou}~|{z{|yzz{||}}|||}}}{yz|~yvw|~{{{{{{{{{{{{{{{{||{|{|||{{{{{{{{{{{|{|{{}}}}||{{}}|}|}}}}}}}|}}}||||||||||||||||z{|}}~}}|||}}}}~~~~~}~~~~~~~~~~~~~~~}}~~~~~~~~||||||||||||||||~}}||||}}}|||}~~{|}}~}}|{{{{{{{{}}||~~}}|||vlowy~zrpu}~|{}~~}~}~}~~}}~~}{y{~~yvx|{{{{{{{{{{{{{{{{zzzzzzzz{z{z{z{z{{{{{{{{|{{{{zzz{{{{{{{{||}|}|}|{{{{{{{{{{{{{{{{||}}~}||||||}}}}}}~}~}~}~~~}~}~~~~~~~~}}}|}}}}}}}}||||||||~~}}}}}}||{{||}~|}~~~~||||||||~|{{}}}}}}}}}xihs|}~}~|snu}|{{}~~~~~~~~~~~~~|yz|~ywx}{{{{{{{{||||||||zzzzzzzz{{z{z{{{{{{{{{{{{{zzzzyyzzzzzzzz||{|{|{|z{z{z{z{zzzzzzzz|}}~}}|{{{{{||||}}|}|}|}}}}}}}}}~~}~}}}}~~~}}||~~~~~~~~||||||||~}}|||}}||||}}~|}~}}}}}}}}~~}~~~~~~~~~~xlku~~|||uqu}}{zz{|}z{{|}~~~~~~~~~}{{|}yvx|||||||||||||||||{{{{{{{{{{{{{{{{|||{|{||{{{{zzzzzzzzzzzz{{{{{{{{zzzzzzzzyyyyyyyy{|}}~}}|zzzz{{{|||||||||||||||||{{{{{{{{|{{{{{{{||||||||{{{{{{{{||{{{{{{||||}~~{|}~}}}}}}}}~~~}}||{|}}vmpy}}||uru}yyz{|}~~{{||||}}~}}}~}||{|~}xvw{}}}}}}}}}}}}}}}}}}}}}}}}{{{{{{{{||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzzzzzxxxxxxxxzz|}~~~~yyzzz{{{{{{{{{{{{{{{{{{{yyyyyyyyxxxyyzzzzzzzzzzzyyyyyyyy{{zzyyzzyyyyz{||yz|}~~~~||||||||}~}~}~~}|zyxxy}~ulqz{{~|qmu}}}~~~}|~||{z}||}}}|}||~~|xuv{~|ywwz}}~~}}}}}}}|}}}}}}}}}}|{{{|}}}}}}}}}}~}|~}}}~~}zz|~}|||||||||x{~|zzzzzzzz}|zyxxyyzz{{{{{{{ywvvx{}zzzzzzzzzzzzzzzzyyyyyyyyzyzyzyzzuwxyyz{}yz{|}||{yyzz{{||}~}|~vorvzymmx~vmmu{||}}}}}|}}}}}~}}}~~}~~|{|~~yuvz~~~~~~~~|yvvy}~~}}~~|{xxxxxxxx{}~}{{|~{|||}}~~|}}}}}}}}}xz~}{||||||||}|{||}~~||{||}||zzzz{|zzzzzzzz}}|{yxww{{{{{{{{xxxxxxxxyyyyyxyyz{{||{zyyyyzz{{{|~~~}{yqkovx|xnmw~|slnu{|}}~~~~~}}}~~~~~~~}}~||{~~yuvz~~{xvvy}}zuuuuuuuuz{}|zy{|{{{{||}}~{yx{||{xxxxxxxxwy|~}{z{{{{{{{{~}{{{|}~~~~~~~~}|{z}}}}}}}}~}}||{{{zzzzzzzzyyyyyyyy}}|{zzyx}}~}}{zyzzzz{{|||~~~}}}~~~{vnlrtquvomv|wskiou{}}~~~~~~}~~}}||}|{|}}xuvz~ywtvy~{vvvvvvvvz|}}{z{}|||}}~~~|yvwxzzywwwwwwwwxy{||{zxwwwwwwwwzywvvwxyz|~|ywyzyzyzyytuvxyz{|{{{{{{{{zzzzzzzz~||}}|z~|zy{|{|||||{~~}|{{|}||}}vqryuprumhow~~wpjdeou{}}~~}}}~}}}}}}~~||{~}wuvz~~~~~~~~~~~xutty~}xxxxxxxx|~~}|}~~~}zz}~~|}}}}}}}}yyz{zzyxwwwwwwwwwvuuuvwx{}~|xvssssssssnnpprsttrrrrrrrrwwwvwwwwzxwy|~}{~~~~|zxwzzzzzyzyz~~}|{zz{|z{|~~vqtyxrstkcgn{|xrmeelu{||~~~}{{|}}}}~|{|}|vtvz}~~~~~~~~~~~~|{}vtrty~xxxxxxxx}}}~|yz{~}|~~~~~~~~{{zzzyyy{{{{{{{{{zyyyz|}~}ywwwvwvwwwxxvusrqqoooooooorrrrrrrrsqpty||zzzzzzxwvxxwwvvvvy~~~}|zzz{{zz{}~~uopqqostkflsx|}}yohku{|}}~~~}}~~|{{||{zz|}~~||{~{utwz|~~~~usrsy~~~~~~|~yyyyyyyy|}~|{}~~~~{xxz||z{{{{{{{{}|{zzyzz||||||||~~}}}}{||||||||~}zxutsyyyyyyyysssssssssposz~}zyz{{{zyyyyxxwwwvy~~~}|{zz{|z{||~wrqnlowwnlu{y}vmmu{}~~~~}}|}}{}~}{zyz{{{|{|}zttwz|~}tsqsx~~}||}}}}}}}}y}}xw{{{{{{{{{z|}|zz{}}}}~~||~~~~~~~~~}|{zzzzzzzzzzzz~~xxxxxxxxyxwvtsrrwwwwwwwwwwwwwwwwwsrv}||}~~~|||{{zzyx~~}|{{{{|{{||}~~|wvqpv~zomvz{}}xppu{~~}}||~}}~|{|||||{{~zttwz|}}~~}wursx~~~}|{{{|}|{zyyz{|zzzzzyyy}}xuvwwzzzz|~~zx|~~~~}}~~~}|z|~~{wx{~~|{{|wz~~yxwvuvxzy||wuvxwvwxyyxwvtuuuvvvwvutvz|}}}~}|}~}{yxy{}}|{{zzy{{|}~~}vpqz{smty~~}|||||}}~~~~~}}||||{{}}vtv{~~~~{|}}~~}wsopv~}|}}~||{zz{|}||||{{{{{~~{z|}}z{}~|z{~~}~~}||}~}|z{}~|yz{|}}|zyx{~}|zywxz{y||wuvxwwwxxxwwvvvvvwwwwvutvz}~~}~~}{}~~{yyy{}}||{zzz{{|}~~~~|wsu|}xqmty~~||{z||}}~~~~~||}}~|{{~}vtv{~~~~~}}~{|}}~}yvrrx}}||||}}{{{zzyyy{~~}~|{{~~|}|}}}~}}|~|zz}~}|}{z{}}zvz{~|yxxyzy||wuvxwxxwvvvvvwwwwwvvvvutvz}~}~~}{}~}{yyz{}}||{{zz{{|}~~}}{|{wsty}xxxvspnmuy~~~~}}||{}}}}~~~~~~}}}|{{}}vtv{~}|~~~~~~~~}{|}}~}~|yy|~~~~~~{{zzyxxx|}~~~~{y|~||{|~}|~|z{|~~~~|{{|{yx{}~zxwwvvy||wuvxwyxvvuvvwwwwvvuuuvutwz~~~}z|~~{zyz{~}}|{{zzz{{}}xnijmmnnmihikuy~}~}~}~~~||}}}}}~}~~~~}|||{{~~}vtv{~~|}{|}}~}}|}~~~}||{{z|~~~{y|~}{{}}~~~~~}|{}~}~~}{z{|}~~zwxwvuy||wuvxwyxvuuuwwxwwvvuuuvtuw{~~~|z|~}|zz{{~~}}|{{zzz{|~~xlddhmnnjgegivy~~~~}}}}}||||||||}}|{|{|||{{}~~}vtv{~}}{{}}}~}||~}|{zz|~||}|zz|}}}~~~~~}}}~~|~}{{}zxyzxvy||wuvxwxwvvuvwxyxxxwwvvuutw{~}|y{}}{zz{{~~}}|{{{zz{|}~xmghlmmmlkklnwz~~}{{zz||||||||{|{|||{{|{{~~~}vtv{~}~~~~~~~~}z|}~}~~|{zzy~~~||}|yy{{{|}~~}}~~~~}~~}}}}xwyzyvy||wuvxwwwvvwwxyxxxxwwwwutuw|}|y{}}|z{|{~~~~}||{{zz{|}~}}}ysomlmpppruvwvwz}~}}||{{z}}}}}}}}z{|}~|{z|{{}}vtv{~~}}}~~~~|{{}~}~}}~~}||}{}|y{}{yx|{zzz|}~~}|}{|~|zutwywty||wuvxwvvvwwxyywwwwvvvvutux|}{y{}}{{{|{~~~~}||{{zz{|}~|yvsqpqmikqwyy}{{}|xz}|||}}~~~~~~~~~~~|||}}}|||{{}}vtv{~~~~~~~~|z|}~}~||}~}}|zz~~~~zy}}yxzz}}{{}~||~~{ttwzywz|{vsuxxyxwvvwyzvvwwxwvvvutw{~{x}}}|yz}}}}~|||yz{|}~|yvqichfflu|~~~~}}z{~~}}||{{zz{||}~~~}}~~~~}}~|{|}~xwy}|{}~}~~~~~~~~~~||||~~||}~}~}~}}~zy|}zy|~}|||||}}~}|}}{utwyxvz|zvsvxxwvuuuvwxwwvvvvvwvutw{~{x}}}{y{}}}}~}{{|yzz|}~~~~~vkchfflu|~~~~}}z{~}~}}||{{}}}}}|||}}~~~~}}~|{|}~xvw{~|{}~}~~~~~~~~~~||||~~}|~~|}}~~zy{}{|~|z|}|{||}~~~}|}}~~}zvuwyxvz|{usuxxwwwwwwxxwwvuuvwwvutw{~~zy~~||{y{}}}|}~}|{z{yz{{|}~~~}}}}~{ofigglv|~}~~}}z{~}}~~~}|{~~}}|||{}}~~~~}}~|{|}~xvvy{}{}~}~~~~~~~~~~||||~~}}~}}}~}|y{{{|~|zz{}||}{{{||}||}~~~}{{xuuwyyxz|zvsvxxvwwyxxxwwvvvuvvwvutw{~}~yz~}{{zy{~}}{{|{{zzzyzz{{|}}|||||}~|smjhgmu}~}~~}}z{~|}~~~||~}}}|}}}}~~~~}}}~|{|~}{xvwy{}|}~}~~~~~~~~~~~|}}|~~~}}~}{z{{||z|}|{|}|zzzz{{|}~~~{yuttuxz{{z|{utuxxtuvwwwvuuuvvwvvuvutw{~}~y{~|zzzx|~}}{zzzzzzyzzzz{{||{{{{|}~{upkiimv|~}~~}}z{~|}~~~~}~~}}||}}~~~~~}}|~|{|}|~}{ywxy{|}|~~}~~~~~~~~~~}}}}}}}~~~~{{{{{{{~}{{{zzyyyz|}~~~|yxsstvxyz{z|zvsvxxuvvwwwvvuuvwwvuuvutw{~~~z|{xyyx|}}{zyxyzzyzzzzz{z{zzz{|}~|tmljinv}~}~~}}z{~}}}}}~~~}||||~~~~~}||~|{|~{{yxxxy{|}~|~~|~~~~~~~~~}}}}|}~~~~}~}~}|{{{|{z}~~}}|{zzyyyz|~~}{yxwtuwwwwwwz|{vsuxxvwwwxwxwuuvvvvvuvutw{~~{|zwxyx|}}{zxxyzzzzzzzzzzzzzz{|}|skmjjnw}~}~~}}z{~}||||}~|||}}~~~~~}||~|{|}zyxxyyz{||~|~~|~~~~~~~~~}}}}||~~~~}}}~|~z{{z{|}zzz{||{z}{{~}||zzyyz|~~}{yxwwwxyxvuttz|{vsuxxuuuvvvvvvvuutuvvvutw{~~}||zwxyx}}}|zxxy{{zzzzzyyyyzzz{|~|slmkjow}~}~~}}z{~}|{{{}~~}||}~~~~~}||~|{|~yxyyzz{{zz~|~~|~~~~~~~~~}~~}{}~{||z}}~~~~~zy||||}}~~~{x{~~~~|zxwvvwxxxxvutx{{ustvuxussuwxxwwvsstututtvz~~~~{zwxxwy~|}zxxz{{zyyyxyzz{yyyz{}~}xpkokhlt{~}|}}|yz}}||~~{~~~~~~~~~}||~~}{xxvyytty}~|}~}~~~|}|~~~{}}~~|}}~~~~{z|||||}}}~~yvy|}~~}{yxwvvwxxxwwutx{zustvuxxvvuuvwutttssttuttvz~}}{zwxxwy~~|}{yyz{{zyyyyyzz{yyyz{}~|||||}}|vokliglu}}~~}y{}~~}|~~~~~~~~~~~~~~~~~}||~~|xxuyyvv{|}~}~~~|~|~~~{}~~~~~~~~{{|{||||}}}{wvx{|~|{zxwwvvwwxxxwvuwzzusuvuwxywvtvwwuvwwttuuttvz~~}|{zwxxwy~~{~|{z{{{zzyyyyyz{zyzz{|~~~}}}|ytpmjhhmv}~~~}z{~~}|}|~}}~}~}~~~~~}}}||}~|yyy||yy||}~}~~~~~~}~}~~}{~}~~~}|}||||||||zxwxz}~zyxwvvwwvwwxxwvuvzzusuwvuwxxwwwxxxxyxvttuttvz~~}|{zwxxwy~}{~}|{{{zzzzyyyzz{zzzz{|}~}zwutsqppomkjilkkpv}}}}~}}}}z|~|{}}{|~~~~~~}~~}|}|{~}zz}{{}~|}~}~~~~~~~~}~~}|~|{}~}}}~~~~~~}}||||||{{{z{~~}yxwwvvwwuvwxxwwvvyyusuwwwvwwyxwvuvwvvvusuttvz~~~~~{zwxxwy~|z||}|{{zz{zzyzzz{{zzz{|}~}yuqnlkmmmmnnnoqqrtx{}}{{{z{{{{{|}{|~||}~~}}}}}~}|}||}~{{|{|~|}~}}}~}~~}~~}~|~|z||{{{|}~~~~~~~~}}}}||~}{z}~||zyxwvvwwuvwxxxwwuyyusvxwzyvvwwussuvuvxxvuttvz~}~{zwxxwy~|yy{||{{{{|{zzyzz{{{zzz||}}zxvuuwwxyz{{{wwwyy{{{|{zzyz{|{}~}}~}~~~}}}}||}~~~~||}|{~||||}~|}|}~}|}}~~~}~~}~|~}{||z{{||}}}}~~~}~~~~~~~~}}}{~zx{~}|{{zywwvvvtuwwxxxwuxyusvxxzywuutuuvvvvvwxxuttvz~~}{zwxxwy~~{yvy{|{z{||{{zzzzz{{{z{{|}~|{{{{{yz|}}|{zz{|||{{{~}|{{{}~|}~}~~|||}}}~~~~~}}}}}}||}}}yy{~}z||}~}||}}~~~~~|~|~{||z||||{{{{}}}~~~~~~~~~||}{~~~}{~yvy||}~~~~}{z|{yxwvvvtuvxxxxwtxyusvxxwwwussvxxvuutssuuttvz~~}|{zwxxwy~~{xux{|{z{}||{zzzzz|{{zz{|}~~}|{||}}~}zx{|~~}||}}||}|}~}}}~{||}~~~}}|}}}}||~}~zy{~|xx||}~}|||}~~~~~|}||}z{|{{{zyyyyy||~~~~||~|}~|zy|}{{|||{zyyxvuttuuvvvvvvyxvvwxxxuuuuuuuvttstuutstssvz}~}}{z~|yzzwx|~}{{{{{{||{{{{{{{{zzzz{{{{|}~~~~}|~~~~~~~~}|{zz{|}}}}~~|}~}}}}}~~~~~~~~||}~~}||~z{{zz||}}}}}}}}~~}~|}}|||}z{|zzzzzzz{{{{|}~~~}zyz|~|ywxzz{zyyxvuutuuuvvvvvxwuvxyyyvvvvvuuuvuttuvutsssvz}~}}~~|zy~|yzywy|~}||{{{{{{{{{{{{{{zzzz{{{{}}~~}}||||||||~}||}~||}}~~~}~~}}}~~}|}~}}}|}~~~~~z{zzz||}}}}}}}}~~~}}}}|~|||y{|zzzzzz{{|{zzz{}}}~~|}~~|||{z~}zvtuwyzyyxwvvuuuuuvvvvvvutvz|{zxxwwvvuuwuuuvvvutssv{}~}|}~~|{y~{yyywy|~}}}||{{zz{{{{{{{{zzzz{{{{}~~}}}}}}}}}~~}}~~~~~|}~~~|{|}|||||}~}}~}zz{zz{~|}}}}}}}}}~~}~~}}|~|||y{{zzzzyzz{{zyxxy|}}~~|{|}~||}{|~~zxutuwxxyyxwwvuuuuuuuvvwussw|}{zyyxwvvuuuuvwwussssvz}~}|~~}y}{xyxvz}}~~}|{{zz{{{{{{{{zzzz{{{{}~~}~~~~~~~}}||}~|}~~~~}|~}}z{zzy{}|}}}}}}}}}~~}~~~~}|~|||yz{yzzzyyyyyzyxwx{~~~~~~}||}~zy|~~}wvvwxxwvxxxwwvvuvvuuuvwwurrw~~zzzyxwvvursuvxvsqtssv{}~}|}~}x}~zxxxv{}~~~~~||{zz{{{{{{{{zzzz{{{{}~~}||}}}}}|~}~}}}}}}~~}|{}~~~~~}{|}~|{{{zzz~|}}}}}|||}~~}~~~~~|||{xzzy{{zyyyyyzyyxyz|}~~~~~}{{~}~{wwz|~~~~}|uvvxyywuxxxwwvvvvvutuvwxvssx~yyyxxwwvvrstvwvsqsssvz}~}{|}~~}{zx|~zwxwu{}~~~~~}}||{{||||||||zzzz{{{{|}}~~}}|{{{{{{{{z{{|||{z{{{{zzyy{|~~~}|{~~}~~}}}}}{{z}~}|}|{z{~|}}}}||||}}~}~~}}~~~||{xzzy{{{zzz{{zzzzzz{{||}}}}|{~|zyz{||~~{xux{}}~~|{uuuvxxwuxxwwwwvvwvuttuwxwtsy}wwwwwwwwwutttvuustssvz}~}|}~~~|zyw|~zwxwu|}~~~~}}}}}|}|||||||||zzzz{{{{{{|}}|{{zzzzzzzzzz|}}|{z|||{{zzzxz}~~~~}~~~~~}||||}{zz|~~~}|{||}}}}||||}}~}~}|}}~||{xyzy|{{{||}}z{{|{zyyz{|}}}}}~||}}}~~~|}}{tw{|}}}|{vtssuvwvxwwwwwwvwvuttuwxxuty}uvvwwwwwwxvtstvvwsssvz}~}~}{zw|}zwwwu|}~~}~~||}}}}}}||||||||zzzz{{{{zz{||{zz||||||||z{}~~}{z~~}}wy|~~~~~~~}~~~zyz|~~~}|||}}}|||||}}}}~}||~}~~}~{yzz{||yz|}|{}}zy{||}|{{}}|{}}{~~}|}~~wwwwy|~~~}|{utsuxyywuuvvvvvvvvvvwwwwrsw{|yxxxvuvwyyywtrrsurrsw|~}|}~~~|zyz{vvxxz}~~}{{{}}||||||||{{{zzzyyz{}~~~~~~~~~}wy{~~~}||||~~~~}}~~~}~~~|{|~~}}}}}}|{||||||}}|~}{||}~zzzz|}|zy{{z{~}zxz{{|zyz|}|z}}}~~~~~}}yxwxy{}~|zyutstwxwvvvvvvvvvvvvvwwwwrsw{|yuwyyxwwwzxwvwxyywsqu{}zxyz{{zxwy{vvxxz}~~}|{|}}||||||||{{zzzzyyz{}~~~~~~~~~~~~~~~~~~~yz}~{|}~~~~~~~~~}}~~}~~~~}~|{|~~~~}}|{||||||}}|~}{{{|~{yzz}}|zzzzy|~~zxyzzzyyz|}}{~~}}}}}}}}zyxwyz|}}zywvuttuvutvvvvvuvvvvvvwwwwstw|}zuwyyxwwwzwux|~wqsz}xvwyzzyxwy{vvwwz}~~}|||}}||||||||{zzzzyyyz{}~~~~~~~~~~~~~|}}~z{~~}~~|{}~~}|}}~~}}}~~|{|}}|{|||{||}}|~~}}~|{|~zzzz|}|{zzzyx{{yx{}}zzy{~~}}}~~~~yxwwxz|~}{yxvuuttttsvvvvuvuuvvvvwwwwstw|}zxxxwuvxzxutx}wvz}}{wyz|||{zy~{uuwwz}~~}}}}}}||||{{zzzzyyyyz{}~~~~~}}}}}}}}{||~y{}~~}|}~~}}~~}|}~|{|}}|{|||||}}}|~~~~~~~}~~~}}~~{yzz{{{{{zzz{}}yxyzy|{{}~~~~}~||||}}~~wvvvx{~}}~}}{zyvvvutssswvvvvuuuvvvvwwwwsux}~zwwwvuuwyussx|z{~yz}~~}|x~zuuwvz}~~}}|}}}||{{{zzzyyxxxz{}~~~~~}}}}}}}}{|}}xy|~~|}~~~~}~}~~|{|}}|{|||{||}}|~~~}|||}~~~~~~~~zzzz{zz|{zyz|~}zwxyx|{|~}~~}~~~~~~~~{{z{zzzzvuuvx{~|}}}||zzuvwvtsttwwvvuuuuvvvvwwwwtux}~{suwwvuuuutuz|{}z{}~~~|{x~zttvvz}~~}|~~}}{{zzzzyyxxxxz{}~~~~~~~~~~~~~|}}~~wy{~~|}~~~~~~~~~|{|}}|{||||||}}|~~~}}~}|}}~}|~~{yzz|{{||yxyvyzxx{}}zz{~~~}~~}~}}}}}}}}}||{zyyxwvvvxz|~}}~}|zyxuvwvutuvwwwvvuutvvvvwwwwtvy}~{rtvvutttvxz~}|||||~}|zw}yttvvz}~}|~~}||{zzyyyyxxxxz{}~~~~~~~~~~~~~~~~~~~~xy|~~~}|~~~~~~~~~|{|}}|{||||||}}|~}|||{{|~{yzz}{{}{xww{}}ywxyxyxz}~}~}}}}}}||~|{zzxwvvwy{|}{ywvtvxwutvxwwwvuuttvvvvwwwwtvy~{tttsrrtvx{~~}zx}~~|{w}~yttuvz}~}|~~}}{{zzyyyyxxxxz{}~~~~~~~~~}y{}~}}}}~}~~~}|}~~|~|{|}}|{||||||}}|~~}~||{|~|yz{y{{{zyxxx|}}{yxy{|yxy~~|}~~}|~}|}}}|||{{{{{{~}|{yxwvuuuuvvwxwvvuuuvvvvvvvvvvttv{}yuvvussux~~zz{{{{||{}~~|zxz{|zwuvxy~~~}{}{zyz{||{zxxwww{|}~{{|~}~~~~~~~~|}~~}|~}}}~~}|}|}|z{}||||||||}}~~||~~}z||{z{{{zzzz{||{xxy{{yx{~~~|~}}}}|{zz{{|||}|{zywvuuuuuuvvwxwvvuuuuvvvvvvvvvttv{}yuvvussux~zzzzyyyy{}~~}zxz{|zwuvxy~~~}{~}{zyz{|{{yxxwwx{|}~{z{~|||}~~~}}}}~~~~~}|}~}|}~|{{|||||||||}}~||~~~{}}|zzzzzzzzy{|zywyzyyz}~~~~{|~}~~~~~~}{yxyz{}~{zyxwutsuuuuvvwwwvvuuuvvvvvvvvvvttv{~yuvvussvx~}}zzzyxwww{|~}{yz{|zwuvxy~~}{}|{zzz{{{zyxxxxx||~zyz}}}~}~~~~}}~~~~}}}}~~}}}|}}|}||{||||||||}~~}|~~{|}{{zyyyyxwxz{{yxxzxx{|{~}{|~~}~~zxwxy|~||zyxwuuuuuuuvvwwwvvuuuvvvvvvvvvttv{}yuvvussux}z{||{zzxxvvz|}~}{yz{|zwuvxx~~~~}{||{{{{{{{zyxxxxx|}~~yyy}}~~~~}~}}|}~~}||}}~}}|}}{{|}|z|||||||||~~~}|~|y{{z{zyyzyxwwy||{yyzyy{|{}~|{{}~~~}~~}{xvxz|~|{yyxvuuuuuvvwvvuuuvvvvvvvvvvttv{~xuvwussvx~{xy{}}|{zyxxy{}~}|zz{|zwuvxx}~~~~~~}{{{{{|{{{zzyxxxyy|}~yyz|}||}}~~~}}}}~~}}~}}|}}{z|}|z|||||||||~}~~}~{yz{y{zyz{}{zvz}~|{z{zz{~~~~}z{{{{}}~~}~~}~~~|zwx{}}|{{vuuuuuuvwvvvuuuvvvvvvvvvttv{}yuvvussux~|yxyz}}||{zyyy{}~|{z{|zwuvxw}~~~~~~~}~}{yz{|||{{zyyxxxyy}~~~zzz}}}}~~~~}}}~~~}}~}|}~}|}~||{|{z||||||||{}~|}}~}~}z{|z|zyz|}|{wz~}||}{{}~~~~~~{{|z{}~~~~~~~~~}}}~~}|yz|~}|{vuutuuuuwvvuuuvvvvvvvvvvttv{~yuvvussux}{yyzyyzzzzzzzzxz}~~}{z{|zwuvxw|~~~~}~~}~~}}}{yy{|}||{yyxxxxyz}~~{z{~~~~~}}}|}~~}|~|z{~}|}~~}{zz{||||||||{}~}|}}~~{}}|}{yz{|zxw{~}}~|z|}|~~|zz}|{zz{|}~}||}~~~z{|~}|{vuuutuuuwvvuuuuvvvvvvvvvttv{}yuvvussux|yxy{zyxxxxyyyyxz|~~}{z{|zwuvxw|~}~~}}~}~~}}}{xy{}}}|{yyxxxyyz}~~~{{{~}||}}~~~}}}}~~}{|}}}}|}~|}}~~~zyz{||||||||{}~~~~{}}~|xyyyzzzzzvux~}}~{{}zwxzz|{{{{{|}}{||{~}z{|}~xwutttvwxwvutuuvyxxwvvuuttw{|vutsstuvzzzzzxwvttuuwwxxzyy||xy~~yvwxvx{~~}}~~}}}}~~}yz{}~~}{zyyxxyz{|{z{~|}~~~~~~~}~~|{|{www{}~~~}|z{{{{{{{{}}|}}||zzzzzzzzwvy~|{}{{|{xyyz{~||||||{{z{{{}|{{|}~zywuttuuwwvvuuuuxxwwvuuuttw{|vvtsstvvzzzzyxwvsstuvwwxzyz}|yy~~yvwxvx{~~}~~~~}}}~~}yz{}~~}{zzyyyyz|}{z{~}~~~~}}}~~~~~|{||xyy|~~~}|z{{{{{{{{|}~|}}|}}||{zyyxwy~{z{{{}|yyzyz|}}}}|{zy{||{}~||}}~|{ywutttvvvvvuuuvvvvuuttttw{|wvuttuvwzz{zzxwvrrstuuvvyy{}~|yy}yvwxvx{~~~~~}}~~}z{|}}~}|{{zzyz{|}{z{~~~~~~}}}}}}~|{||xzz}~}~}|{|||||||||}}~||~|~|zxwwvy~|{}{{||yz{z{}||}~}{zy{{z{{}}}}}}~~}{yvussuvvwwvutuuuuuuuuttw{|xwvuuvwx{{{{yxvurrrssttuxy{~~~{zy~~yvxxvx{~~~~~~~}{|}}}}}||||{{z{|}{{{~~~~~~~~}}~~~~~|{|}yzz}~}}}~|{|||||||||||}}}}}||~~~{xwwvy}}{{}~~~~~{yz||}~z{}}}|{zyyyyyy{|~~~~}{yvtstuvwwvvuuuuuvvvvttw{|yxwvvwxy{{|zzwvurrsrssttwy|~~}|{y}yvwxvx{~~~~~~~~~~|~}}}}}~~~}|{{|}}{z{~~}}}~~~~~~~}}}~~~}}}}}~|{|}zyy|~}||~~}||||||||||||||||||{~~|{zy|}{{|~~}}~~~zy|}}}z{||}}|||{{|{z|~}zxutuuuvvvvvvvvwwxxxttw{|zyxwwxyz||{{ywutsssssssswz}}|{{y~~yvxxvx{~~~~~~~~~|}~~}~}}|}~}}|}}~{{{~~}}}~~}}}}}}||}}}}}}||~|{|~{yy|}|{|}~}|||||||||}}}}}~~~~{{}~~}{|{{}~{z|~||}{{{|||}}~}}}|{}~|ywuuuuuvvwwwwxxyyzzttw{|{zyxxyz{|||{ywuttttstsssvz~}{{|y~~yvwxvx{~~~~~~~~~}||~~}}||}~}}}}~{z{~}~~~~}}~~~}||}}}}}}}|~|{|~|zz}|{{|}~~}{{{{{{{{}}~~~~~~{{}}|yy{{|}{}}|{|{{{{{|}}|zz{yxz~~~}zxvvuutuvwxxxyyz{{|ttw{|{{yyyy{{}||{ywusttttttttvz~}{{|y~~yvwxvx{~~~~~~~~~~~}{|}~~}||}~}}}~{z{~|}}~~~}}~~~~~}}}~|{|~|{{~|{z|}~~}{{{{{{{{}~~}}}~~~~~}{{~}}vvx~|}~}}~~|z{}{}~~~~}{yxwqsuvuvxz|{xwvwyzyxy|}|zwuuw{}~}zxvvvwwvusssstwy{}}|{yy}~zxyzyy|~~~~~}~~~~~~~~~~~~}}|~}}}}~|||}~{z{~y{~~|~|z||||}~~}||~|{|~z{|{~}{{||||{{~~}}~~}}~~{{~~y|~~}|~{{}{}}||~}{|~|~~~~}|{zzvvvtssvy}|{yyyyywvw{}|zwuuxz}|{ywvvvwwvuttttuxz|~~}|zx|}ywyzxy|~~~~~~~~~~~~~~~~~}}}}~}}}}~~}|}}~~{z{~|}~~~~}}|||}~~}|~~|{|~z{|{~}||{{{{||}}~}}}}~}~{|~~~}uwxuqpuz|~{{|{{}~||~~~~~}}|||{zxursux~~~}|zyxttvz~|zxvvwz{yxwvvvvwxwvuuuvvyz}~|{x|}ywxyxy{~~~~~~~~}}}~}}|}~~}}}~~~{z{~~}||}~~|}~~~||}~~|}}~|{|~{{|{}|}|{zz{|}||~~}{|}|~~~~~~{{~~~zwywogejpz}y{{zz|~|{}~~}}|||||ywvxz~|ywssuy~~|{xwvxyzwvvvuvvvxxwvvwwxxy|~~}{zw||yvxyxy|~~~~~~~~}~}}~}}||}~~~}~~~{{{~~}||}~~}}~~}}|}~~~|{|{{|{}~|}|{{z{|}{{}~~~z|~~~}}}}|}{|~}~~y{zwmc`fmz~|yz{yz{{yy|~}{zyz|~}}~}{yutvz~|{zxxwxxuuvvvuuuxwwvwwxywx{|}|zyx|}ywxyxy{~~}}}~}~~~~~~||||}~}}~~~~~{z{~||}}~~~}~~~~|{|{||{}~{{{|{|{|{z{|~~~~~}y|~~~}}~||}{{}}~~~yyxtledjp|}yz{zz|zxx{~|zyxx|~~}|xxx|}{{zyxwwvvwwwvvtswwvvvwxywy{}}|{yy}~zxzzyy|~~~~}}}~~~~~~|{{{|||}~~~~~{{{~z{}~~}}}~~~|||}~~}|~|{|~||}{}~{{{||||{{{||}}}}|}~~~~}}z}~}|}~}~{|||}~~wwuqlhinsz}z{|zz||zy{~~}~~}|zyyx{~~}}}~|{|~|{{{zywvuxxyxwusrvvvuvwxyz{}}|z~{y{|zy|~~~}}}}~~~|{{{{|{|~~}~{z{~{|~~}{|~~}|}}{{|||||{~|{|{zz|||{|~{||{{{{||||}}}|{z||}}~}}||~}{{}}~{{{|}}}vxupljkorv|~z{|{{|~|z{}~}|}~}}}|{{zzz|}~}~~}{{|~~~~|{{{zywutyyzyxusqvvuuvwxy|~~{|z|}{y|~~~|}}~~~|{zz{{z|~~~~{z{~}}~~~~}|y}}|~~}||||}~~|{|~}xvw||}{|}z}|{zz{|}}}}}|{zy{{|}}||{}~|yz|~}{~{{{{}}ywtoljjkkn}|z|}|zyyz{}~~~~~~~~~}}}{|}~}~~}{yz|}}}}}~|{{|}|xvyyywtrrsuuvtsrtvw{~~}yz~zxyyxw}~~~~~~}}|||{{~~~}||~~~~}}}}~~}}}~|||||||||{{}|yz{|}}||{~{|}}~||{{zz{{|{~~zy{|{}||||{{{}~~||}}}||~~}~y}~~~}yxvrnjgggn|}zy{}}{yzz|}~~~~~~}}|{{}}}}|{z{}}~}}~|{z{|{xuyzzxusssvwwvttvxy|~}}~{z~~zwyyxx}~~~~~}}|||||~~~}}~~~~~}}}}~~~~~~|||||||||{{}~|z{|}}}|{}z{}|~||{{zz{{|z|}}|}{z}||||{{{|}~||}}}|}}}}~}y|~~}}zyyuogccdn|}{yy{}}|zz{|}~~~~~|zy|}|||{{|~}~~}~~|{zz|zxv{{{yvttuvwxvuuwy{}}||~~|z~zxyyxx}~~~~~~~~}}||||~~~~}}}}}~~~~~~}||||||||||{{}~}yz{|}}}|}y{||}~{|{{{{{{||z{|~|{{}||||{{{z}~||}}}|~}}~~|}~}}}zzzumd`bfq|{zz{{{{{||}}~~}~}|{}}|}}|||~~~}~~}{|||zx{||zwuuvuvwvtuwy{~~}}~|z~~zwyyxx~~}~~}}||}~~|||}}~~~}}}~}}|{|||||||||{{}|zyxyz}}~~~|yz|{}~{|{{{{{{|{xz{{{~}||||{{{y|~||}}}|}}}}~z{ytjb`eju~~|{zz}}}}~~~~~}~~~~|{|~~~~||}~~}}}|~}|{|}}zxuuvstutssvxz}}yz~zxxyxy~~}~~}}}~~}|||}}}~~{{{z{{|}|||||||||{{}|vtsvwz|~~|yz|{|~{{||||||{{{|}zz}}||||{{{y|~||}}}|~}}|}z{xrjcbfku|||~~~~~~~}~~~{y{~~}|}~~}|~}}}~}||}|{wuuvrsttstvxz~}xz~~zwyyxy~~~|~~}}~|||}}}}}{zyyy{|}|||||||||{{}ztqquwy|~}zz}{}~{{||||||{|}zz}||||{{{z}~||}}}|~}}||}||z{xsmhfffs~~~~}}~~~{z|~~~~~~}{}~{z{|{z{||zwttustuttuwz|}zz~zxyyxy~|~~~~}}}||||||||{|||||||||||||{{}{usttvy{~}z{}|~|{||}}||{~|z}||||{{{|}~||}}}|}~~}|||xw~|zzxtqmhdbp}~~~~}}}~~~~~|||}~|~}{}}yxyzzy{|{yvttutuvvuvy{~~}~|z~zwyyxz}|~~~~~}}}}||{{}}~~~}{{|||||||||{{}}wvxtux|~~{|~}~|{||}}||{~}||||{{{}~~||}}}||~}|}}|||~xsv~{syvtsmedfr}~~~}|~~~~~~~~||~~~}}}~~}{yxxw{{zywvutwvutuwz|z}~~~{z|zzzx{~~~~~~~}~}~}}}}}}}}~~}|{{{{{|||}}~~zzz|}yvuuwxz~}}||||{}|{|z}|yy{~~}||}~~}}}~}}~~{}~toqz|zzwtqkeeis~~~~}}~~~~~~~}|~~}~~}{zzzyxwvvuvuttuxz|z}~}}~|yz|zzzx{~~~~}~~~}}}}}}}}|}}~}|zy{{{||}}}zzz|}yvuuwxz~}}~~~}~~{|}}}|}~}}||}~~~~~}~|~~|}~}}rknw|x|{wsojeglt~~~~~~~~~~}|~~~|zxxxxwwwwttttvx{|{~}}}{xz|zzzx{~~~~~~}|}}}}}}}}||~~~|zy{{{{||||zzz|~xvuuwxz}}~}~}{y|{|||}~~}}|}~~~~~~~|{}|}~y~~tmox~uu{|wrnifiow~}}}~}}~~~}~~~~~~~}}~{wwwwwwwwwtttuvy{|}~~~|xz|zzzx{~~~}}~~~~~~~~|}}}}}}}}}}||{|{{{{{{{{{{zzz|}yvuvxx{}~~~~|~w||~}}}}}~}}}~}zy||}~~}x{~|xrt{zvw|{vrojgjqx|{z{|{{|~~~~~~~~~~}~~~~~~}~~{wvvvwwwwxttuvxy{|~}yz|zzzx{~~~~}~~~~~~~~~}}}}}}}}}}||||{||{{{{{{{{zzz|~xvuvxyz~~~~}y{~~}}}}~}}}~~yxz}|}~~{vy{{}zvx|~yy{}~yurplikqz}{{|||{}~~~~~~~}}|}~~~~~}~~~~{vvvvwwwwvwwxyz{{}}zz|zzzx{~~~}}~~~~~~~~~~~~~~}}}}}}}}}z|}~}}{z||||{{{{zzz|}yvuvxy{~~~|{{}~~~|~~~~}}~~~~~zwzz{|}~~{wttwxz{{vuvxyy|ywxyyxvtsrojlp{|z{|}||~~~~}~~~~~~~~~~~~}~~~~~}~~wwvvvvvvxyyzzz{zz}~~~}zz|zzzx{~~~~}}}}}}}}}~~~~~~~~~~~~|}}}}}}}}z{}}~}{z}}}||{{{zzz|}xvuvxy{~~}{z{}}~~~~~~}~~~~zxyyw}}~~|vsrutrqswwuxurrstsqttttttsrusstpllp{{yz|}}|}~}}~~~~~~~~~}~~~~~~~~~}}}wwwvvvuuzz{{{{zzx{}||}|yz|zzzx{~~~~~~~}}}}}}}}}~~~~~~~~~~~~}~z}}}}}}}}|||||||}~~}}|||{zzz|}yvuvyy{~~~~~~~~~~}~}}}~{xywu}}}~~~rnqtsomortutsrqqrrqpontrpnmnpqopqrsuuv~|zzz|}}~~~~~}}}}~~~}||}~~~~}}wsuwwwx|{yvy}~}|~|x||y{{z|}~~}||~~~~~~~~~~~~~~}}}}}}}}{|}}}}|{}||||{{{|{z{{wuvxy{~~~~~}~}}}|xwxxy||}ypnrwwuoprssrqptttttsrqrqnmlmnommnpqrstyz}~}}~}~~~~~~}}}}~~~~~~||~~~~~}}wstvvvw|xty~~{{zxz~{xyzx{}~~~~}|}~~~~~~~~~~~~~~||||||||{|}}}}|{}||||{{{|{z|~zvtwyy{~~~~~}}}xxxxy|zz|~}vtx{|zvwxxxwvuwvvuuttttsqpopqqlmnopqrsrsvy}}||}zz{{}~~}}}}~~~~~~~~}|~~~}xstuuuvztz{yyyz}~zwxywz}}~~~~~}~~~~~~~~~~~~~~~~||||||||{|}}}}|{}||||{{{|zz{|xvxzz|~~~~|}~}yxxwy{zw|||}~zwxyxwttuuuuuusrqpooppssrqpppptsrqpnmmnoqtx|}zyxxwwxyz|}~~}}}}}~~~~~~}}~~~~}ztsttst~z|~|zzz{{xyzx|~~~~~~~~~~~~~~~~~~~{{{{{{{{{|}}}}|{}||||{{{|{z||zy{{|}~~~}~yyxwx{|y{{~}|}~zwwwwvxxxxyz{{{zxvvvwxrrrqqpooqqpppooomnoquy}}zyxwzzz{}~~}|}}}~~~~}~~~}|uttsrs}|}~~|z}}{{|{~~~~~~~~~~~~~~~~~~~{{{{{{{{{|}}}}|{}||||{{{|zz{}{z{{|}}{yxwxz~~z{zz}}~~~~}zz{}zz{{{yxwrrqqppoonnnprwz}~~}|||{||}~~}||}}~~~~}~~~}~wttrrs|z||{~~{}}|}~~~~~~~~~~~~~}}}~~~~~~~||||||||{|}}}}|{}||||{{{|{z|~{yy{{|}}~{zxwwz{}yzxxyx{~~~~~zxxxxywsqprtv~|xtpmlonmmpswy~~~~~}}|z{{|}~|~~~~}||||~~~~~~~~~~}xutrrs~}{}}{|}}{||{}~~~~~~~~~~~~~}|}}~~~}}|~||||||||{|}}}}|{}||||{{{|zz{~{yxzz|}~~|zywwyx}xxwwxv{|{~~}||}~yusrr{xurqsvx}~~|z}|zyyonllmptv~~|{yx{{{|~}}~~~~}||||}~~~~}~~~~}yvtrrs}}~zz|||yz{y~}~~~~~~~~~~~~~}||}~~~}}||~}}}}}}}}{|}}}}|{}||||{{{|{z|}{xyz{}~~~~~~~~~~}|zxwwywxxwyzx}zy~{uqrvyuxyvrrx~~}tqstvy}~|yrnoqppq~}|||{|}}~~~~||}~~~}}|{{{|~~}}~}}{yyyz~}}|tqrsry~~}yz~|yzzw~~~~}~~~}}}}~~~~~{~}}}~~~}||{{||}{{{z{||}{{|||}}}}||~}yxxuwz}~~}~~~~}yyxvy~xyyvvy|}}|{|~{uqruxwyyuqry}tqstux|{vsnknu~~}}{{|}~zy|~~}|}}~~~}}||{{|~~~}~~|zxxyxxz}~}~vrrsrx}{|~|xz|yzzx~~~~}~~}}}}~~~|}}}}}~~{{{{{|}~{{{{{|}}{{|||}}}}|{}|xvvvxz}~~}~~~}|yzywx|xxywvy|}}}|}~zurrtuxyxtprz}tqsuuwz}xpls~{{{{}~{tty~~}||}~~~~}||{{|~}~}{xwuutxwxz}~~}xsssrx|z{~}yz|zzzx}~~~~~~~~~~~~~~}}}~}}||{|}~~~{z{{|}~|{{{{|}}|||||}}}|{z|}xvuwx{}~~~}~~~|~~|||zyzyxywvx|}~}}~~~||}~}|~~~~ytssssyyxtps|{tqsxwwz}~~~}xv}||{||~ypow}~||}}~~~}}||{}~}|zwutttuwwwz}~~~}ztrrrw~{||{}z{zx}~~~~~~}}}~~~~~}}}}~~||{{{||}~||{{{|}~||{{{|}~||||||||{zz|}ywwyz{}}~}}~~|~|zzwxwwx{}~}}~~{yyz{zy|}~~}~~wttttswyyurt}zrpsyxxy}|{|~~~~}~xoov}~|||}~~~}|||}}~~{yxwuuuwxwvvy}~~~}~{tqqrv~~{z~~|{}{{{y}~~~||}}~~~~~~}}|}}}|{z{{||~}~}|{{{|||||{|}}~||||||||{zz|{{{{{||}}}}~~}}}y~{wwxxx{}||}~~}|zxwxyyxz{~~||~{usuvusuxyxuv}xpnqxxwy|{{~~zsrw|}{|}}~~~}}||~}}~|zxwwwwxxyvuuy|~}}}tqqru~}yxz|{y{~{|{y|~~~|}}}~~~~~~~~~~}}|}~~~}}|{z{{|}~~}|{{z{}||||}~~}}}||||||{z|}|}|}|}}}}}~}}{v{|vvxxxz}{{|~~~|zwwxyxwz{}~}||~}xssvywtqv{{yx|}vnmpuuvy}|z~}xwz||{||}~~~}||~}}~}}~}{xwwxxxwwvutux|}}vrrtu}ywyzyw|~|||z|~~~}}}~~~~}~~~~}}|||}~~}~~~}|{{{|||~~~}|{{{{{}}|||}~}}}|||{{}|{}|{|~}}}}}}}~~||~{vz|uvxyxz}~{z||zxxyyyxz{}~~}{|}{vqrwzxuou|}{y||umlorsuy~{y~}~~}|{{|{{|}}~~~}}|~}}~}}}}zxwxyyxvtrttux|}}wssuu}|yz{zx||||z|~~~}~~~~~~}~~~~~}|||}}}~}}~~~}|{{|||~~}||{{||}}}|||}~}}}|||{{}||~{zz~~}}|}}}~~}~~|{}|w{}uuxyxz}~zz|}{xxyzzx{|}~~}{|ywuttuwysuwwvwz}}|zwtqontux{{yx||{{{~{yyz{}}~~~~}}~~~~~}}}}~~~~~|{zwyzywvvwuuvwz|~}~~~~}~~|{||x{}|{z{}|yww|~}~}~~~~~~~~~~~~~~~~~~~}}|||}}~~~~}}}|{{{{|}~~~~}}}|||~~~~~~~~}|||~~{zz}|{{zz{}|z{~~~~~}}~zzz|~wuvy{|~~{{~zwxzyy{y|~}{{{~yxvuuvxy{ytpot}~~|zwvtsz{}}~}y~|zyz{|}}~~~}}~~~~~}}}}~~~~~|{yywyzywvvwuvvxy|~~~~~~~~~~}y{}|zy{~|yww|~}~}~~~~~~~~~~~~~~~~}}~~}~}}|||}}~~~}}}}}|{{{{|}~~~~}}}|||~~~~~~~~~~}|||||{zz}|{{zz{}|z{~~~~~|{{{{||}}}wvvvwz|}}ywwyyz|~~{{~zwyyyy{z{}}xrqwzvxyzxyyxxyz|}}~~~~}~~~~}}}}~~~~~|{yxwwxyywvvvuuvwz|~~}}~~~~z{}{yx|~|ywv|~~~~~~~~~~~~~~}}}~}}~}}|||}~~~}}|||}}|{{{{||}~~~}}}|||}}}}}}}}|||}|{{z{zz}|{{zz{}|z{~~}|}~~|{z~}}||||{}yxxwwwwwvvwy{{yw|~}{{~zwxzxy{{{||~~}{vpnptvvttuwy{}}~~~~~~~~}}}}~~~~~|{xwvuxxxxwwuuuvvxy|~~}}~~z|}zxw||xvv{~~~~~~}}~~~~}||||~~}}||{|}}|{{{{||}~~~}}}|||}}}}}}}}{|}~~|{z{zz}|{{zz{}|z{~}~~}xuxxxyxxxxuwy|}|xv|~}z{~{wyyyxz{{zz{}}yvtuuuvvuuuoruutrstssttvvwwtttttsssrssrqppqqqqqqqqqpnllnprrpqtwz}~~~~~~~}}}}~~~~~~~}{xwvvuxxxwxvvuuuvwz|~~}}~~z|}zxw}|xvu{~~~~~~~}}|||~}}|}||{}}|{{{{||}~~~}}}|||}}}}}}}}|}~|{{zz}|{{zz{}|z{~~~~}uquvwxyxxxxxyz|{yw|~|z{~{xxzxxz{zyyz|~~{wurqqrsttsrqopqpnnpspppqrsssrrrqqqqqnoqponpqppoonmllqqpqppoonqtx{~~~~~}}}}~~~~~~~~}|zxvvvwxwwwwwutuvvxy|~~~~~~~z{}{yx~|xuuz~~~~~}}}}}~~}}}}||}}|{{{{|{|~~~~}}}|||}}}}}}}}~~~~}|{zz}|{{zz{}|z{~~wsvwwxxyxxxwvwxzzy|~~|z{~|xyyxwyzzyyz|~|yusrstnoqrrqonpppmklorpppqqqrrppppqqqqmprqnmoqtsssrrqqtuusrpppoqvz}~~~~}}}}~~~~~}}}|{ywvvxxxwvvxwusuuvwz|~~}y{}|zy~|wutz~~~}}~~~}}~~~~~}}}}}|{{{{|{|~~~~}}}|||||||||||~}||||}{zz}|{{zz{}|z{~ywwwwvwwwwywuuwxyy|~~{z{|yyywwyyyzz|}~~usolkmprpqsuusqpqrsqonpsuuuuuuuuppqqrrsspsusomorpppqqqqqtttsqqrsprw{~~~}}}}~~~~~}}}|zxvvwyzxwvvwwusuvvxy|~~~~|x{}|{z~|wutz~~~~||}~~}~~~~}~~~~~}}}|{{{{|{|~~~~}}}|||||||||||~}|zz{|}{zz}|{{zz{}|z{~~~zyxxwwwxxy|zwwwxxw|~}{y{~|yyywwyyyz{|}~~zwsonoqrurppsutrsrppqrrqrtspnopqqrstuuuuvurqrrrrqqqrrsstssssssrrrvz||||}~~~~~~~}~}}}}}}}}~~}{ywxxxxxxxxxyyxxwvuwvuuw{~~~~~}|z|}{{|{wuuy~~~~~~~~~~~}}}}}}}}}}}}}~~~~|{zzz}{{}~}}}||{{}}}}}}}}~~~~}}}}{zz}~{zz~|xxzz{{wwxy{||zxzyxwwxyy~~~~{{~{xyzyy{xwwwy{~~ywtrpppqsqnortrqqqpppppqqqqpnnpsuvwxxwvvtsqqrtuussssrrqqqrrrqqppruz}||}~~~~~~~}}}~}}}}}}}}|}}|{yxvwwwwwwwwxxxxwwvuwvuvwz~~~~~}|z||{{|{wvvy~~~~~~~~~~~~~~~~~}~~~~}|{zzz}|{}}}}}||||}}}}}}}}~~~~}}}}{zz}~{z{||yz|{z|xvwy{|}}|zzxwwxxy~~}{{~{wxxwwywwwxy|~xwusqppoqomnpqqonoqppopponooonrv}|{yxvtsrqpprsttsrqppqrsqrsrqoopquz|}|}~~~~~}~}}}}~}}}}}}}}{{zyxwwvwwwwwwwwwwxxwvvuwvvvxz}~~~~|z|}{{|{xvv{~~~~~~~~~~~}|{z{{}{{}}}}}}|||}}}}}}}}~~~~}}}}{zz}{{{z{y{}{y~}zwxzz{|}~{zyxwwxx~~}z{}zwwywwyxxyz{}zwsommnopnmnoqonnopqppopnnnooqty}{xutsssqpoooppprqommnopprsronnppuy||}}~~}~}}}}|||}~|||}|}||zzyxwwwwwwwwwwwwwwwxwwvuwwvwxz|~~~~~~~~|z||{{|{ywx{~~~}~}~~~}|{{{{}|{}}}}}}}}}}}}}}}}}~~~~}}}}{zz}|zzzzxy|zy~}zzz{zyz}{zyxwwxx~~}zz}~yvyzzz}}}}~~|xqljkmponnnpppnooppqqponopoqtxyzwtqqqqqqponoonmmnnoonnmnoqpnlmnpty|}}~~~~~~}}}|||{||~~{{{{|{{{zyxxwwxxwwwwwwwwwwxwxwvvxwxxz{|}~~~~~~}|z|}{{|}yxy|}}}}~~~~~~}{{{{|}{{}}}}}~}~~}}}}}}}}~~~~}}}}{zz}zyy{zvvxwx~{yyzzxy{{zyxwwxx~~|zz|}xwy|}}ztommnponnopqonoonpqrqpoppqtxyywtrqqpompooooponllnoopppmoppnmmnosx|}~~~~~}}|||{{{{|}}zzzzzzzzxxxxxxxxwwwwwwwwwxxxxxwwyyz{{}}}~~~~~~|~|z||{{|}zyz|~}|||~~~~~~~}}~~||{{||}|{}}}}~~~~~}}}}}}}}~~~~}}}}{zz}}ywwzxtsvuw}zwvz|{z|zyxwwxyy}~~|yy|}xvy|}}}{zwsponnoopponnnnnopppqporvyywrpoopqpnonnopponponmmmnonnooooooosx|}~~~~}}|||{{{z{{|}xxyxyxyxvwxyyxwvwwwwwwwwxxyyyyxxzz||}~~~~~~~~~~|}}|z|}{{|}{zz{~~~~|{{}~~~~}}}}|~}|{{{||}{{}}}~~~~}}}}}}}}~~~~}}}}{zz}|xvvwvssvtuz|ww|}{{yyxwwxyz~}~{yy|}yvxzz{}}~|yrmnnnopponmnnnmnoqronsxywuspmmnoomnmmnnonmnnmlllmmlmmnoonnnsx|}~~~}}}|||{{zzz{|}xxxxxxxxtuwxyxvuxxxxxxxxxxyyzyyx{|}~~~~~~~~}~~}|~{~|z|}{{|~{z{{||}~~}||~~~~|{{|}~}}}||||~}||{||}}{{}}}}~~}}}}}}}}~~~~}}}}{zz}{wuuttstwtswzx~zyyxwwxyz{~}~~{yy{~~yvwwvvxxy|}uroooooooonmmmlllkhkpw||yvssrqppoonnnmllkkppoonmmlmmmnnnnnkqy~}|~~~}|{{{zzyyxwwwwwwwxxxxwwxxxxwwvvvwwwwwwxyyxy|~~||~~}~~~~~~~~}}~~}}}|{}|{|{|}zxx}{|||}}~~~~~}|||||~}||||}||{{{{{z{}~~~~}}~~}|~}}}}|z|{{}|xvvwvvxxvx|{{{z{xxyyyz{|~~~}~|yy{~}xuvxwxz}~|~xtppppppppttssrrrrpquz|{vrssrrrqqqsssssrrrtttsrrqquuuttssspv|~~~|{zxxwwwwwwwwwwwwxxxxwwxxxxwwvwwwwwwxxyzzyz}~||~~~~~~~~}}~~}~}}|{~|{|{~|zy|~}~~~~~~~}|||}{{|~~}||||||{{{{{{{|}~~}~~}||}~~}}}}|{|{{}}xwvxwwxwvw{}z}z}|zxvwz|{y~~~}~|yy{~}xuxyyz|}{x{zsrrrrrrrrsssrrrqqsux|~|wszzzz{{{{zzzzz{{{}||{{{{zyyyxwvvvw{~~}{ywvuwwxwxxxxwwwwxxxxwwxxxxwwwwwwxxxxy{|{{{~~||~~~~~}|}~~}~|||~|{|{}{z{~~}~~~~~~}}}}zz{|~~~}||||}||{{{{{{|~~}~~~|zz|~}}}}|{|{{}~zxxyxxyxuwz~{yz{xvvy{zx}~~~}}|yy{~}xvx{z|~~{wzxpmmmmmmmmoooonnmmprw}}zyzz{{|}}{{zzyyyx{|{{{{{{{{{{{{{{z|~~|{}|{zywvuuyyxyxxwwwwwwxxxxwxxyxxwwwwwxxyyz{|}}|}~||}~~}|}~~}|~~~|||~||||~|zzz~~~~~~~~~~~~~}~}}}}}}~}||||||{{{{{|{|}}~~~~~~~~~~}}~~}||{{}~{yyywxyxvx|{y~{zyxvwxy}~~~}}|yy{~}yvxzzz}}y{}rknnnnnnnnpooonnnnmpv|~yyzz{||}~~}|{yyx{{{{{{||{{|}~~xz{{xwy{xwwwwvvvwwwvwvvvwwwwxxxxxxyyyxxwxxxyzz{{|}~~}~~||~~~}||}~}||{||~}|}|}~zyxz~~~~~~~~~~~~~~~~}||||}||{{{{{{|}~~~}}|~}}~~}||{{}~{zzwvwyyxz~~|zz}|zwvvxy}}~}|}~|yy{~~yvwyxy{~{|~ulilllllllloonnmmmmnqv|~zxxxyyzzzzzyxxwvvuuuvvwwwstttuuvvsvyywvvwvvvvvwwwuvvwwxxxwwwwxxxxxxxyyyxxxxyzz{{||~~}~~||~~}|{|}~}|}}~}|}|}||}zxw{~~~~~~~~~~~~~}||||||{{{{{{z{|~~~}~~~~~}}~~}|{{}~{zzvuvyyy{~~{|zx{~xwwyxv|}}}||~|yy{~}yvxzz{}}{||vmiknnnnnnnnmlllkkkjnpu{{wqppppooolmmnnoppmnnoppqqrrqonmlknsy{zwvvwwwvvvvvvvwwxxyywwwwxxxxxxyyyyxxxyyz{||}|}~~}~~||~~}|{|}}|{|~}}}|~|}~{yy}~~~~~~}~~~}||||}||{{{{{yz|}~}~~~~}|}~~}~~}|{{}}zyywvwyyy{~|yzyxy}zwxyxw|}}}||~|yy{~|xvy}~{z{zqhhnllllllllnmmmlllkkmsy~{xppponmmllmnpsuvwxxyzz{||{zxuromklry}|ywvyxxwvuttvvvvvvvvwwwwxxxxxxyyyyxxxyyz{|}}|}~~}~~||~~~}|{|}}|{z|}}}}~}|{z}}}}~~~~~~~~}}}~~~~}|||||||{{{{{yy{}}~~~zz{{|~~}~~|{{}|zyyywxzyxz~~~{xxxyyy}ywwxy|}}|||~|yy{~{ww{}xtqonkhnnnmmkkjlmonmllmmqsuz}upppoonnmlnrv{~|}~|~|upmhmuyyyxyxxyyxxwwvvwwwwwwxxxwwxxxxxxyyyyyzzzz{{{{~~~~~~~{|~~}}}}zwwy}z|zxz|}~}|{~}|||zyz{~~~~~~~~~~~}~~~~~~~~}}}}}~}~~~~}|{{{||||{{{{{{yz|||}~~~~~~~~~~~~}~|{{}|{}}{yyvtvwwz~}|x|}yvx}uw}||~xyz|}}wtw|xronnmkhjklkkkkijlmlkjjloqsx~ynnmmnoppmpsx}}|~}vplhmtyyyxyxxxxxxwwvwwwwwwxxxxwwxxxxxxxyyyyzzz{{{||~~~~}~~|}~~~}{{yvvx|{wx}|yyz|}~}|{~}||{~yy{{~~~~~~~~~~}}}~}~~~~~~||{{||||{{z{{{y{|||}~~~~|~~}}~}|{}}{xwyvuvwwz~|yzxuw}}}}~yzz{|}~~|vru|wpllmmlhijjiikmiikmnmkjklnov}~sqolllmnoqv{~~~||~~xpkhmuyzxyywwxxxwwvwwwwwwxxxxxwwxxxwwwxyzzzz{{{{|||~~~~~~~}}~~{yyxuuw{~yutwyxxzyz||}}||~~||{{}yz}|~~~~~~~~~~~~}~~}}~~~~~}}{{{||||{{{{{{z{}||}~~~~~|}}}||}~{xwzwvwxxz~|zxvv~zvy}wojijiiklkiggjmiiikmnljijjmrywuqmkkklptx~~}{}~}~zqjhntyyyxywwwxwwwvwwwxwxxxxxxwwxxxvwwxyz{{{{{|||}}~~~~}~~}~}yvwvutv||wxutuuuw{z{{||}|}~~}||{{z{}~}}~}~}~~~~~~~~~~~~~~}}}~~~~~~||{{||||{{z{{|{|}}|}~~~~~~~~~~~~}}}}}||~~{z{xxyzy{~|~{y{~xqkihgejkjihgikigggjjjiihhjnszzxtpnlllqtz|{|}~|qhhmuyzxyyvwwwxwwwwwwwxxxxxxxwwxxxwxxyz{||||||}}}}~~~~~~~~~}xtuvttv{~|xuwuuvutvz{{{{||}}}||||{{{ywvy~~~}~}~}~}~~}~~~~~~~~~~}~~~~~}~~~||}}~~~~~}}{{{|||{{z{{|||}~}}}~~~~~~~~~~~~~~}~}}~}{}{z|}{|~~{|}ytqomjggghiiihgjigghjjkhggjkmry|xrnjhptz~}~~~~qghntyyyxyvwwxxxwwwwwxxxxxxxxwwxxxyyz{{||}||}}}~~~~~~~}~~~~}wstuutvwz|}{yxwvttvustw|{z{{|}~{{{{z{zzxusv|~{{{{{{{{}|{}~}}~}~}~}}~}}}}~~~~~}}}}}~|||}}~~~~~||{{||{{{{{{||}~~~}}~~~~~~~}}}|}}~~{yww{z|zyyzwsokiggiihfjjjiiijkighjifjq}zupmosz~}}~~~rfhmuyzxxywwxxxxxxwwxxxxxxxxxwwxxx{{{||}}}}}}~~~~~~~~~~~~~~~}wrruuuvvwyyxwwxxustuttw|{zzz|~zzzzzzzzwvvvy}{{{{{{{{zyy{~~}}}}}}}}|||||}~~~~~~}|||}}||||}}~~~}}{{{||{{{z{{||~~~}}~~~~~{~}}~~~|ywvv{y{{|{wtngegiiggijjhghiighjgcelpty~~}nsz}{z{~~~rfhmuyyyxywwxxyyxxwxxxxxxyxxxwwxxx||}}}}}}}}}~~~~~~~~~~~~~~~wqruuuvxxxwustv{vstuuvy}|zzz|~yyyyzzzzwyyxvx{|{|{|{|xwwz}~}}}}}}}}|{{{{|}~~~~~}|{|||||||}}}}~~||{{||{{{{{{||~}}~~~~~~~~~~~~}}~}~}|{~}~{zzz{y~}{zz{|{zyxtnhegiggggggggcddefgghbgov}z|}~~~|{}~}}}~~|zxxzxxxxxxxxxxxxxxxxwwxxyzzz{{{{||}~~~~~}~~~~~|yvuuuuuwxxxxwvuuuuttuvvzzzyz{|}{{zyxwvvw}xvz~{{zyz{{{z~xv||z~~}}}~|{}~|{||||||||||||{|}~~}}~~}}||{{{|}|z{}~~~}}}}~~~}~}||||}~~~~~}~{|~}}~~~~~~~~~~|wojptwwsnkijjjjjjjjmnnoppqqrtwyzywv{}}|zyx{~~~~}zwwyxxxxxxxxxxxxxxxxwwxxyzzz{{{{|}}~~~~~}~~~~~~|yvuuvuuwwxxwwvuvvuttuuv{{{{|~~}|zywvvvz}|wux}~{xwvvxxwv{vu|~}~}}}}~~zz|}|{||}|}|}|}}}||}~~~}}~~}}||{{{|||z{}~~~}}~~~}}}|}~}zxy}~~~{{~~~}||||||||{|}~~xqkpv}}yusyyyyyyyyyzzz{{||~{yvtr~~|z}xwvx}~~~~zwvwxxxxxxxxwwwwxwwwwxxyyzz{|{|{|}~~~~~~~}~~~~~|zwuuvvuwwwxwwvuvvuuuuvv}}}}~~|zywwvxyxvuwy}zvuttuvutuqrz~}}|}~}zy{}{z{}}}}}}}}~~}~~~}}~~}}||{{{{|{{{}~~~}~~~}}|{{}~~{y{}~}~}zz|~}}~~~~||||||||~}||}~~|z|~~~|zzzz}yz{zxz|}~~zwvvxxwxwxxxwwwwwwwwxxxyzz{{|||||~~~~~~~~}~~~~~}zwuvvvvvwwxxwvvuuuuvwxy~~}~~|{zyxxwxxxxx|zvuttuutsnlnw}}}|||}}zy|}|{|~~}~}~~~~~}}~~}}||{{z{||z{~~}~~~~~}}}~|yyz{z{~}~~~~}}|}}~~}|~||~~}~~yy}|~}|~zwvwxxxxxxxxxwxwxwxwxxyyz{{{}|}|}~~~~~}~~~~~}zwvvvvvvwxxxxwwttuvxz{}~||{||~~}}{zyz{|{z~|zvuttttsrjhltz|}}||{|}~~{{}~}~~~~~~~~}}~~}}||{{y{|{{{~~~}|}}|}~{zzz{zz}~~}}}{|||}~~~~|}~~}}~{xyz||||||||~~~}}|||~zy~~||~{xxyxxxyxxxxxxxxxxxxxxyzz{{|}}}}}~}~~~~~}{xvvwwvwxyyzyyxvvwxy|}~~|{z{{{~}}}~~~}~|{vussttsqkjnuyz{||{{{}~~{{~~~}}~~}}||{{yz{{{|~}}~~~~}{{|||{|~~~~}|{{zz{{||}}~~~~|{{|~~|z}}zxx~~}|||}~~|z~~}||yz{yyyyyyyyyyyyyyyyxyyz{{||~}}}~~}~~~~~}{xvvwwwxyzz{{zzyyyz{|}~|{{{{|~~~~~}|xwvuvvtsppsy{z{|{{{{|}~}{z}~~}}~~}}||{{yz{{{|~~~}{}~~~~~}|{zzy|||}}~~~}{{}~}|}}}~~~}z}~}}{~~}|~|z{|yyyyyyyyyyyyyyyyyyyz{{||~~~~~~}~~~~~~{xvwwwwyyz{||{{}||{{|}}yzz{}~}}~~~}{zyxyxwvutw|}{||{{z{|}~|zy|~~}~~~}}~~}}||{{xz{{{|~}}||||||||~~}}~~~}}}~||~~~~~~||{||}}~~~|}}~~}~}}}~~}{xvwz~~~}}|~}|yz|}~~}}|{zyyyyyzzzzz{|}~}}}}~~}}}}~~|||}~|yvuvwxxxz}~~|||yzz{||}}~~~~~~~~~~{{{zzyyyuwy{|}}}}}}}|||}}}}~~~~}||||{{{{{{|||||~}}~~~}~~~}}}}}}}}z|~~|{|~~~~}{}}|{{{|}~}~~~~}~~}{zz{||||}}|zxwx{~~}}}}}{|}~~~}||zzyyyy{{{{|}~~~~}~~~~}}}~~~||}}~{yvuvwxxxz|~}{zzyyzz{||}}}}}}}}}~~~|||{{zzzz{}~}}}}|||}}}}~~}}||||{{{{{{|||}}~~~|{~}|}~~~}|~~|{zz{{~~}}}~}}}~||{{{|}~z{{{zywvy|~}~~~~}}|||~~~}~}|{zzyzz|||||}~~~~~~~~~~~~||}~~{ywvwxxxxz}}|zyyzzz{||}}~}~}~}~}~~}}}}|||||}}}}|||}}}}~~}}}||||{{{{{||}}}}}~~~|~~~~~}~~}}|}~~}|||~~}}~|{zz~}}}|}}}zz{{|}}~{{zyxvuty{}}{yyz{{z{zzzz~~~~~}}|}}}}{{zzzz{{{{{|}~~~~~||}~~~{ywwxxxxxz}~}|{{||}}~~~~~~}}~~~~~~~~}}}}|||}}}}~~~||}||||{{{{{{|}~~~~~~~~}}}~~~~~~~}z|||~~}|zyx{{{||||{vwwxyyzz}|zxutrrwy{zyvwwxxxxxxyx{|||}}}~||{{}~~}|{{z{{|{{{|}~~~~|}}~~~~~{yxxyyxwxz~~~}}}}}}}|||}}}}~~~~~}||}||||{{{{{||~~~}}}}~~~~~}~}}~}}xyzz|}~~~|xw{}}}~}zyxwxyz{|{{zwxyzzzyy|{yvutsswy{{ywwxwxxxxyyyz{{||}}}||{{|~~~||{{{{}}}}~~~~~|}~~~~~{zxyyzxwx{~~~~}|}}}}|||}}}}~~}||}||||{{{z{|}~~~~~~~~~~~~{yz~}~|||{zzyzz||~~}yww}~~~~}~~~}|zyxwvwy{{{zyz{|}}{zyyxwuuvwxy{}}|{{|zz{{||}}{{|}}~||||}~~}||{||~~}~~~~~~~~~~~|}~~~~}{zyyzzywy|~~~~~~}}}}}|||}}}}~~}|}||||{{{z{|}~~}}~|~{||zz{}}}|}||||zxx}~~~~~}}}}~}}~}|{zyxxuvxz{zyxyz{{zywuvuuuvx{|{}~}}}~||}~}|||}~~}|||||}}|}}~~~~~~~~}}~~~|~~~~}}{zyyzzywz}~~~}}}}|||}}}}~~~}|}||||{{{z{|}~~|~}z{}}}}|{zyxyww{~~~}}~~~}}}}||zz{|}}~~ysrw|tvy{}|{zzzyyxxwwttuvwxxyz{|}}}}}}}|||}~~xyz{}|}}{z|~~~~~}||}~~~~~~~~~~~~}~|}~}ywvvvy|~~~~~}}}||~}||}}~~~~~~~}}}}~}|{zz{{|}~~~||}~}|yw{~{{|~}}|{{vx{~~~~~~~~~~}}}}}}}}|||{{{zz|zxy~}~~~~~~~|wssvytuxz{{zzzzyyxxwwtuuvwxyyxyz||}||~}}|||||{|||}}~~|}}{z{~~}}~~}}}}}~~~~~~~~~~~}~}}~|yvvvvx|~}}}||~}||}}~~~~~~}}}}}}|{z{{{|}~|{|~|{xvx}|z{|||{{zyyy~~~~~~~~~~~~~~}}}}}}}}|||{{{zz|zxz~~~~~~~|yvttuvstvxyyyxzyyyxwwwtuuvwxyyyy{{||||}}||{{{z~~~}}||}}}{zz~~~~}}}|}}}}}}}}~~~~~~~~~~|}~~~{xvuvux{~~~~~~}}}||~}||}}~~~~~~}}}}}||{{{{||}~~~~~~~~~}ywyz}{zwtuy{z|}}}}}||{{~}}~~~}}}}|}}}}||||}}}}~}}}}}}}}}}}|||{{{zz|zyz~~~~}~~~|xwvwutsstuvvvvxxxwwvvvtuuvwxyyz{{|{{zzyzz|||||~~~~~~~~}{yz|~~}}|||||}}~~~}}~~~~~~~~~{|~~~~~ywuuuuwz~~~~~~~}}}||~}||}}~~~~~~}}}}||{{z{{||}~~~~~~~~~~~~{|{ywx|}{{yutv}~~}~~~||{{~~}}}}||}}}}}}}}}}}}}}}}}}}}}}}}|||{{{zz{zy|~}||}}}}}~}zyzyvsrrrrssttvvvuuttssttuvwxxzzzyywvuuvy{~~||}~zxx{~~~~}}|||||~~}|}}~}~~~~~~~~~{|}~~~xvuuvuwz~~~}}}||~}||}}~~~~~~}}}}|{{z{{|||}~~~~~~~~~~}~}{|}|}{wuv~~~~~~~~~~~~}~}}|||||||}}}}}}}}}}}|}|}}}}}}}}}}|||{{{zz{zz}~}~}||~}|||}}{{{zvsrqpooprssssrrqqqrrstuuvwwwwwutrrrux|yz|}{wwy|~~}}||{|~~|{|||}}~}~~~~}~~~~~|||}~}|xutvvvwz}~~~}}}||~}||}}~~~~~~}}}}|{{{{||}|}~~~~~~}~~}|~~zwv}}~~~~}|~~~~}}}}|}|}||||}}}}}}}}}}}}}}}}}}}}|||{{{zz{z{}}~~}}}|{|}|||{zxurrpommnpqqppoonnnppqrstuuuuvvvutssuy}yz|~zwvx{~~~~~~~~~~~}z|~|z{{||||}}~~}~~~~~~~~}|}~~|zwuuvwvxz~~~~~~~~}}}||~}||}}~~~~~~}}}}{{{z{|}}}}~~~~}}}~~~~~~}}}}~{|{wv~~||}~~~}}~~}}}}~~~~~~~~~~~~~~||||}}}}}}}}}}}}}}}}}}}}|||{{{zzzz|}|~~|{|~z{{{yvsqqpnllmopoonnmmlloopqrsstuuvxxxxwtw{z{}~zvvxz~~~~~~~~~~~z||z{{{{||||~~~~}~~~~~~~}|}~~{ywuuwwwx{~~~~~~~~~~~~~~~~~~~~~}}}||~}||}}~~~~~~}}}}{{zz{|}~}}~~~~~}||}~~~}~~}|uy~}}~|zz~{wu}}{{|}}~~}~|{{|~~}}~}}|||}}}}}|}}}}}}}}}}}}~~~}}}}}}}}}}}}}}}}|||{{{zzzz|}{}}{|~|zwwvtpmlmpqqoljkoollnolllmnopqquuttssrrrtw{}~~~yyz|~}}|wtx}~}||}}~~}}|~~}}||}}}}}}}}~~~~~~~~}}}}~|||{zxwvvvvuvwxx~~~~~~~~~~~~~}}}}~~~}}}~~~~~~~~~}|{{zzzzz{}~|}~}}~~~~~|z}~}~~}~~|ww{{wvz|~}~||||||||yz{{z{}~|~}}~~~}}~}}}}}|||}}}}}~~~~~~~~~~~~}}}}~}}|||{{{xz~~zxwwyyvtqrtutrommopmmoomoopqrsttttssrrqqqsvyz{zzxwwy{|zy}}|{wux}~}||}}~~~}}~~~}}}|}}}}}}}}}}}}}}}}}}}}~|||{zxwvvvvvvwwx~~~~~~~~~~~~~~}}}}~~~}}}~~~~~~~~~}||{z{zzz{}~|}~}}~~~~~|{|}~}urwzywxz}||||||||y{{{z{}~~}~~}}}~~~~~~}}}}}}}}~~~~~~~~~~~~~}}}}~~}}||{{{{x{~~~|yxyxuswxyyxvsqnoonnoonppqrstuurrstuvwwrsvwyyxw|{{|~~|z|{{zxux}~~}}}~~~}}}~~~~}}}}}}}}}}}}||||||||}}}}~}||zyxwvvvvuvvxx|}~~~~~~~~~~~~~~}}}}~~~}}~~~~~~~~~~~~~}}|{{{{{z|}|}~}}~~~~~}~}}~~vqsyzxw~}zxy~~||||||||z{|{{{~~~~~}}}}~~~~~}}}}}}}}}~~~~~~~~~~~~}}}}~~}}|||{{{y{}}~|yvuvwut{{|{zwutnnmmnonmnooqqsstqqrrrrssstvwxyyy{zz|~~}{zyy{ywy}~~}}}~~~~}}~~~~}}}}~~~~~~~~}}}}}}}}}}}}~~}{zxwwwvvuvvwwx|}~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~}}||{{{{{{|~|}~}}~~~~~~~yxyzyy~zwwuv~~||||||||z{||{|~~~~}}}~}||}}}}}}|||}}}}}}}}}}}}}}}}~}}}}}}~}}||{{{{y{~}~|}~~nllnswzz{{{zyxvunlkmnnmlnnooqqrsrrqqqpppqrtvwyyzxxx{~~|zxyz{y{~~~~~~}}~~~~}}}}}~~~~~~~~~~~~}}}}~~|yxvwwvvvuvvxx|}~~~~~~~~}}}}~~~~~~~~~~~~~~}}}}||||||{}~|}~}}~~~|z|~zwwtrx}||||||||z{}|||~~~}}}~}||}}}}}}|||}}}}}}}}}}}}}}}}}}}}}}}}}|}||{{{y|}}~~|}~~~}}}}}}mjhjnsuvzzyyxxxxplkmoomloopqrsttsrrrrrqqpqrtuwxyvvwy}~}|yxx||||~}~~}~~~~}}}}~~~~~~~~~~~~}}}}~|ywvvwvvuvvwwx}~~~~~}}}}~~~~~~~~~~}}}}|}|}||||}|}~}}~~~|yzwtw~}|||||||||{|}}|}}~~~~~~}}}}~~~~~}}}~~}}}}}}}}}}}}}}|}}}}}}|}}||{{{{z|}|~}}~}|{{zzz|~~sojikmooyyxxyzz{rmkorpnnppqrstuuutsrponmrrstuvwxsssuxyxvywx|~}~}}}~~~~~|}}}~~~~~~~~~~~}}}}~|xvvvwvvvuvwxx}~~~~~}}}}~~~~~~~~~}}}}}}}}}|||}|}~}}~~~}{||{z{~}|||||||||{|}}|}~|}}~~}~~}}}~~~~~~}}}~~~}}}}}}}}}}}}}||}~~}||}}|||{{{z}~}}~~}}|yyzz|~~rnihknpqyxxyz{}~uomqtrppppqrsttuuuttsssrvvuvvwwxyxxy{{ywywx}}}}~~~~||}}~~~~~~~~~~}}}}~|xvuvwvvvvvwxx|~~~~~~~~~}}}}~~~~~~~~|||}}}}}}}|}}|}~}}~~~~}|}~{~||||||||||{|}}|}}~z{|}~}}~~~}}~}}}}}|||~~~}}}}}}}}}}}}}||}~~}||}}||{{{{z}~}~zzzz{|~~wrljmrvx}}}|{zzzutsrrstuqqrsuuvwxwuttuvwvvwxyz{{{{{||}}}xyz|~~~~~~~~}~~}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}||{|}~}xuux{{wuvxxxy~~~~~~}~~}~~~}||||}~~~}}}~~~~~}|||||||}}~~~~~{zzz|}|{~|zy||||||||||||~~|||}~~}~}~}~}}}}}}}}}||}}}~~~}}}}}}}}||||||||{{{zyyyxw|||}~~~}|{zyx~yutw{}~}||{{zzyxxxxwwvvwwxyyzz{yxwvvvxyzzzz{{{{yzzz{{{|yyz|}~~~~~~~~}~~}~~~~~}}}}~~~~~~~~}}}}}}}}}||}}}yuux{{wuvwwwx}~~~~~~~|||||}~~~~~~~~~~~~~~~}|||||||}~~~~~~~|x|{zzzzyy~|{z|||||||||||}}~|||}~}}~}~}~}~}}}}}}}}}}}|||||}~}~}~}~}}}}}}}}||{{zzyyx}}|~~}|||~{xx{}~}~}}}|||{}~}{zyyyyyyyyyyxxxxyz|{{{{zzzyyyzz{{{||||}}~~~~~~~~~~}~~~~~~~}}}~~~~~~~~~}}}~}yvvx{{xvvwvvx}~~~~~~~}}|||||}~~~~}}~~~~~~~~}}||{||||}}~~~~~~~}}~{w}|{{{z~|{{||||||||||||~~||}~~|~}~}~}~}}}}}}}}}~~~}}||}}}}}}}}~~~~~~~~|||||{{{z~~}|~}{yz|}|z~~~~~~}}~|xxxxwwwwyyyyyzz{zzzzzzzz{{{||}}~~~~~~~~}}~~~~~~}}~~~~~~~~~~~~zwwx{{xvvvuvx}~~}}~~~~~~}|||||}~~}}~}}~~~~~~}}||{||||||~~~}~}}~}~~~}~}xx{}zw|}~}{{{|x}}}~}|{|||||||||||}}||}~~{}~}~}~}~}}}}}}}}~~~~~}}}|}|}|}||~~~~~~~~}}|}|}||{~~~~~|{{}~}{zzzzzzzzz||zzyzyyxxxyyz{{|||{{|}}~~~~~~}~~~~~~~~~~~~~~~~~~|xwyzzwvvvtvx}~~~~~~~~~}|}~~~~~||{|||}~}~~~~~~~~~}||{|{||||}}~}}~}}}~}~~|xuuxyxvxz|{yxz}}~~~}}||{||||||||||||~|}}~~{~}~}~}~}}}}}}}}}|||}}}}~}}}}}}}}~~~~~~~~}}}}}}}}}~~~}|}~|xutvxwvsssttuuutuwzzzyxzzzzzzzz{|}~~~}}}~~~~~~~~~~~~~~~~~}yxyzxvvvutvy}~~~~~~~~~~~~~~~~~}|{||||}~~~~~~}}||{{{||||||~~}|}~~|}}}~}~~~}}~~~~~~}~~~}}}|||{{{{{{zz|||||||||||}}~}~}}~}}~}~}~}~}}}}}}}}~~~~~~~~~~~|||||||||||}}}}~~~~~}~~~~yuomoqssqrrssttustuwy{|}||}}~~~~~}~~~~~~~~~~~~~~}}}~}zxyzvuuvutw{}~~~~~~~}}{{{|||}~~~~~~~}}}||{{{{||||}}}||~~|||}}~~~~~~~~}}}||{{{zzz{{{zzyyyyxyyyyyy||||||||||||~}~~}}~~}~}~}~}}}}}}}}}}}}}}}}}{{{{{{{{{{||}}}}~}~}}~~xqnpsvwtuuvwwxxvwwy|~~~}~~~}}}}}}}}~}||}}~~{yyzutuvutw|~~~~~~}}~~~~~~~~~~~}|{{{|}|}~~}~~}~~~~~}}}||{{{{||||}~~~}{|~}|||}}}~~~~}|vvz~~}}||{{{{zzzyyxxxwwwvvzzyyxxwwwwwxxxxy||||||||||||~~{~}~}|}~}~}~}~}}}}}}}}||{{zzzzzzzzzzzzzzzzzzzzz{{||}}}~}}}~}{yy{||{{wuwzwy{|~}}~~~~~~~~}~}}~~}||||}}~~~~~~}~{yyzxwvuuwz}~~~}}~~}|{{|||{}}~~~~~~~~~~}||{{{{{{{{{{{{|}~~||~~|||}~~~~~}~{ywvvyy{~zv{zyxxy{|yyyyyyyyyyyyyyyy{{{zzzyyyyyxwvvv{||}}||{{|zy|~~~~~~~~~~}zzzyzz{{yyyyzyxwxyyzzzyyyyzzz{{{z{|}}}||~~|{{}}}yvwxxz{||~~~~~~~~~~}}~~}~}}}}}~~~}~~~|z{|yxvuux|~~}|z{|}||}}}~~~~}||{{{{{{{{{{}}~~||~~||}~~{{}}z|~|ywvutwwx{}~}||{zyxyz{||||||||zzzzzzzzxwwwvvuuuuuvvwww{||}|||{{|{z}}||||{{{~}zyyyyzz{yxxyyzyxyyyzzzzz{{{{{{{|{{|}}}}|~yz{}|xvuyz{{{z|}~~~~~~~}}}~~}~~~~~~}}}~~~}~|}yxuuvz~~|{zz|}}}||}}~~~~}}|{{{{{{{{{{~~~||~~}}~~zy{}|y{}|{ywutswwwwxy||{zxxxxyxxxxxxxxwvwvwvwwvvvuutttuuvwwxxy{|||}||{|}|{~~}|yyyxyyzzyxxxzzzyyyyzz{{{||||||||{|}~~~}}~xwx{~~zurwyzzyyyz~~~~~~~~~}}~~~}}}~~~~zxuux}~~|{z{|}}}{||}~~~~~~~}||{{{{{{{{{{}~~~||}~}~~|z{~~{|||{zxvtsxyxwuux{zzyxwwvvttttttttvvvvvvvvyyyxxwwwzzzzyyyy{||}|||{}~}{~}~~}}{zyxxxxyyzzyxyz{zzyyzz{{||}}||{{{{||}~~~~~~~|ywx{|uqtvxyyxyyz~~~~~~~~~~~~~~~~~~}~zxuvy~~~~|{zz|}}}{||}~~~~~~}}|{{{{{{{{{{{||}~~||~~}~~}yy}~||~yzzywusruwxxvuwxzzzzzxxwyyyyyyyy{{{{{{{{{{zzzyyyzzzzyyyy{|||}||{}~}{~||}~~~~~||{xxxxxxyy|{zy{{{{zzzzz{|}|{{{{zzz||}~~~~~~~}~~~~~~~~~zy|}vrrtwyxxxyxz}~~~~~~~}~~~~zwuw{}||~~}|z{{|||||}}~~~~~~}||{{{{{{{{{{{{|}~~||~~}}~~{wwz|{z{wwxxwtrppsvxxxxx{||}}}{{||{|{|{|z{z{z{zzyyyxxwwwstuvxyzz{||}|||{|}|{~~~}|xxxxxyyz}|z{{}}}{{zzz{||zzzzzzzz{|}~~~}}~|{}}xsqtwyyxxxuuwz~~~~~~~~~~~~}~}ywvx}}|{{}~~~~}{{{|{z}}}~~~~~~~}}|{{{{{{{{{{||}~~||~~||}~~~}~~|xv{}|{{yz{{zxusrstwyyxwwxz|}||{yyyyyyyywwwwwwwwxxxwwvvvrstuwxyz{|||}||{{|{z}}~}|{yxxxxyyz|{z{}|{zyyz{{zzzzzzzz{{|}}}}|~}{{}~}~~}yuruxyxwwwrrty~}}~~~~~~}~}~~~~}}ywvy~}{zz{}~~~|{{{zz}}~~~~~~}||{{{{{{{{{{~~~||~~|||}~~~}|}~~~zy}}~}~}zxvuuvxyvtrtvyzzyyyyyyyyyyxxxxxxxxyyxxwwwvwwwwxxxx{||}}||{{|zy|}~|{yxyyxxyyzz{zz{~}|zyyyz{yzzzz{{{z{|}}}||~~{z{|~}{||{zyxwutuvy}}~~~~~}}}}~~~~~~}{{zvw~}zyz~~~~}}}|||||z{|}~}~~~}}}}}}}}{{{{{{{{|}~~{z|~~~~}||~~|~~~~~~~~{ywwy{||{zyxxyyy{zzzzyyyxvtuwyyxwxyyyyxwwwwwwwwwwwwwwwwwwvvvwxyy{}~|{{ywwuttvz~~~~}~~~~}|yyxxwwwxz{{{}zzzzyy{{{zzzzzz{||}}|||{~~~~{zyz{}}}~~~~|{zyvvvwz}}~~~~~~~~~~}||{wx|zz{~~~}}}}||||z{|}~~~~~~}}}}}}}}{{{{{{{{|}~~{{|~~~}|}~}|~~~~~~~~}{xxy{||zyxxwxxxzzzzzzzzyxvwyzzyvwwxxxwvwwwwwwwwwwwwwwwwvvvvwxyy{}~|{{ywwvttvz~~~}~}zyxwwxyyz{{|~|{yxyyyx{{{{{{{{z{{}}}||~||~}~}~|yxz{}}}|{xxxxz|~~~~~~~~~~~}}}}}||{xy|z{}~~~~}}||{z{|}~~~~}}}}}}}}||||||||||~}{{}~~}~}}}~~~}~|yxzz{{zzyxwwxxyyzz{{||{yxxz{{zwwxxxwwwwwwwwwwwwwwwwwwwwvvvwxyy{}~|{{ywwuttvz~~|{z|zyxxy{|y{{|~~{zyyyzyyzz{{{{||z{||}||||}|~|~~~~~~~~~~~{yz}~~}}||}~~~~}|{zzz{|~~~~~~~~~}}}}||}}|||zz~|{}~~||{{z{|}~~~}~~}}}}}}}}|||||||||}~}{|}~||}|~~~|yxxyyy{{zxxxxxxxyz{|}~{zyyyz{{xxxyxyxxwwwwwwwwwwwwwwwvvvvwwxyy{}~|{{ywwvttvz~~~~}{yxzywvvxz{yz{|}~{{zzz{{{{yzzzz{{{z{{}|}|||~}}~}}}}}}}}}~~~~~~~}}{xz|~}}{zyxxzz||~}}}|||{||}}~~~~~~~~~}}}}}||||~}}|{{}~~~~~||~~}||{z{|}~~~~~}}}}}}}}||||||||||~~}|}~{{{}}}zxvwxyx{zywwvvwwxxz{|}~zzzyxyzzxxxxxxxxwwwwwwwwwwwwvvvvwvvvwxyy{}~|{{ywwuttvz~~~~|{zyxwwxz{yz{{}~}zyyzzzzzzyyyyzzzzz{||}||||~}~~~}}}}}}}}~~~~~~}}}~yxy|~~}|{zyyz{|}~~~~}}}}}}}}~~~~~~}~}}|}|||~~}~}|}}~~~|}~~}}|{{z{|}~~~~~}}}}}}}}|||||||||}~}}|~}{z|}~~|zwwwyyyzywvuuuuwwxyz{|}z{{zxxy{xxwwwwwxwwwwwwwwwwwwvvvvvvvwwxyy{}~|{{ywwvttvz~~}|zzyyxxyyyzzz{}|zwxyyyxxyzzyzyzyzz{{}}}||{~~~~}}~~~~~~~~~~~~~~~}zy{}}}}~~}}}~~~~~~~~~}}}}|{~~~~~~}}~~~|{{z{|}~~}}}}}}}}}}}}}}}}}}||~}|}~}z|}~~|zxwy{||{{yxwvvvvwwxyz{{||}{yyz|xwwvwwxxwwwwwwwwwwwwvvuuwvvvwxyy{}~|{{ywwuttvz~~}|zz{zzxwvzzzyz}}{z|}}|{{{|{{{zzzzz{||}|||z}}~~}~~~}{{}}}}~~~~}~~~~~~~~~~}}}}{~~~~~~~}}}||{{|||}}}}}|{|~~|{{z{|}~~}|}}}}}}}}}}}}}}}}|}~}||~}z}~}~~}{yy{}~~~|{zyyywwwxxyyz}~~}zz{}yyxwwxyywwwwwwwwwwwvvvuuvvvvwxyy{}~|{{ywwuttvz~~}|zzyxz}~}}}||{{zzz{||}}||x{}~~~~~~}}|}}~~~~~~~~~~}}~~~~}~~~~~~~~~xxy|~}zwxyz||}}}~|{{}}}}}}}|||}~~~}||}}}~~~~}}}||||||||{|||}}}~|{~~}~~~~~~~~~~~~~}}}}||}|}||||||||||||||}|{{zzxxxyyxwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvutstvy}}{z{~~}}||{z{{||{zz{{|~~~~}|}}~~}~}~~~~~}}}}~~~~~~~~~~~~~~}~~~~~~~{zz|}}zxyz{|}}||}~~~{zyz}~~~~}}}~~~~~~~}||||||||{|||}}}~|{~~}~~~~~~~~~~~~~}}}}|||||||||||||||||||||{zzyyxxxyxwxxxwwwwwwvvvwwwvvvvvvwxxz||||}zvutstvy}~}}~~}|{{{}~~~}|||}}~~~~~~~~~~~~~~}}}}~~~~~~~}~~~~~~~~~~}||}}{z{||}}}||z{{{zz|~~~~~}}}~~~~~~~||}|}|}|{{||}}}}|{~~}~~~~~~~~~~~~}}}|{{{{{{{{||||||||{||||{zzyxxxxxxwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvutttvz}}|zzz{|~~}}|{{{|}~~~~~}}}}~~~}~~~~~~~~}}}||}}~~~}||yz{||~~~~~~}}}}}}}}{||}|}}~|{~~}~~~~~~~~~}~~}}||z{z{z{z{||||||||{{|||{zyxxxxxxwwxxxwwwwwwvvvwwvvvvvvvwxxz||||}zvuuttvz~~{}{zyyyyz|}~~}}{{z{{|~~~}}}}}}}~~}}~~~~~}}}~~~~~}|}~}~~~~~~~~}}}}}}}}{{||}}}}|{~~}~}~~}||{{z{z{z{z||||||||{{{|{zyyxxxxxwwwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvvutuwz~~~|z{|}~}{zyyyyz{}~~~}|{{z{{|}~~}}}}}}}}~~~~~~~|}~~~~~||}~}~~~~~~~~~~~}~}~}~{||||}}~|{~~}~~~~}|{{{{{{{{{{||||||||z{{{{zyywxxxwwwwxxxwwwwwwvvvwwwvvvvvvwxxz||||}zvvuuuw{~~||}~~~~~{|}}|yyyz{}~~}|{{{{{|}~~~~}}}}}}}~~~~~~~~~~|}~~~~}~~~~}{{{}~~~}|}}}}~~~~~~~~~~~~~~{|||}}}}|{~~}~~~~}{{z||||||||||||||||zz{{{zyxwxyxwvwwxxxwwwwwwvvvwwwvvvvvwwxxz|}|}}zvvvuuw{~~~~}|z{{{||}}~~}|yyz{}~~||{{|z{}~~~~~~~}}}}}}}~~~~~~}}~~~~|}~~~{~~}~|~~}}}}}}}}||~~~|{z||}}}~~~~~~~~~~~~~{|||}}}~|{~~}~~}}~}|zz|||}|}||||||||||zz{{{zyxwxxxwvwxxxxwwwwwwvvvwwwvvvvvvwxxz|}|}}zvwvuux{~~}}~|zxxz{{||{zzyyz|~~~}||||yz|~~~}~~~}}}~}{{z{{{}~~~~~~~~~}}~~~~~~~~~~~~}}~~~~~~}|||~~~|||~~}~~~|zy{|}~}~~~~~}|}}}}~~~~~~~}}}~~~~~~}}|}}}}}}}}}}{{|{|{|{yyxxxxxxyxxwwwwwxxwwwwwwvwwxxwvuwvuvwwxzz}~}}|zxuttvy}}~~~~}|{zzz{~||~~~}|{~}||}~~~~}~~~~~}|{{{|}~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~}|}}|}|}}}}~~~|zz{|}~~~~~}}}}}}~~~~~~}}}~~~~~~~}|||}|}|}|}|}{{{{{{{{yyxxxxxxxxwwwwwwxxwwvwwwvwwxwwvuwvuwwwxzz}~}}}{xvvuwz~~}|zzz{~}}~~}||||{{|~~~~~}}}~}}|||}}~~~~~~~~~~~~~~~~~~~~~~~~}{~}|{{{}}|}}~~|{z||~~~~}~~~}}~}}}}~~~}}}~~~~}}~~~~|||||||||||||{{{{{{{{yyxxxxxxyxxwwwwwxxwwwwwwwwwwwwvvwvvwwwwyz|~}}|{xxwwy|}|zzz{}~|~~}}}|{{{|}}}||}}}}}~~~~~}}~~~~~~~~~~}}~~~~~~~~}~~~|{~}||{||}{||}~~}{z|}}}|~~~~~}}}}}}~~~}}}~~~~}|||~~~~}|{{{{{{{{{{{{zzzzzzzzyyxxwxxxxxwwwwwxxxwwvwwxwwwwwwvvwvvwxwwxz|}}|}{yzyy{~|{zzzz}~}~~~~~~}}}|}|}}~~~}}}~~~~}|~~~~~~}}}~~~~~}~~~}|{{}}}{}}~~~}}|{{||~~}|{|}~~~}~~~~~~~~}~~~~~~}}}~~~~}||||}~~~}|{zz{{zz{z{zzzyyzyzyzyyyyxxxxxyxxwwwwwxwwvwwxxwwwwvvvvwvvxxwwx{|}||||z{{{}}{yzzzz|}~}~~~}}}}}}~~~~||~}}}~~~~~~~~~~}}}}~~~~~~~~|{z{|}|{|}~~}{||}}~|{}}~~~~~~~~~~~~~~~}}}~~~~}}~~|}}~}|{zzzzzzyzyzyzyyyyyyyyyyxxwxxxxxwwwwwwwwwwwwxxxwwvvvvwwvwxxwvw{|}||}|z|||~~~}zy{zzz|~~~}~~~}}|||}~~~}~~~~}}|}}~~~}}}~~~~~~~~~~~~}}}||}}}~~|{{}}|{{{|}~~||}}~~|{}}~~~~~~~~~}}}~~~~~~}|{zyyyzyyyyyyyyxxxxxxxxyyxxxxxxyxxwwwwwwwwvwwxxxwwvvvwwwvwxxwvw{|}{|||{}|}~}|{{zyz{~~~~~|~}|{{z{{}}~|{z~~~~~}||}}}}~~~~~~}|}}}~~~~~}}||||}}~}}}}{z{zz{|}||}~~|{}~~~~~~~~}}}}~~~~~~~}}}~~~}|{zyyyyyyyyyyyyxxxxxxxxyyxxxxxxxxxwwwwwwwwwwwxxxxwvvvwwwvwyxwvw{||{{}|{}}}}~~~~}|~}{zyz{~~}~~{~}|{zzxy{|{zyx~~~~~~~~}~~}}}~~~}}~~}||||{{|}}}}|zz{}}||}~|{|}~~~}}}}~~~~}}}}|{zzyy||||}~~~~}}~~}~~~~~~}}~~~~|}~{{{zzzyyzzzyyyyyyyyyyyyywwxxxxyyxxxxxxxxxxxxwwwwwwwvvvuuwuvxyxxz{|}}|{{{vwy|~~||}}}}{ywx{}~~|~~~~~}}~}|{zz{{|}~~}}~~~~~~~~~}~~}}}~~~|}~~~~|zyyxxzz{||||{{{{}~|{{{|}}~}}|{~~}}{{{{~~}||{{{||||}~~~~~~~~}}}~~~~~~~}}~~~~~~~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxwwwxwxxxwwwwvvuuwuvxxxxzz|}}||||xxz~~~}~}|}{yxx{}~~|~~~~~}}~}||{{||}~~~~~~~~~}~~~}~~~~}}~~~~~~~~}{yxwxxyyz{|{{z|||}~|z{|}}}}}{{z~}}|||||~~}}||||||}~~~}}~~~~~~~~}}~~~~}|{~~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxwwwwxxxxwwwwvvvvwuvwxwxyz{}}}|}~{{}}~~}||{yxy{}}}|~~~~~}}~~}}|||~~~~~~}~~~}~~~}~~~~~~~~~~}{{z{yz{|||{{}||}~}}||}}}}|{{}~~~}}|||||}~~~~~~~~}}~~~~~~~||~~~~~~~~}|~{{{zzzyyzzzyyyyyyyyyyyyyyyxxxxwwxxxxxxxxwwwxwxxxwwwwvvvvwvuxxwwyy{|~}}~~~~}~~~~}~}}}~}||{zyz{}|||~~~~~}}~~~~~~}}~}~~~~~}~~~~~}~~~~~~~}|}}z{||}}}|}||}~~}z{|}~}~~~~~}||||||}~~~~~~}|~~~~~~~~~~~~~~~}|{~{{{zzzyyzzzyyyyyyyyyyyyyyyyxxwwwxxxxxxxxxxxwxwwwwwwwwwwvwuvwxvvxyz}}}|}~}}}}}|{|~~{{z||~~~}~|{{{zzz}}|z|~~~~~~~~}~~~~~}~~~~~~~}}||}~~~~~~||{|{||}}~}}}|{|~~~|}~}~~~}||||||}~~~~~~~~}}~~~~~~~~}|~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwvuwwvvwy{|}|||}~}|}~~|zz|~~~zxwy{}}~~~~|{{{zz{}}{y|~~~~}~~~~}}~~~~~~~~~~~~~~}}||}~~~~}~|{{{{z{||}||||{{|~~||~}~~}|||||}~~~~~~~~}}~~}~~~~}~~{{{zzzyyzzzyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxwxwwwwwwwwwwwwuvwwuuwy{||{z{{~~}}~}}~|{{}~~}||{}{yxxy{|~~~~|zz{zz|}}{x|~~~~}}~~~~~}~~}~~~~~~~~~~~~~~~~~~~}}|{{{yzz{|{{{{zz|~|}~~~~~~}||||}~~~~~~~~}}~~}}~~~~~~|~~~{{{zzzyyzzzyyyyyyyyyyyyywwwxxyyyxxxxxxxxwwwwxxxxwwwwwwwwwvuwwuuwy{||zzzz}}~~}|}~|{{}~}}|{{zzzzzzz{{|~}}~~|zz{z{|}}zx|~~~~}}~~~~}}~~~}~~~~~~~}~~~~~}}~}~|zyyyxxz{{zzyyyz|~~~~{||}~}|{{|}~~~}}~~~~~}~}}~~~~~|{zzyzz{yyyyyyyyxxxxyyyywxxxxxxxzyxwwwxxyyxwwwwxwwwwvuttvvvvwwwwz{||{{|}~|~}|}~~~~|zxxy{|}}}}}}|||~|{{{}~{yzzyz{~}}}||}}}~~~~~~~~~~~~~~~}~~~~~~|{zyywxyzzzyx{{|~~~~{||}~~|{{|}~~~}~~~~~~~~~~~}~~~~~}}~~~~~||zzyyyzyzyzyzyzyyyyyyyyyxxwwxxyyyxwwwxxxxwwwxxyvwwwvvuuvvvvvwwwz{||{{|}~~zz|~~~~~~~~}{z{}~~~~}}}}}}{{{{{||}|{|zzz{}~}}}}}~~~~~~~~~~}~}~~~}|{{{z{{|||||~~~~}~~~~~~~~}}|~||}}~|{{|}~~~~~~}}~~~~}~~}}}~~~~~}|{yyxxxzzzzzzzz{{{zyxxxzyxwwwyyyyxxwxxxwwwwxxyywwxwxwwvvvvvwwwwz{||{{|}~~|||~}||}~~|||}~~~~~~~}}}yz{{|||{}}}~{zzz|~~~~~~~~~~~~~}~~~}~~~~|}|}}~~~~~~~~}}}~}}}|}}}~}||}}~~~|{{|}~~~~~~~}~~~~~~~}{zxxxx{{{{{{{{}}|{zyxxzyxxwxxyxxxxxxxxwwwxwxxywxxyyyxxvvvwvwwwz{||{{|}~|{{}}||{|}}}{zz|}}}~~}}||}}}}}}}}}~{{zz{~~~~~~~~}}~~~~}}~~~|}~~~}~~~~~~~~~~~~~~~}}}||}~~~~{{|~~|yv}}~~~}|||~~~}~~}}}~~~~~~|zzxyx|||{|{||~}}{{yyxyyyyyxxxxxxxxxxxxxxxxwwwwwxxxxxxvvvvwwwwz{||{{|}~~}~~~}|{|}{{||}||{zyyz}~~~}}}~~~~|{{{{zz{~~~~~~~~~~~~}~~~~~~}}~~~|}~~~}~~~~~~~~~~~~~~~}{z{|}~}}}~~{xu}}~}||}~~}}~~~~}}~~~~~~~~}}~~~~~~}|zzzz|}|}|}||~~}|{{zzyzyzyyxwwwxxyxxxxxxyxxwvvvvwwwvvvvvwvwwwz{||{{|}~|z{|||zywvxyz{||}}z{|}}|{zzz{}~}}}~~~~}{zy{zzz|~~~~}}~~~~}}}~~~~~~~}}~~~~}~~~~~~~~|zz{}}~}|}~~~||}~~}}~~~~~~~~~}}~~~~~}}}~~~~~~}||||}}}}}}}}}}}}|}|||zzzyyxxxwwxyyyxxwxxxyxwwwwwwwvvuvvvvwwwwz{||{{|}|yxz{||{ywvwxz|}|{y||||||||||}~~}}|~}~~~|{zzzz{}~~~~||}}~~~}}}}~~~}}~~~~~}}~~~~~~~~~~}~|{||~~~~}~~~}}}~~}}~~~~~~~}}~~~~~~~}}}~~}}}}}}}}|||}}}}~{{yxwxxyvwxyyyxxvwxyyyxxyyyyxxwvvvvvwwwwz{||{{|}~{xvyz{}}|zyxxy{|~~~}}{{{||}}}~~}}||~~~~~}}|~}|{zyz{~~~~~~~~~~~}~}~~~~~~}~~~~~~~}~~~~|z{|~~~}}~~~~~~~~~~~}~~~~~}|}~~~~}}}}}~~~}}|}}|||}}}}|zzzzzzzz{||{yxxywwxxxyyywwwwwwwwwwxxxxwvxwwwvwwwxz||{zz{~~}zyz{{zyxxywxyz{|||}}}~~}}}~~~~}|{{{}~|zz~}|zzz{~~~~}~~~}}}~~~~~~~~~~~}{{|~~|zy~~~}}~~~~~~~~~~~~~}~~~~}|}~~~~}~~~|}}}}}||{{z{{{{{zz{zxwwxxxxxxyxyyxxxxwwwwxxxxxwwxwwwvwwwxz|}||||~~}zyyzzzyyzzyyz{|}}}}~~~~~~}}~~~~}|{||||}}|||{|~~~~~~~~~}|}}~~~~~~~~~~~~~~~~~~~|||~~~~~~|{~~~}~~~~~~~~~}}}~~~~~~}|}~~~~~~~~~~}}|{{{{{{{{yyzyxxxyyxyxxxxxzzyyxxwwxxxwwwwwwwwvwwwxyz|}}}}}}zzyz{{{{}~{||}~~~~}~~}|{~{{|~|{~}||}~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~}}~}}}}||}}}~~~~~~~~~~~~~~}}}~~~~}|}~~~~~}||{{{{z{zzyyzzzzzzyyyyxxwwyyyxxxwwyxwwvwwxwwwvwwxxyz{}}}||}}zz{|}~~}~}||~}{~~}{{}|{{}~}~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~}}~~}{{}}}||}}}~~}}|}}~~~~~~~~~~~~~~~~~}~~}|}~~~~~~~}~~}}|{z{zzyzyyyyyzzzzyyyxxxxwwwwwwwwwyxxvwwxxwwvvwwxxzz{||{zy{|{z}}}||}}~~||{{zz{}~}~~}~~}~~~~}}}}~~~~~}~~~~~~~~~~~~~~~~~}{{|}}}|}}}~}{zz{|~~~~~~~~~~~}~~~}}~}}}~~}|}~~~~~~~~~}}}~~~~~}}{{z{zzyyxxwxyyyxyyxyxxxxwwwwwwwwxxwwwwwxwvvvwwxx{{{|{{yxz}{{}~~~~}|{z}}|||zzzz|~~}}~~}~~~~~~}~~~~~~~~~~~}~~~~~~~~}||}}}|}}~~~}{{{|}~~~~~~~~~~~}}~~~~~~~}|}~~~~~~~~}}}~~}~~~~~}}|||{{zzzyxwxxyxwxxxxxxyyxxxxxwwwxxxxxwwwvvvvwwxy{{{|}|{yz|{{|}~~~~~}|{z}~}||}|{zz|~~~~~~~}}}~~~~~~}~~}~}}}}~~~~~~}~}~~~~~~}}}}|}}~||||{{zz~~~~~~~~~~}~~~~}|}~~}~~~~~~}}}~~~~}~}}}||{{zyxyyzywwwxxxyyyzyyyxxwwwwxxxxwvvvvvwwxy{{{|}~|{{|{{}~~~~}{z{~}}}}~}{{{}}}~~~}~}|}~~}~}}~~}}}~~~~~~~~~~~~~~~~~~}}}~~~}|{{{|~~~~~~~~~~~~~~~~~~~~||~~~~~~~~~~~~}}}}~~~~|||{{zzzzyxyyyyxyxyxyxyxxxxxxxxxwwwwwwwwwwwwwwxx{{|||{{{~}|{|~~}|{|}~}}||||{{}~~}~~}~~~}}~~~}}}}~~~~~~~~~~~~~~~~~~~~~~}}}~}|}}}~~~~~~~~~~~~~~}|~~}~~~~~~~~~~}}}}}|||{{zzzyxyyyyxxyxyxyxyxxxxxxxxwwwwwwwwwwwwwwxxz{||{|||}|{|~~}|{{||}|}}||||{{}~~~~~~}}}~~~}}}}|}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}||~~}}~~~}~~~~}}}}}|||{{{zzyxyyyyxyxyxyxyxyyxxxxwwwwwwwwwwwwwvwwxxyz||||}~~|{|~~~~{{{{||||{|~~}~}||||{{}}}~~~}}~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}|~~}}~~~~}~~}}||||{{zzzzzyxyyyyxxyxyxyxyyyyxxwwwwwwwwwwwwwwwwxxxyz{|{|~~}|}}~}~~~~z{{}}}}}|}~~~}|}~}|}||{{}~~~}~~~~~}~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~||~~~~~~~}~~~~~~}}||{{{zzyyyzyxyyyyxyxyxyxyxyyyxxwwwwwwwwwwwwwwvwwxxyz||{|~}|}~}~~~~||}~~~~~~}}~}|}||{{}~~~~~~}}~~~~~}~}~}}~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~}|~~~~~~~~~~~~~~~}}}}{{{{zzyyzyxyyyyxxyxyxyxyyyxxxxwwwwwwwwwwwwwwwwxxy{||z{~}|}~~~~~~}||||{{}~}}}~~}}}}~~~~~~~~~~~~~}}~~~~~~~~~~}~~~~~~~~~~~~~~~}~~~~~}|{{||}~~~~~~~~||}}~~~~~~~~~~~}}}}|||{{zzzzyxyyyyxyxyxyxyxxxxxxxxxwwwwwwwwwwwwwwxxz|}|z{~}|}~~~}||||{{}~~~}}|}~}~~~~~~~}~~~}}}~~~~~~~}}~~~~~~~~~~|}}~~~~~~|zyzz{|~~~~~~~~~~~~~~~~~~~||{|}~~~~~~}}}}~~~~}}}||{{{zyxyyyyxxyxyxyxyxxxxxxxxwwwwwwwwwwwwwwxx{|}|zz~~|~~~~}}}}}||||{{}~~}|||~~}}~~~~~~~~~~~}}~~||||||||~~~}}}}xxwxz|~~|}}~~~~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}||{{{yyyyyyyyyyyyyyyyxxxxxxxxvvvvvvvvxxxxxxxxz{{|}~~~~~~~~~~~~~~~~~~~|z||{|}}}~~~~~~~~~}}||}}~}}}}}}}}~~}}}|||{{z{|~~~}}~~~~||||||||~~~}}}}}}}}|||{{{zzyyyyyyyyyyyyyyyywxwxwxwxxxxxxxxxwwwwwwww{||}~~|z}}|}}~~~~~~~~~~~~~}~~~~}}}~~~~~~~~~~~ \ No newline at end of file diff --git a/venv/Lib/site-packages/pygame/examples/dropevent.py b/venv/Lib/site-packages/pygame/examples/dropevent.py index 060693c9a0fdf0bf0c33902581dd4bd59ecee8d5..d1596166c890498722f37c592ef1f08e41fe0854 100644 --- a/venv/Lib/site-packages/pygame/examples/dropevent.py +++ b/venv/Lib/site-packages/pygame/examples/dropevent.py @@ -1,28 +1,37 @@ -import pygame as pg +#!/usr/bin/env python +""" pygame.examples.dropfile + +Drag and drop an image on here. -if pg.get_sdl_version() < (2,0,0): - raise Exception("This example requires SDL2.") +Uses these events: + +* DROPBEGIN +* DROPCOMPLETE +* DROPTEXT +* DROPFILE +""" +import pygame as pg -pg.init() def main(): - - Running = True - surf = pg.display.set_mode((640,480)) + pg.init() + + going = True + surf = pg.display.set_mode((640, 480)) font = pg.font.SysFont("Arial", 24) clock = pg.time.Clock() - spr_file_text = font.render("Feed me some file or image!", 1, (255,255,255)) + spr_file_text = font.render("Drag and drop a file or image!", 1, (255, 255, 255)) spr_file_text_rect = spr_file_text.get_rect() spr_file_text_rect.center = surf.get_rect().center spr_file_image = None spr_file_image_rect = None - - while Running: + + while going: for ev in pg.event.get(): if ev.type == pg.QUIT: - Running = False + going = False elif ev.type == pg.DROPBEGIN: print(ev) print("File drop begin!") @@ -31,32 +40,33 @@ def main(): print("File drop complete!") elif ev.type == pg.DROPTEXT: print(ev) - spr_file_text = font.render(ev.text, 1, (255,255,255)) + spr_file_text = font.render(ev.text, 1, (255, 255, 255)) spr_file_text_rect = spr_file_text.get_rect() spr_file_text_rect.center = surf.get_rect().center elif ev.type == pg.DROPFILE: print(ev) - spr_file_text = font.render(ev.file, 1, (255,255,255)) + spr_file_text = font.render(ev.file, 1, (255, 255, 255)) spr_file_text_rect = spr_file_text.get_rect() spr_file_text_rect.center = surf.get_rect().center - #Try to open the file if it's an image + # Try to open the file if it's an image filetype = ev.file[-3:] - if (filetype in ["png", "bmp", "jpg"]): + if filetype in ["png", "bmp", "jpg"]: spr_file_image = pg.image.load(ev.file).convert() spr_file_image.set_alpha(127) spr_file_image_rect = spr_file_image.get_rect() spr_file_image_rect.center = surf.get_rect().center - - surf.fill((0,0,0)) + + surf.fill((0, 0, 0)) surf.blit(spr_file_text, spr_file_text_rect) - if (spr_file_image): + if spr_file_image and spr_file_image_rect is not None: surf.blit(spr_file_image, spr_file_image_rect) - + pg.display.flip() clock.tick(30) pg.quit() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/eventlist.py b/venv/Lib/site-packages/pygame/examples/eventlist.py index 7c31836313b789a78d5369dcfa4b8f13eaf4d6ff..3afe5bc98d289fd11ea45df96a7fa32ec9c21015 100644 --- a/venv/Lib/site-packages/pygame/examples/eventlist.py +++ b/venv/Lib/site-packages/pygame/examples/eventlist.py @@ -1,123 +1,194 @@ #!/usr/bin/env python +""" pygame.examples.eventlist -"""Eventlist is a sloppy style of pygame, but is a handy -tool for learning about pygame events and input. At the -top of the screen are the state of several device values, +Learn about pygame events and input. + +At the top of the screen are the state of several device values, and a scrolling list of events are displayed on the bottom. -This is not quality 'ui' code at all, but you can see how -to implement very non-interactive status displays, or even -a crude text output control. """ -from pygame import * +usage = """ +Mouse Controls +============== + +- 1st button on mouse (left click) to toggle events 'grabed'. +- 3rd button on mouse (right click) to toggle mouse visible. +- The window can be resized. +- Mouse the mouse around to see mouse events. +- If events grabbed and mouse invisible show virtual mouse coords. + + +Keyboard Joystick Controls +========================== + +- press keys up an down to see events. +- you can see joystick events if any are plugged in. +- press "c" to toggle events generated by controllers. +""" + +from typing import List +import pygame as pg + +import pygame._sdl2.controller + + +img_on_off: List[pg.Surface] = [] +font: pg.font.Font +last_key = None + +# these are a running counter of mouse.get_rel() calls. +virtual_x = 0 +virtual_y = 0 -ImgOnOff = [] -Font = None -LastKey = None def showtext(win, pos, text, color, bgcolor): - textimg = Font.render(text, 1, color, bgcolor) + textimg = font.render(text, 1, color, bgcolor) win.blit(textimg, pos) return pos[0] + textimg.get_width() + 5, pos[1] def drawstatus(win): + global virtual_x, virtual_y bgcolor = 50, 50, 50 win.fill(bgcolor, (0, 0, 640, 120)) - win.blit(Font.render('Status Area', 1, (155, 155, 155), bgcolor), (2, 2)) + win.blit(font.render("Status Area", 1, (155, 155, 155), bgcolor), (2, 2)) - pos = showtext(win, (10, 30), 'Mouse Focus', (255, 255, 255), bgcolor) - win.blit(ImgOnOff[mouse.get_focused()], pos) + pos = showtext(win, (10, 30), "Mouse Focus", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.mouse.get_focused()], pos) - pos = showtext(win, (330, 30), 'Keyboard Focus', (255, 255, 255), bgcolor) - win.blit(ImgOnOff[key.get_focused()], pos) + pos = showtext( + win, (pos[0] + 50, pos[1]), "Mouse visible", (255, 255, 255), bgcolor + ) + win.blit(img_on_off[pg.mouse.get_visible()], pos) - pos = showtext(win, (10, 60), 'Mouse Position', (255, 255, 255), bgcolor) - p = '%s, %s' % mouse.get_pos() - pos = showtext(win, pos, p, bgcolor, (255, 255, 55)) + pos = showtext(win, (330, 30), "Keyboard Focus", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.key.get_focused()], pos) - pos = showtext(win, (330, 60), 'Last Keypress', (255, 255, 255), bgcolor) - if LastKey: - p = '%d, %s' % (LastKey, key.name(LastKey)) + pos = showtext(win, (10, 60), "Mouse Position(rel)", (255, 255, 255), bgcolor) + rel = pg.mouse.get_rel() + virtual_x += rel[0] + virtual_y += rel[1] + + mouse_data = tuple(list(pg.mouse.get_pos()) + list(rel)) + p = "%s, %s (%s, %s)" % mouse_data + showtext(win, pos, p, bgcolor, (255, 255, 55)) + + pos = showtext(win, (330, 60), "Last Keypress", (255, 255, 255), bgcolor) + if last_key: + p = "%d, %s" % (last_key, pg.key.name(last_key)) else: - p = 'None' - pos = showtext(win, pos, p, bgcolor, (255, 255, 55)) + p = "None" + showtext(win, pos, p, bgcolor, (255, 255, 55)) + + pos = showtext(win, (10, 90), "Input Grabbed", (255, 255, 255), bgcolor) + win.blit(img_on_off[pg.event.get_grab()], pos) - pos = showtext(win, (10, 90), 'Input Grabbed', (255, 255, 255), bgcolor) - win.blit(ImgOnOff[event.get_grab()], pos) + is_virtual_mouse = pg.event.get_grab() and not pg.mouse.get_visible() + pos = showtext(win, (330, 90), "Virtual Mouse", (255, 255, 255), bgcolor) + win.blit(img_on_off[is_virtual_mouse], pos) + if is_virtual_mouse: + p = f"{virtual_x}, {virtual_y}" + showtext(win, (pos[0] + 50, pos[1]), p, bgcolor, (255, 255, 55)) def drawhistory(win, history): - win.blit(Font.render('Event History Area', 1, (155, 155, 155), (0,0,0)), (2, 132)) + img = font.render("Event History Area", 1, (155, 155, 155), (0, 0, 0)) + win.blit(img, (2, 132)) ypos = 450 h = list(history) h.reverse() for line in h: r = win.blit(line, (10, ypos)) win.fill(0, (r.right, r.top, 620, r.height)) - ypos -= Font.get_height() + ypos -= font.get_height() + + +def draw_usage_in_history(history, text): + lines = text.split("\n") + for line in lines: + if line == "" or "===" in line: + continue + img = font.render(line, 1, (50, 200, 50), (0, 0, 0)) + history.append(img) def main(): - init() + pg.init() + pygame._sdl2.controller.init() - win = display.set_mode((640, 480), RESIZABLE) - display.set_caption("Mouse Focus Workout") + print(usage) - global Font - Font = font.Font(None, 26) + win = pg.display.set_mode((640, 480), pg.RESIZABLE) + pg.display.set_caption("Mouse Focus Workout. h key for help") - global ImgOnOff - ImgOnOff.append(Font.render("Off", 1, (0, 0, 0), (255, 50, 50))) - ImgOnOff.append(Font.render("On", 1, (0, 0, 0), (50, 255, 50))) + global font + font = pg.font.Font(None, 26) + global img_on_off + img_on_off.append(font.render("Off", 1, (0, 0, 0), (255, 50, 50))) + img_on_off.append(font.render("On", 1, (0, 0, 0), (50, 255, 50))) + + # stores surfaces of text representing what has gone through the event queue history = [] - #let's turn on the joysticks just so we can play with em - for x in range(joystick.get_count()): - j = joystick.Joystick(x) - j.init() - txt = 'Enabled joystick: ' + j.get_name() - img = Font.render(txt, 1, (50, 200, 50), (0, 0, 0)) + # let's turn on the joysticks just so we can play with em + for x in range(pg.joystick.get_count()): + if pygame._sdl2.controller.is_controller(x): + c = pygame._sdl2.controller.Controller(x) + txt = "Enabled controller: " + c.name + else: + j = pg.joystick.Joystick(x) + txt = "Enabled joystick: " + j.get_name() + + img = font.render(txt, 1, (50, 200, 50), (0, 0, 0)) history.append(img) - if not joystick.get_count(): - img = Font.render('No Joysticks to Initialize', 1, (50, 200, 50), (0, 0, 0)) + if not pg.joystick.get_count(): + img = font.render("No Joysticks to Initialize", 1, (50, 200, 50), (0, 0, 0)) history.append(img) going = True while going: - for e in event.get(): - if e.type == QUIT: - going = False - if e.type == KEYDOWN: - if e.key == K_ESCAPE: + for e in pg.event.get(): + if e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: going = False else: - global LastKey - LastKey = e.key - if e.type == MOUSEBUTTONDOWN: - event.set_grab(1) - elif e.type == MOUSEBUTTONUP: - event.set_grab(0) - if e.type == VIDEORESIZE: - win = display.set_mode(e.size, RESIZABLE) - - if e.type != MOUSEMOTION: - txt = '%s: %s' % (event.event_name(e.type), e.dict) - img = Font.render(txt, 1, (50, 200, 50), (0, 0, 0)) + global last_key + last_key = e.key + if e.key == pg.K_h: + draw_usage_in_history(history, usage) + if e.key == pg.K_c: + current_state = pygame._sdl2.controller.get_eventstate() + pygame._sdl2.controller.set_eventstate(not current_state) + + if e.type == pg.MOUSEBUTTONDOWN and e.button == 1: + pg.event.set_grab(not pg.event.get_grab()) + + if e.type == pg.MOUSEBUTTONDOWN and e.button == 3: + pg.mouse.set_visible(not pg.mouse.get_visible()) + + if e.type != pg.MOUSEMOTION: + txt = f"{pg.event.event_name(e.type)}: {e.dict}" + img = font.render(txt, 1, (50, 200, 50), (0, 0, 0)) history.append(img) history = history[-13:] + if e.type == pg.VIDEORESIZE: + win = pg.display.set_mode(e.size, pg.RESIZABLE) + + if e.type == pg.QUIT: + going = False drawstatus(win) drawhistory(win, history) - display.flip() - time.wait(10) + pg.display.flip() + pg.time.wait(10) - quit() + pg.quit() + raise SystemExit -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/fastevents.py b/venv/Lib/site-packages/pygame/examples/fastevents.py deleted file mode 100644 index 07ff7933fe59ccf1fa1179451080a58700d05d09..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/fastevents.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python -""" This is a stress test for the fastevents module. - -*Fast events does not appear faster!* - -So far it looks like normal pygame.event is faster by up to two times. -So maybe fastevent isn't fast at all. - -Tested on windowsXP sp2 athlon, and freebsd. - -However... on my debian duron 850 machine fastevents is faster. -""" - -import pygame -from pygame import * - -# the config to try different settings out with the event queues. - -# use the fastevent module or not. -use_fast_events = 1 - -# use pygame.display.flip(). -# otherwise we test raw event processing throughput. -with_display = 1 - -# limit the game loop to 40 fps. -slow_tick = 0 - -NUM_EVENTS_TO_POST = 200000 - - - -if use_fast_events: - event_module = fastevent -else: - event_module = event - - - - -from threading import Thread - -class post_them(Thread): - def __init__(self): - Thread.__init__(self) - self.done = [] - self.stop = [] - - def run(self): - self.done = [] - self.stop = [] - for x in range(NUM_EVENTS_TO_POST): - ee = event.Event(USEREVENT) - try_post = 1 - - # the pygame.event.post raises an exception if the event - # queue is full. so wait a little bit, and try again. - while try_post: - try: - event_module.post(ee) - try_post = 0 - except: - pytime.sleep(0.001) - try_post = 1 - - if self.stop: - return - self.done.append(1) - - - -import time as pytime - -def main(): - init() - - if use_fast_events: - fastevent.init() - - c = time.Clock() - - win = display.set_mode((640, 480), RESIZABLE) - display.set_caption("fastevent Workout") - - poster = post_them() - - t1 = pytime.time() - poster.start() - - going = True - while going: -# for e in event.get(): - #for x in range(200): - # ee = event.Event(USEREVENT) - # r = event_module.post(ee) - # print (r) - - #for e in event_module.get(): - event_list = [] - event_list = event_module.get() - - for e in event_list: - if e.type == QUIT: - print (c.get_fps()) - poster.stop.append(1) - going = False - if e.type == KEYDOWN: - if e.key == K_ESCAPE: - print (c.get_fps()) - poster.stop.append(1) - going = False - if poster.done: - print (c.get_fps()) - print (c) - t2 = pytime.time() - print ("total time:%s" % (t2 - t1)) - print ("events/second:%s" % (NUM_EVENTS_TO_POST / (t2 - t1))) - going = False - if with_display: - display.flip() - if slow_tick: - c.tick(40) - - - pygame.quit() - - - -if __name__ == '__main__': - main() diff --git a/venv/Lib/site-packages/pygame/examples/fonty.py b/venv/Lib/site-packages/pygame/examples/fonty.py index bdd77f1608d30c8a5bc2efd9a922d50eb1a9703c..786f7413ebfdfa1c186dfd3a55a517e4586c4b92 100644 --- a/venv/Lib/site-packages/pygame/examples/fonty.py +++ b/venv/Lib/site-packages/pygame/examples/fonty.py @@ -1,101 +1,81 @@ #!/usr/bin/env python +""" pygame.examples.fonty -"""Here we load a .TTF font file, and display it in -a basic pygame window. It demonstrates several of the -Font object attributes. Nothing exciting in here, but -it makes a great example for basic window, event, and -font management.""" +Here we load a .TTF True Type font file, and display it in +a basic pygame window. +Demonstrating several Font object attributes. -import pygame -from pygame.locals import * -from pygame.compat import unichr_, unicode_ -import sys -import locale +- basic window, event, and font management. +""" +import pygame as pg -if sys.version_info >= (3,): - def print_unicode(s): - e = locale.getpreferredencoding() - print (s.encode(e, 'backslashreplace').decode()) -else: - def print_unicode(s): - e = locale.getpreferredencoding() - print (s.encode(e, 'backslashreplace')) - def main(): - #initialize - pygame.init() + # initialize + pg.init() resolution = 400, 200 - screen = pygame.display.set_mode(resolution) + screen = pg.display.set_mode(resolution) -## pygame.mouse.set_cursor(*pygame.cursors.diamond) + ## pg.mouse.set_cursor(*pg.cursors.diamond) fg = 250, 240, 230 bg = 5, 5, 5 wincolor = 40, 40, 90 - #fill background + # fill background screen.fill(wincolor) - #load font, prepare values - font = pygame.font.Font(None, 80) - text = 'Fonty' + # load font, prepare values + font = pg.font.Font(None, 80) + text = "Fonty" size = font.size(text) - #no AA, no transparancy, normal + # no AA, no transparency, normal ren = font.render(text, 0, fg, bg) screen.blit(ren, (10, 10)) - #no AA, transparancy, underline + # no AA, transparency, underline font.set_underline(1) ren = font.render(text, 0, fg) screen.blit(ren, (10, 40 + size[1])) font.set_underline(0) + a_sys_font = pg.font.SysFont("Arial", 60) - a_sys_font = pygame.font.SysFont("Arial", 60) - - - #AA, no transparancy, bold + # AA, no transparency, bold a_sys_font.set_bold(1) ren = a_sys_font.render(text, 1, fg, bg) screen.blit(ren, (30 + size[0], 10)) a_sys_font.set_bold(0) - #AA, transparancy, italic + # AA, transparency, italic a_sys_font.set_italic(1) ren = a_sys_font.render(text, 1, fg) screen.blit(ren, (30 + size[0], 40 + size[1])) a_sys_font.set_italic(0) - # Get some metrics. - print ("Font metrics for 'Fonty': %s" % a_sys_font.metrics (text)) - ch = unicode_("%c") % 0x3060 - msg = (unicode_("Font metrics for '%s': %s") % - (ch, a_sys_font.metrics (ch))) - print_unicode(msg) + print(f"Font metrics for 'Fonty': {a_sys_font.metrics(text)}") + ch = "\u3060" + msg = f"Font metrics for '{ch}': {a_sys_font.metrics(ch)}" + print(msg) ## #some_japanese_unicode = u"\u304b\u3070\u306b" ##some_japanese_unicode = unicode_('%c%c%c') % (0x304b, 0x3070, 0x306b) - - #AA, transparancy, italic + + # AA, transparency, italic ##ren = a_sys_font.render(some_japanese_unicode, 1, fg) ##screen.blit(ren, (30 + size[0], 40 + size[1])) - - - - - #show the surface and await user quit - pygame.display.flip() - while 1: - #use event.wait to keep from polling 100% cpu - if pygame.event.wait().type in (QUIT, KEYDOWN, MOUSEBUTTONDOWN): + # show the surface and await user quit + pg.display.flip() + while True: + # use event.wait to keep from polling 100% cpu + if pg.event.wait().type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): break + pg.quit() - -if __name__ == '__main__': main() - +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/pygame/examples/freetype_misc.py b/venv/Lib/site-packages/pygame/examples/freetype_misc.py index 21c5f70a8ab862c736c054358bba1e9a5e0dc3be..70a6090eac0f32f3e8bf1d28a800e667bacc1be5 100644 --- a/venv/Lib/site-packages/pygame/examples/freetype_misc.py +++ b/venv/Lib/site-packages/pygame/examples/freetype_misc.py @@ -1,103 +1,157 @@ -import sys, os -import pygame -from pygame.locals import * - -try: - import pygame.freetype as freetype -except ImportError: - print ("No FreeType support compiled") - sys.exit () - -colors = { - "grey_light" : pygame.Color(200, 200, 200), - "grey_dark" : pygame.Color(100, 100, 100), - "green" : pygame.Color(50, 255, 63), - "red" : pygame.Color(220, 30, 30), - "blue" : pygame.Color(50, 75, 245) -} +#!/usr/bin/env python +""" pygame.examples.freetype_misc + + +Miscellaneous (or misc) means: + "consisting of a mixture of various things that are not + usually connected with each other" + Adjective + + +All those words you read on computers, magazines, books, and such over the years? +Probably a lot of them were constructed with... + +The FreeType Project: a free, high-quality and portable Font engine. +https://freetype.org + +Next time you're reading something. Think of them. + + +Herein lies a *BOLD* demo consisting of a mixture of various things. + + Not only is it a *BOLD* demo, it's an + italics demo, + a rotated demo, + it's a blend, + and is sized to go nicely with a cup of tea*. + + * also goes well with coffee. + +Enjoy! +""" +import os +import pygame as pg +import pygame.freetype as freetype + def run(): - pygame.init() + pg.init() - fontdir = os.path.dirname(os.path.abspath (__file__)) - font = freetype.Font(os.path.join (fontdir, "data", "sans.ttf")) + fontdir = os.path.dirname(os.path.abspath(__file__)) + font = freetype.Font(os.path.join(fontdir, "data", "sans.ttf")) - screen = pygame.display.set_mode((800, 600)) - screen.fill (colors["grey_light"]) + screen = pg.display.set_mode((800, 600)) + screen.fill("gray") font.underline_adjustment = 0.5 font.pad = True - font.render_to(screen, (32, 32), "Hello World", colors["red"], - colors['grey_dark'], size=64, - style=freetype.STYLE_UNDERLINE|freetype.STYLE_OBLIQUE) + font.render_to( + screen, + (32, 32), + "Hello World", + "red3", + "dimgray", + size=64, + style=freetype.STYLE_UNDERLINE | freetype.STYLE_OBLIQUE, + ) font.pad = False - font.render_to(screen, (32, 128), "abcdefghijklm", colors["grey_dark"], - colors["green"], size=64) + font.render_to( + screen, + (32, 128), + "abcdefghijklm", + "dimgray", + "green3", + size=64, + ) font.vertical = True - font.render_to(screen, (32, 200), "Vertical?", colors["blue"], - None, size=32) + font.render_to(screen, (32, 200), "Vertical?", "blue3", None, size=32) font.vertical = False - font.render_to(screen, (64, 190), "Let's spin!", colors["red"], - None, size=48, rotation=55) + font.render_to(screen, (64, 190), "Let's spin!", "red3", None, size=48, rotation=55) - font.render_to(screen, (160, 290), "All around!", colors["green"], - None, size=48, rotation=-55) + font.render_to( + screen, (160, 290), "All around!", "green3", None, size=48, rotation=-55 + ) - font.render_to(screen, (250, 220), "and BLEND", - pygame.Color(255, 0, 0, 128), None, size=64) + font.render_to(screen, (250, 220), "and BLEND", (255, 0, 0, 128), None, size=64) - font.render_to(screen, (265, 237), "or BLAND!", - pygame.Color(0, 0xCC, 28, 128), None, size=64) + font.render_to(screen, (265, 237), "or BLAND!", (0, 0xCC, 28, 128), None, size=64) # Some pinwheels font.origin = True for angle in range(0, 360, 45): - font.render_to(screen, (150, 420), ")", pygame.Color('black'), - size=48, rotation=angle) + font.render_to(screen, (150, 420), ")", "black", size=48, rotation=angle) font.vertical = True for angle in range(15, 375, 30): - font.render_to(screen, (600, 400), "|^*", pygame.Color('orange'), - size=48, rotation=angle) + font.render_to(screen, (600, 400), "|^*", "orange", size=48, rotation=angle) font.vertical = False font.origin = False - utext = pygame.compat.as_unicode(r"I \u2665 Unicode") - font.render_to(screen, (298, 320), utext, pygame.Color(0, 0xCC, 0xDD), - None, size=64) + utext = "I \u2665 Unicode" + font.render_to(screen, (298, 320), utext, (0, 0xCC, 0xDD), None, size=64) - utext = pygame.compat.as_unicode(r"\u2665") - font.render_to(screen, (480, 32), utext, colors["grey_light"], - colors["red"], size=148) + utext = "\u2665" + font.render_to(screen, (480, 32), utext, "gray", "red3", size=148) - font.render_to(screen, (380, 380), "...yes, this is an SDL surface", - pygame.Color(0, 0, 0), - None, size=24, style=freetype.STYLE_STRONG) + font.render_to( + screen, + (380, 380), + "...yes, this is an SDL surface", + "black", + None, + size=24, + style=freetype.STYLE_STRONG, + ) font.origin = True - r = font.render_to(screen, (100, 530), "stretch", - pygame.Color('red'), - None, size=(24, 24), style=freetype.STYLE_NORMAL) - font.render_to(screen, (100 + r.width, 530), " VERTICAL", - pygame.Color('red'), - None, size=(24, 48), style=freetype.STYLE_NORMAL) - - r = font.render_to(screen, (100, 580), "stretch", - pygame.Color('blue'), - None, size=(24, 24), style=freetype.STYLE_NORMAL) - font.render_to(screen, (100 + r.width, 580), " HORIZONTAL", - pygame.Color('blue'), - None, size=(48, 24), style=freetype.STYLE_NORMAL) - - pygame.display.flip() - - while 1: - if pygame.event.wait().type in (QUIT, KEYDOWN, MOUSEBUTTONDOWN): + r = font.render_to( + screen, + (100, 530), + "stretch", + "red3", + None, + size=(24, 24), + style=freetype.STYLE_NORMAL, + ) + font.render_to( + screen, + (100 + r.width, 530), + " VERTICAL", + "red3", + None, + size=(24, 48), + style=freetype.STYLE_NORMAL, + ) + + r = font.render_to( + screen, + (100, 580), + "stretch", + "blue3", + None, + size=(24, 24), + style=freetype.STYLE_NORMAL, + ) + font.render_to( + screen, + (100 + r.width, 580), + " HORIZONTAL", + "blue3", + None, + size=(48, 24), + style=freetype.STYLE_NORMAL, + ) + + pg.display.flip() + + while True: + if pg.event.wait().type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): break - pygame.quit() + pg.quit() + if __name__ == "__main__": - run () + run() diff --git a/venv/Lib/site-packages/pygame/examples/glcube.py b/venv/Lib/site-packages/pygame/examples/glcube.py index be2d7f9fa1e15718e6e9a373fe72ceda44a4b91a..9d85c242a0ae1c449a4c85719023c72d7111af0a 100644 --- a/venv/Lib/site-packages/pygame/examples/glcube.py +++ b/venv/Lib/site-packages/pygame/examples/glcube.py @@ -1,129 +1,591 @@ #!/usr/bin/env python +""" pygame.examples.glcube + +Draw a cube on the screen. + + + +Amazing. + +Every frame we orbit the camera around a small amount +creating the illusion of a spinning object. + +First we setup some points of a multicolored cube. Then we then go through +a semi-unoptimized loop to draw the cube points onto the screen. + +OpenGL does all the hard work for us. :] + + +Keyboard Controls +----------------- + +* ESCAPE key to quit +* f key to toggle fullscreen. -"""Draw a cube on the screen. every frame we orbit -the camera around by a small amount and it appears -the object is spinning. note i've setup some simple -data structures here to represent a multicolored cube, -we then go through a semi-unoptimized loop to draw -the cube points onto the screen. opengl does all the -hard work for us. :] """ +import math +import ctypes -import pygame -from pygame.locals import * +import pygame as pg try: - from OpenGL.GL import * - from OpenGL.GLU import * + import OpenGL.GL as GL + import OpenGL.GLU as GLU except ImportError: - print ('The GLCUBE example requires PyOpenGL') + print("pyopengl missing. The GLCUBE example requires: pyopengl numpy") raise SystemExit +try: + from numpy import array, dot, eye, zeros, float32, uint32 +except ImportError: + print("numpy missing. The GLCUBE example requires: pyopengl numpy") + raise SystemExit -#some simple data for a colored cube -#here we have the 3D point position and color -#for each corner. then we have a list of indices -#that describe each face, and a list of indieces -#that describes each edge +# do we want to use the 'modern' OpenGL API or the old one? +# This example shows you how to do both. +USE_MODERN_GL = True +# Some simple data for a colored cube here we have the 3D point position +# and color for each corner. A list of indices describes each face, and a +# list of indices describes each edge. CUBE_POINTS = ( - (0.5, -0.5, -0.5), (0.5, 0.5, -0.5), - (-0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), - (0.5, -0.5, 0.5), (0.5, 0.5, 0.5), - (-0.5, -0.5, 0.5), (-0.5, 0.5, 0.5) + (0.5, -0.5, -0.5), + (0.5, 0.5, -0.5), + (-0.5, 0.5, -0.5), + (-0.5, -0.5, -0.5), + (0.5, -0.5, 0.5), + (0.5, 0.5, 0.5), + (-0.5, -0.5, 0.5), + (-0.5, 0.5, 0.5), ) -#colors are 0-1 floating values +# colors are 0-1 floating values CUBE_COLORS = ( - (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 0), - (1, 0, 1), (1, 1, 1), (0, 0, 1), (0, 1, 1) + (1, 0, 0), + (1, 1, 0), + (0, 1, 0), + (0, 0, 0), + (1, 0, 1), + (1, 1, 1), + (0, 0, 1), + (0, 1, 1), ) CUBE_QUAD_VERTS = ( - (0, 1, 2, 3), (3, 2, 7, 6), (6, 7, 5, 4), - (4, 5, 1, 0), (1, 5, 7, 2), (4, 0, 3, 6) + (0, 1, 2, 3), + (3, 2, 7, 6), + (6, 7, 5, 4), + (4, 5, 1, 0), + (1, 5, 7, 2), + (4, 0, 3, 6), ) CUBE_EDGES = ( - (0,1), (0,3), (0,4), (2,1), (2,3), (2,7), - (6,3), (6,4), (6,7), (5,1), (5,4), (5,7), + (0, 1), + (0, 3), + (0, 4), + (2, 1), + (2, 3), + (2, 7), + (6, 3), + (6, 4), + (6, 7), + (5, 1), + (5, 4), + (5, 7), ) +def translate(matrix, x=0.0, y=0.0, z=0.0): + """ + Translate (move) a matrix in the x, y and z axes. + + :param matrix: Matrix to translate. + :param x: direction and magnitude to translate in x axis. Defaults to 0. + :param y: direction and magnitude to translate in y axis. Defaults to 0. + :param z: direction and magnitude to translate in z axis. Defaults to 0. + :return: The translated matrix. + """ + translation_matrix = array( + [ + [1.0, 0.0, 0.0, x], + [0.0, 1.0, 0.0, y], + [0.0, 0.0, 1.0, z], + [0.0, 0.0, 0.0, 1.0], + ], + dtype=matrix.dtype, + ).T + matrix[...] = dot(matrix, translation_matrix) + return matrix + + +def frustum(left, right, bottom, top, znear, zfar): + """ + Build a perspective matrix from the clipping planes, or camera 'frustrum' + volume. + + :param left: left position of the near clipping plane. + :param right: right position of the near clipping plane. + :param bottom: bottom position of the near clipping plane. + :param top: top position of the near clipping plane. + :param znear: z depth of the near clipping plane. + :param zfar: z depth of the far clipping plane. + + :return: A perspective matrix. + """ + perspective_matrix = zeros((4, 4), dtype=float32) + perspective_matrix[0, 0] = +2.0 * znear / (right - left) + perspective_matrix[2, 0] = (right + left) / (right - left) + perspective_matrix[1, 1] = +2.0 * znear / (top - bottom) + perspective_matrix[3, 1] = (top + bottom) / (top - bottom) + perspective_matrix[2, 2] = -(zfar + znear) / (zfar - znear) + perspective_matrix[3, 2] = -2.0 * znear * zfar / (zfar - znear) + perspective_matrix[2, 3] = -1.0 + return perspective_matrix + + +def perspective(fovy, aspect, znear, zfar): + """ + Build a perspective matrix from field of view, aspect ratio and depth + planes. + + :param fovy: the field of view angle in the y axis. + :param aspect: aspect ratio of our view port. + :param znear: z depth of the near clipping plane. + :param zfar: z depth of the far clipping plane. -def drawcube(): - "draw the cube" + :return: A perspective matrix. + """ + h = math.tan(fovy / 360.0 * math.pi) * znear + w = h * aspect + return frustum(-w, w, -h, h, znear, zfar) + + +def rotate(matrix, angle, x, y, z): + """ + Rotate a matrix around an axis. + + :param matrix: The matrix to rotate. + :param angle: The angle to rotate by. + :param x: x of axis to rotate around. + :param y: y of axis to rotate around. + :param z: z of axis to rotate around. + + :return: The rotated matrix + """ + angle = math.pi * angle / 180 + c, s = math.cos(angle), math.sin(angle) + n = math.sqrt(x * x + y * y + z * z) + x, y, z = x / n, y / n, z / n + cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z + rotation_matrix = array( + [ + [cx * x + c, cy * x - z * s, cz * x + y * s, 0], + [cx * y + z * s, cy * y + c, cz * y - x * s, 0], + [cx * z - y * s, cy * z + x * s, cz * z + c, 0], + [0, 0, 0, 1], + ], + dtype=matrix.dtype, + ).T + matrix[...] = dot(matrix, rotation_matrix) + return matrix + + +class Rotation: + """ + Data class that stores rotation angles in three axes. + """ + + def __init__(self): + self.theta = 20 + self.phi = 40 + self.psi = 25 + + +def drawcube_old(): + """ + Draw the cube using the old open GL methods pre 3.2 core context. + """ allpoints = list(zip(CUBE_POINTS, CUBE_COLORS)) - glBegin(GL_QUADS) + GL.glBegin(GL.GL_QUADS) for face in CUBE_QUAD_VERTS: for vert in face: pos, color = allpoints[vert] - glColor3fv(color) - glVertex3fv(pos) - glEnd() + GL.glColor3fv(color) + GL.glVertex3fv(pos) + GL.glEnd() - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINES) + GL.glColor3f(1.0, 1.0, 1.0) + GL.glBegin(GL.GL_LINES) for line in CUBE_EDGES: for vert in line: pos, color = allpoints[vert] - glVertex3fv(pos) + GL.glVertex3fv(pos) + + GL.glEnd() + + +def init_gl_stuff_old(): + """ + Initialise open GL, prior to core context 3.2 + """ + GL.glEnable(GL.GL_DEPTH_TEST) # use our zbuffer + + # setup the camera + GL.glMatrixMode(GL.GL_PROJECTION) + GL.glLoadIdentity() + GLU.gluPerspective(45.0, 640 / 480.0, 0.1, 100.0) # setup lens + GL.glTranslatef(0.0, 0.0, -3.0) # move back + GL.glRotatef(25, 1, 0, 0) # orbit higher + + +def init_gl_modern(display_size): + """ + Initialise open GL in the 'modern' open GL style for open GL versions + greater than 3.1. + + :param display_size: Size of the window/viewport. + """ + + # Create shaders + # -------------------------------------- + vertex_code = """ + + #version 150 + uniform mat4 model; + uniform mat4 view; + uniform mat4 projection; + + uniform vec4 colour_mul; + uniform vec4 colour_add; + + in vec4 vertex_colour; // vertex colour in + in vec3 vertex_position; - glEnd() + out vec4 vertex_color_out; // vertex colour out + void main() + { + vertex_color_out = (colour_mul * vertex_colour) + colour_add; + gl_Position = projection * view * model * vec4(vertex_position, 1.0); + } -def init_gl_stuff(): + """ - glEnable(GL_DEPTH_TEST) #use our zbuffer + fragment_code = """ + #version 150 + in vec4 vertex_color_out; // vertex colour from vertex shader + out vec4 fragColor; + void main() + { + fragColor = vertex_color_out; + } + """ + + program = GL.glCreateProgram() + vertex = GL.glCreateShader(GL.GL_VERTEX_SHADER) + fragment = GL.glCreateShader(GL.GL_FRAGMENT_SHADER) + GL.glShaderSource(vertex, vertex_code) + GL.glCompileShader(vertex) + + # this logs issues the shader compiler finds. + log = GL.glGetShaderInfoLog(vertex) + if isinstance(log, bytes): + log = log.decode() + for line in log.split("\n"): + print(line) + + GL.glAttachShader(program, vertex) + GL.glShaderSource(fragment, fragment_code) + GL.glCompileShader(fragment) + + # this logs issues the shader compiler finds. + log = GL.glGetShaderInfoLog(fragment) + if isinstance(log, bytes): + log = log.decode() + for line in log.split("\n"): + print(line) + + GL.glAttachShader(program, fragment) + GL.glValidateProgram(program) + GL.glLinkProgram(program) + + GL.glDetachShader(program, vertex) + GL.glDetachShader(program, fragment) + GL.glUseProgram(program) + + # Create vertex buffers and shader constants + # ------------------------------------------ + + # Cube Data + vertices = zeros( + 8, [("vertex_position", float32, 3), ("vertex_colour", float32, 4)] + ) + + vertices["vertex_position"] = [ + [1, 1, 1], + [-1, 1, 1], + [-1, -1, 1], + [1, -1, 1], + [1, -1, -1], + [1, 1, -1], + [-1, 1, -1], + [-1, -1, -1], + ] + + vertices["vertex_colour"] = [ + [0, 1, 1, 1], + [0, 0, 1, 1], + [0, 0, 0, 1], + [0, 1, 0, 1], + [1, 1, 0, 1], + [1, 1, 1, 1], + [1, 0, 1, 1], + [1, 0, 0, 1], + ] + + filled_cube_indices = array( + [ + 0, + 1, + 2, + 0, + 2, + 3, + 0, + 3, + 4, + 0, + 4, + 5, + 0, + 5, + 6, + 0, + 6, + 1, + 1, + 6, + 7, + 1, + 7, + 2, + 7, + 4, + 3, + 7, + 3, + 2, + 4, + 7, + 6, + 4, + 6, + 5, + ], + dtype=uint32, + ) + + outline_cube_indices = array( + [0, 1, 1, 2, 2, 3, 3, 0, 4, 7, 7, 6, 6, 5, 5, 4, 0, 5, 1, 6, 2, 7, 3, 4], + dtype=uint32, + ) + + shader_data = {"buffer": {}, "constants": {}} + + GL.glBindVertexArray(GL.glGenVertexArrays(1)) # Have to do this first + + shader_data["buffer"]["vertices"] = GL.glGenBuffers(1) + GL.glBindBuffer(GL.GL_ARRAY_BUFFER, shader_data["buffer"]["vertices"]) + GL.glBufferData(GL.GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL.GL_DYNAMIC_DRAW) + + stride = vertices.strides[0] + offset = ctypes.c_void_p(0) + + loc = GL.glGetAttribLocation(program, "vertex_position") + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, 3, GL.GL_FLOAT, False, stride, offset) + + offset = ctypes.c_void_p(vertices.dtype["vertex_position"].itemsize) + + loc = GL.glGetAttribLocation(program, "vertex_colour") + GL.glEnableVertexAttribArray(loc) + GL.glVertexAttribPointer(loc, 4, GL.GL_FLOAT, False, stride, offset) + + shader_data["buffer"]["filled"] = GL.glGenBuffers(1) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["filled"]) + GL.glBufferData( + GL.GL_ELEMENT_ARRAY_BUFFER, + filled_cube_indices.nbytes, + filled_cube_indices, + GL.GL_STATIC_DRAW, + ) + + shader_data["buffer"]["outline"] = GL.glGenBuffers(1) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["outline"]) + GL.glBufferData( + GL.GL_ELEMENT_ARRAY_BUFFER, + outline_cube_indices.nbytes, + outline_cube_indices, + GL.GL_STATIC_DRAW, + ) + + shader_data["constants"]["model"] = GL.glGetUniformLocation(program, "model") + GL.glUniformMatrix4fv(shader_data["constants"]["model"], 1, False, eye(4)) + + shader_data["constants"]["view"] = GL.glGetUniformLocation(program, "view") + view = translate(eye(4), z=-6) + GL.glUniformMatrix4fv(shader_data["constants"]["view"], 1, False, view) + + shader_data["constants"]["projection"] = GL.glGetUniformLocation( + program, "projection" + ) + GL.glUniformMatrix4fv(shader_data["constants"]["projection"], 1, False, eye(4)) + + # This colour is multiplied with the base vertex colour in producing + # the final output + shader_data["constants"]["colour_mul"] = GL.glGetUniformLocation( + program, "colour_mul" + ) + GL.glUniform4f(shader_data["constants"]["colour_mul"], 1, 1, 1, 1) + + # This colour is added on to the base vertex colour in producing + # the final output + shader_data["constants"]["colour_add"] = GL.glGetUniformLocation( + program, "colour_add" + ) + GL.glUniform4f(shader_data["constants"]["colour_add"], 0, 0, 0, 0) + + # Set GL drawing data + # ------------------- + GL.glClearColor(0, 0, 0, 0) + GL.glPolygonOffset(1, 1) + GL.glEnable(GL.GL_LINE_SMOOTH) + GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) + GL.glDepthFunc(GL.GL_LESS) + GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST) + GL.glLineWidth(1.0) + + projection = perspective(45.0, display_size[0] / float(display_size[1]), 2.0, 100.0) + GL.glUniformMatrix4fv(shader_data["constants"]["projection"], 1, False, projection) + + return shader_data, filled_cube_indices, outline_cube_indices + + +def draw_cube_modern(shader_data, filled_cube_indices, outline_cube_indices, rotation): + """ + Draw a cube in the 'modern' Open GL style, for post 3.1 versions of + open GL. + + :param shader_data: compile vertex & pixel shader data for drawing a cube. + :param filled_cube_indices: the indices to draw the 'filled' cube. + :param outline_cube_indices: the indices to draw the 'outline' cube. + :param rotation: the current rotations to apply. + """ + + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + + # Filled cube + GL.glDisable(GL.GL_BLEND) + GL.glEnable(GL.GL_DEPTH_TEST) + GL.glEnable(GL.GL_POLYGON_OFFSET_FILL) + GL.glUniform4f(shader_data["constants"]["colour_mul"], 1, 1, 1, 1) + GL.glUniform4f(shader_data["constants"]["colour_add"], 0, 0, 0, 0.0) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["filled"]) + GL.glDrawElements( + GL.GL_TRIANGLES, len(filled_cube_indices), GL.GL_UNSIGNED_INT, None + ) + + # Outlined cube + GL.glDisable(GL.GL_POLYGON_OFFSET_FILL) + GL.glEnable(GL.GL_BLEND) + GL.glUniform4f(shader_data["constants"]["colour_mul"], 0, 0, 0, 0.0) + GL.glUniform4f(shader_data["constants"]["colour_add"], 1, 1, 1, 1.0) + GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, shader_data["buffer"]["outline"]) + GL.glDrawElements(GL.GL_LINES, len(outline_cube_indices), GL.GL_UNSIGNED_INT, None) + + # Rotate cube + # rotation.theta += 1.0 # degrees + rotation.phi += 1.0 # degrees + # rotation.psi += 1.0 # degrees + model = eye(4, dtype=float32) + # rotate(model, rotation.theta, 0, 0, 1) + rotate(model, rotation.phi, 0, 1, 0) + rotate(model, rotation.psi, 1, 0, 0) + GL.glUniformMatrix4fv(shader_data["constants"]["model"], 1, False, model) - #setup the camera - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - gluPerspective(45.0,640/480.0,0.1,100.0) #setup lens - glTranslatef(0.0, 0.0, -3.0) #move back - glRotatef(25, 1, 0, 0) #orbit higher def main(): - "run the demo" - #initialize pygame and setup an opengl display - pygame.init() + """run the demo""" - fullscreen = True - pygame.display.set_mode((640,480), OPENGL|DOUBLEBUF|FULLSCREEN) + # initialize pygame and setup an opengl display + pg.init() - init_gl_stuff() + gl_version = (3, 0) # GL Version number (Major, Minor) + if USE_MODERN_GL: + gl_version = (3, 2) # GL Version number (Major, Minor) + + # By setting these attributes we can choose which Open GL Profile + # to use, profiles greater than 3.2 use a different rendering path + pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, gl_version[0]) + pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, gl_version[1]) + pg.display.gl_set_attribute( + pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE + ) + + fullscreen = False # start in windowed mode + + display_size = (640, 480) + pg.display.set_mode(display_size, pg.OPENGL | pg.DOUBLEBUF | pg.RESIZABLE) + + if USE_MODERN_GL: + gpu, f_indices, o_indices = init_gl_modern(display_size) + rotation = Rotation() + else: + init_gl_stuff_old() going = True while going: - #check for quit'n events - events = pygame.event.get() + # check for quit'n events + events = pg.event.get() for event in events: - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): + if event.type == pg.QUIT or ( + event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE + ): going = False - elif event.type == KEYDOWN: - if event.key == pygame.K_f: - if not fullscreen: - print("Changing to FULLSCREEN") - pygame.display.set_mode((640, 480), OPENGL | DOUBLEBUF | FULLSCREEN) - else: - print("Changing to windowed mode") - pygame.display.set_mode((640, 480), OPENGL | DOUBLEBUF) - fullscreen = not fullscreen - init_gl_stuff() - + elif event.type == pg.KEYDOWN and event.key == pg.K_f: + if not fullscreen: + print("Changing to FULLSCREEN") + pg.display.set_mode( + (640, 480), pg.OPENGL | pg.DOUBLEBUF | pg.FULLSCREEN + ) + else: + print("Changing to windowed mode") + pg.display.set_mode((640, 480), pg.OPENGL | pg.DOUBLEBUF) + fullscreen = not fullscreen + if gl_version[0] >= 4 or (gl_version[0] == 3 and gl_version[1] >= 2): + gpu, f_indices, o_indices = init_gl_modern(display_size) + rotation = Rotation() + else: + init_gl_stuff_old() - #clear screen and move camera - glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) + if USE_MODERN_GL: + draw_cube_modern(gpu, f_indices, o_indices, rotation) + else: + # clear screen and move camera + GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + # orbit camera around by 1 degree + GL.glRotatef(1, 0, 1, 0) + drawcube_old() - #orbit camera around by 1 degree - glRotatef(1, 0, 1, 0) + pg.display.flip() + pg.time.wait(10) - drawcube() - pygame.display.flip() - pygame.time.wait(10) + pg.quit() -if __name__ == '__main__': main() +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py b/venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py index ac39662cda3e402a4baa2b98d6146756b6231687..1b5a9431360c40c1a34b57c368fea8022f93540e 100644 --- a/venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py +++ b/venv/Lib/site-packages/pygame/examples/headless_no_windows_needed.py @@ -1,55 +1,50 @@ #!/usr/bin/env python -"""How to use pygame with no windowing system, like on headless servers. +""" pygame.examples.headless_no_windows_needed + +How to use pygame with no windowing system, like on headless servers. Thumbnail generation with scaling is an example of what you can do with pygame. -NOTE: the pygame scale function uses mmx/sse if available, and can be run +NOTE: the pygame scale function uses mmx/sse if available, and can be run in multiple threads. - """ -useage = """-scale inputimage outputimage new_width new_height +usage = """-scale inputimage outputimage new_width new_height eg. -scale in.png out.png 50 50 """ -import os, sys +import os +import sys -# set SDL to use the dummy NULL video driver, +# set SDL to use the dummy NULL video driver, # so it doesn't need a windowing system. os.environ["SDL_VIDEODRIVER"] = "dummy" +import pygame as pg -import pygame.transform - - -if 1: - #some platforms need to init the display for some parts of pygame. - import pygame.display - pygame.display.init() - screen = pygame.display.set_mode((1,1)) - +# Some platforms need to init the display for some parts of pg. +pg.display.init() +screen = pg.display.set_mode((1, 1)) def scaleit(fin, fout, w, h): - i = pygame.image.load(fin) + i = pg.image.load(fin) - if hasattr(pygame.transform, "smoothscale"): - scaled_image = pygame.transform.smoothscale(i, (w,h)) + if hasattr(pg.transform, "smoothscale"): + scaled_image = pg.transform.smoothscale(i, (w, h)) else: - scaled_image = pygame.transform.scale(i, (w,h)) - pygame.image.save(scaled_image, fout) + scaled_image = pg.transform.scale(i, (w, h)) + pg.image.save(scaled_image, fout) + def main(fin, fout, w, h): """smoothscale image file named fin as fout with new size (w,h)""" scaleit(fin, fout, w, h) + if __name__ == "__main__": if "-scale" in sys.argv: fin, fout, w, h = sys.argv[2:] - w, h = map(int, [w,h]) - main(fin, fout, w,h) + w, h = map(int, [w, h]) + main(fin, fout, w, h) else: - print (useage) - - - - + print(usage) diff --git a/venv/Lib/site-packages/pygame/examples/liquid.py b/venv/Lib/site-packages/pygame/examples/liquid.py index 6bfad0d6dee24a24794ab50253fc3050fed872f8..22431ba3e4bfaa364435efca7ae693407e29c5dd 100644 --- a/venv/Lib/site-packages/pygame/examples/liquid.py +++ b/venv/Lib/site-packages/pygame/examples/liquid.py @@ -1,6 +1,7 @@ #!/usr/bin/env python +""" pygame.examples.liquid -"""This examples demonstrates a simplish water effect of an +This example demonstrates a simplish water effect of an image. It attempts to create a hardware display surface that can use pageflipping for faster updates. Note that the colormap from the loaded GIF image is copied to the colormap for the @@ -9,57 +10,60 @@ display surface. This is based on the demo named F2KWarp by Brad Graham of Freedom2000 done in BlitzBasic. I was just translating the BlitzBasic code to pygame to compare the results. I didn't bother porting the text and -sound stuff, that's an easy enough challenge for the reader :]""" +sound stuff, that's an easy enough challenge for the reader :] +""" -import pygame, os -from pygame.locals import * +import pygame as pg +import os from math import sin import time main_dir = os.path.split(os.path.abspath(__file__))[0] + def main(): - #initialize and setup screen - pygame.init() - screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF) + # initialize and setup screen + pg.init() + screen = pg.display.set_mode((640, 480), pg.HWSURFACE | pg.DOUBLEBUF) - #load image and quadruple - imagename = os.path.join(main_dir, 'data', 'liquid.bmp') - bitmap = pygame.image.load(imagename) - bitmap = pygame.transform.scale2x(bitmap) - bitmap = pygame.transform.scale2x(bitmap) + # load image and quadruple + imagename = os.path.join(main_dir, "data", "liquid.bmp") + bitmap = pg.image.load(imagename) + bitmap = pg.transform.scale2x(bitmap) + bitmap = pg.transform.scale2x(bitmap) - #get the image and screen in the same format + # get the image and screen in the same format if screen.get_bitsize() == 8: screen.set_palette(bitmap.get_palette()) else: bitmap = bitmap.convert() - #prep some variables + # prep some variables anim = 0.0 - #mainloop + # mainloop xblocks = range(0, 640, 20) yblocks = range(0, 480, 20) - stopevents = QUIT, KEYDOWN, MOUSEBUTTONDOWN - while 1: - for e in pygame.event.get(): + stopevents = pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN + while True: + for e in pg.event.get(): if e.type in stopevents: return anim = anim + 0.02 for x in xblocks: - xpos = (x + (sin(anim + x * .01) * 15)) + 20 + xpos = (x + (sin(anim + x * 0.01) * 15)) + 20 for y in yblocks: - ypos = (y + (sin(anim + y * .01) * 15)) + 20 + ypos = (y + (sin(anim + y * 0.01) * 15)) + 20 screen.blit(bitmap, (x, y), (xpos, ypos, 20, 20)) - pygame.display.flip() + pg.display.flip() time.sleep(0.01) -if __name__ == '__main__': main() - +if __name__ == "__main__": + main() + pg.quit() """BTW, here is the code from the BlitzBasic example this was derived diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/JavaCompiling.plist b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/JavaCompiling.plist deleted file mode 100644 index 6e7346ac7237f8d8686a52d1880dac77ff783e3d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/JavaCompiling.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - JavaSourceSubpath - _MainMenu_EOArchive_English.java - - diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/_MainMenu_EOArchive_English.java b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/_MainMenu_EOArchive_English.java deleted file mode 100644 index 558bc784bd82419b08ef205fe5f2196f6d4f98cf..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/_MainMenu_EOArchive_English.java +++ /dev/null @@ -1,68 +0,0 @@ -// _MainMenu_EOArchive_English.java -// Generated by EnterpriseObjects palette at Tuesday, March 2, 2004 8:45:51 PM America/New_York - -import com.webobjects.eoapplication.*; -import com.webobjects.eocontrol.*; -import com.webobjects.eointerface.*; -import com.webobjects.eointerface.swing.*; -import com.webobjects.foundation.*; -import javax.swing.*; - -public class _MainMenu_EOArchive_English extends com.webobjects.eoapplication.EOArchive { - PygameAppDelegate _pygameAppDelegate0; - - public _MainMenu_EOArchive_English(Object owner, NSDisposableRegistry registry) { - super(owner, registry); - } - - protected void _construct() { - Object owner = _owner(); - EOArchive._ObjectInstantiationDelegate delegate = (owner instanceof EOArchive._ObjectInstantiationDelegate) ? (EOArchive._ObjectInstantiationDelegate)owner : null; - Object replacement; - - super._construct(); - - - if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "delegate")) != null)) { - _pygameAppDelegate0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (PygameAppDelegate)replacement; - _replacedObjects.setObjectForKey(replacement, "_pygameAppDelegate0"); - } else { - _pygameAppDelegate0 = (PygameAppDelegate)_registered(new PygameAppDelegate(), "PygameAppDelegate"); - } - } - - protected void _awaken() { - super._awaken(); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(_owner(), "unhideAllApplications", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(_owner(), "hide", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(_owner(), "hideOtherApplications", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(_owner(), "orderFrontStandardAboutPanel", ), "")); - - if (_replacedObjects.objectForKey("_pygameAppDelegate0") == null) { - _connect(_owner(), _pygameAppDelegate0, "delegate"); - } - - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(_owner(), "terminate", ), "")); - } - - protected void _init() { - super._init(); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "undo", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "paste", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "arrangeInFront", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "copy", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "showHelp", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "clearRecentDocuments", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "selectAll", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "performMiniaturize", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "toggleContinuousSpellChecking", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "print", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "cut", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "runPageLayout", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "checkSpelling", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "delete", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "redo", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "performClose", ), "")); - .addActionListener((com.webobjects.eointerface.swing.EOControlActionAdapter)_registered(new com.webobjects.eointerface.swing.EOControlActionAdapter(null, "showGuessPanel", ), "")); - } -} diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/classes.nib b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/classes.nib deleted file mode 100644 index 1c6603e878c15820797def1e893a6fbc2bc080fd..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/classes.nib +++ /dev/null @@ -1,13 +0,0 @@ -{ - IBClasses = ( - {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - { - ACTIONS = {}; - CLASS = PygameAppDelegate; - LANGUAGE = ObjC; - OUTLETS = {}; - SUPERCLASS = NSObject; - } - ); - IBVersion = 1; -} \ No newline at end of file diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/info.nib b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/info.nib deleted file mode 100644 index 7d939051732b74d0c9f4d55d2053524f9fbb4fbe..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/info.nib +++ /dev/null @@ -1,21 +0,0 @@ - - - - - IBDocumentLocation - 269 494 356 240 0 0 1600 1002 - IBEditorPositions - - 29 - 125 344 278 44 0 0 1600 1002 - - IBFramework Version - 349.0 - IBOpenObjects - - 29 - - IBSystem Version - 7D24 - - diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/keyedobjects.nib b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/keyedobjects.nib deleted file mode 100644 index 8ef64c01b9601afcbef17deafd8a64ce0a99dd93..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/MainMenu.nib/keyedobjects.nib and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/aliens.icns b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/aliens.icns deleted file mode 100644 index 6dbe10202d6f6c04a95475c5b51eb93aa81bb6f4..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/English.lproj/aliens.icns and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/README.txt b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/README.txt deleted file mode 100644 index 29573e6929ac25047904bcb08e561a149dd446b6..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/README.txt +++ /dev/null @@ -1,17 +0,0 @@ -********************************************************************* - THESE INSTRUCTIONS ARE ONLY FOR MAC OS X 10.3, AND WILL ONLY CREATE - STANDALONE BUNDLES FOR MAC OS X 10.3. THERE IS NO SUPPORT FOR - MAC OS X 10.2. - -Also works on 10.4 and 10.5 -********************************************************************* - - -Install py2app and its dependencies. - -easy_install py2app - - - -To create the bundle: - python setup.py py2app diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/aliens.py b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/aliens.py deleted file mode 100644 index 5a23e4180ae289bfeea624d6b5e2896839545778..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/aliens.py +++ /dev/null @@ -1,325 +0,0 @@ -#! /usr/bin/env python - -import random, os.path - -#import basic pygame modules -import pygame -from pygame.locals import * - -#see if we can load more than standard BMP -if not pygame.image.get_extended(): - raise SystemExit("Sorry, extended image module required") - - -#game constants -MAX_SHOTS = 2 #most player bullets onscreen -ALIEN_ODDS = 22 #chances a new alien appears -BOMB_ODDS = 60 #chances a new bomb will drop -ALIEN_RELOAD = 12 #frames between new aliens -SCREENRECT = Rect(0, 0, 640, 480) -SCORE = 0 - - -def load_image(file): - "loads an image, prepares it for play" - file = os.path.join('data', file) - try: - surface = pygame.image.load(file) - except pygame.error: - raise SystemExit('Could not load image "%s" %s'%(file, pygame.get_error())) - return surface.convert() - -def load_images(*files): - imgs = [] - for file in files: - imgs.append(load_image(file)) - return imgs - - -class dummysound: - def play(self): pass - -def load_sound(file): - if not pygame.mixer: return dummysound() - file = os.path.join('data', file) - try: - sound = pygame.mixer.Sound(file) - return sound - except pygame.error: - print ('Warning, unable to load,', file) - return dummysound() - - - -# each type of game object gets an init and an -# update function. the update function is called -# once per frame, and it is when each object should -# change it's current position and state. the Player -# object actually gets a "move" function instead of -# update, since it is passed extra information about -# the keyboard - - -class Player(pygame.sprite.Sprite): - speed = 10 - bounce = 24 - gun_offset = -11 - images = [] - def __init__(self): - pygame.sprite.Sprite.__init__(self, self.containers) - self.image = self.images[0] - self.rect = self.image.get_rect() - self.reloading = 0 - self.rect.centerx = SCREENRECT.centerx - self.rect.bottom = SCREENRECT.bottom - 1 - self.origtop = self.rect.top - self.facing = -1 - - def move(self, direction): - if direction: self.facing = direction - self.rect.move_ip(direction*self.speed, 0) - self.rect = self.rect.clamp(SCREENRECT) - if direction < 0: - self.image = self.images[0] - elif direction > 0: - self.image = self.images[1] - self.rect.top = self.origtop - (self.rect.left/self.bounce%2) - - def gunpos(self): - pos = self.facing*self.gun_offset + self.rect.centerx - return pos, self.rect.top - - -class Alien(pygame.sprite.Sprite): - speed = 13 - animcycle = 12 - images = [] - def __init__(self): - pygame.sprite.Sprite.__init__(self, self.containers) - self.image = self.images[0] - self.rect = self.image.get_rect() - self.facing = random.choice((-1,1)) * Alien.speed - self.frame = 0 - if self.facing < 0: - self.rect.right = SCREENRECT.right - - def update(self): - self.rect.move_ip(self.facing, 0) - if not SCREENRECT.contains(self.rect): - self.facing = -self.facing; - self.rect.top = self.rect.bottom + 1 - self.rect = self.rect.clamp(SCREENRECT) - self.frame = self.frame + 1 - self.image = self.images[self.frame/self.animcycle%3] - - -class Explosion(pygame.sprite.Sprite): - defaultlife = 12 - animcycle = 3 - images = [] - def __init__(self, actor): - pygame.sprite.Sprite.__init__(self, self.containers) - self.image = self.images[0] - self.rect = self.image.get_rect() - self.life = self.defaultlife - self.rect.center = actor.rect.center - - def update(self): - self.life = self.life - 1 - self.image = self.images[self.life/self.animcycle%2] - if self.life <= 0: self.kill() - - -class Shot(pygame.sprite.Sprite): - speed = -11 - images = [] - def __init__(self, pos): - pygame.sprite.Sprite.__init__(self, self.containers) - self.image = self.images[0] - self.rect = self.image.get_rect() - self.rect.midbottom = pos - - def update(self): - self.rect.move_ip(0, self.speed) - if self.rect.top <= 0: - self.kill() - - -class Bomb(pygame.sprite.Sprite): - speed = 9 - images = [] - def __init__(self, alien): - pygame.sprite.Sprite.__init__(self, self.containers) - self.image = self.images[0] - self.rect = self.image.get_rect() - self.rect.centerx = alien.rect.centerx - self.rect.bottom = alien.rect.bottom + 5 - - def update(self): - self.rect.move_ip(0, self.speed) - if self.rect.bottom >= 470: - Explosion(self) - self.kill() - - -class Score(pygame.sprite.Sprite): - def __init__(self): - pygame.sprite.Sprite.__init__(self) - self.font = pygame.font.Font(None, 20) - self.font.set_italic(1) - self.color = Color('white') - self.lastscore = -1 - self.update() - self.rect = self.image.get_rect().move(10, 450) - - def update(self): - if SCORE != self.lastscore: - self.lastscore = SCORE - msg = "Score: %d" % SCORE - self.image = self.font.render(msg, 0, self.color) - - - -def main(winstyle = 0): - # Initialize pygame - pygame.init() - if pygame.mixer and not pygame.mixer.get_init(): - print ('Warning, no sound') - pygame.mixer = None - - # Set the display mode - winstyle = 0 # |FULLSCREEN - bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32) - screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth) - - #Load images, assign to sprite classes - #(do this before the classes are used, after screen setup) - img = load_image('player1.gif') - Player.images = [img, pygame.transform.flip(img, 1, 0)] - img = load_image('explosion1.gif') - Explosion.images = [img, pygame.transform.flip(img, 1, 1)] - Alien.images = load_images('alien1.gif', 'alien2.gif', 'alien3.gif') - Bomb.images = [load_image('bomb.gif')] - Shot.images = [load_image('shot.gif')] - - #decorate the game window - icon = pygame.transform.scale(Alien.images[0], (32, 32)) - pygame.display.set_icon(icon) - pygame.display.set_caption('Pygame Aliens') - pygame.mouse.set_visible(0) - - #create the background, tile the bgd image - bgdtile = load_image('background.gif') - background = pygame.Surface(SCREENRECT.size) - for x in range(0, SCREENRECT.width, bgdtile.get_width()): - background.blit(bgdtile, (x, 0)) - screen.blit(background, (0,0)) - pygame.display.flip() - - #load the sound effects - boom_sound = load_sound('boom.wav') - shoot_sound = load_sound('car_door.wav') - if pygame.mixer and pygame.mixer.music: - music = os.path.join('data', 'house_lo.wav') - pygame.mixer.music.load(music) - pygame.mixer.music.play(-1) - - # Initialize Game Groups - aliens = pygame.sprite.Group() - shots = pygame.sprite.Group() - bombs = pygame.sprite.Group() - all = pygame.sprite.RenderUpdates() - lastalien = pygame.sprite.GroupSingle() - - #assign default groups to each sprite class - Player.containers = all - Alien.containers = aliens, all, lastalien - Shot.containers = shots, all - Bomb.containers = bombs, all - Explosion.containers = all - Score.containers = all - - #Create Some Starting Values - global score - alienreload = ALIEN_RELOAD - kills = 0 - clock = pygame.time.Clock() - - #initialize our starting sprites - global SCORE - player = Player() - Alien() #note, this 'lives' because it goes into a sprite group - if pygame.font: - all.add(Score()) - - - while player.alive(): - - #get input - for event in pygame.event.get(): - if event.type == QUIT or \ - (event.type == KEYDOWN and event.key == K_ESCAPE): - return - keystate = pygame.key.get_pressed() - - # clear/erase the last drawn sprites - all.clear(screen, background) - - #update all the sprites - all.update() - - #handle player input - direction = keystate[K_RIGHT] - keystate[K_LEFT] - player.move(direction) - firing = keystate[K_SPACE] - if not player.reloading and firing and len(shots) < MAX_SHOTS: - Shot(player.gunpos()) - shoot_sound.play() - player.reloading = firing - - # Create new alien - if alienreload: - alienreload = alienreload - 1 - elif not int(random.random() * ALIEN_ODDS): - Alien() - alienreload = ALIEN_RELOAD - - # Drop bombs - if lastalien and not int(random.random() * BOMB_ODDS): - Bomb(lastalien.sprite) - - # Detect collisions - for alien in pygame.sprite.spritecollide(player, aliens, 1): - boom_sound.play() - Explosion(alien) - Explosion(player) - SCORE = SCORE + 1 - player.kill() - - for alien in pygame.sprite.groupcollide(shots, aliens, 1, 1).keys(): - boom_sound.play() - Explosion(alien) - SCORE = SCORE + 1 - - for bomb in pygame.sprite.spritecollide(player, bombs, 1): - boom_sound.play() - Explosion(player) - Explosion(bomb) - player.kill() - - #draw the scene - dirty = all.draw(screen) - pygame.display.update(dirty) - - #cap the framerate - clock.tick(40) - - if pygame.mixer and pygame.mixer.music: - pygame.mixer.music.fadeout(1000) - pygame.time.wait(1000) - - - -#call the "main" function if running this script -if __name__ == '__main__': main() - diff --git a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/setup.py b/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/setup.py deleted file mode 100644 index 4c45283e17465f1742cfd83cffc17d5046dc13ea..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/aliens_app_example/setup.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Script for building the example. - -Usage: - python setup.py py2app -""" -from distutils.core import setup -import py2app - -NAME = 'aliens' -VERSION = '0.1' - -plist = dict( - CFBundleIconFile=NAME, - CFBundleName=NAME, - CFBundleShortVersionString=VERSION, - CFBundleGetInfoString=' '.join([NAME, VERSION]), - CFBundleExecutable=NAME, - CFBundleIdentifier='org.pygame.examples.aliens', -) - -setup( - data_files=['English.lproj', '../../data'], - app=[ - #dict(script="aliens_bootstrap.py", plist=plist), - dict(script="aliens.py", plist=plist), - ], -) diff --git a/venv/Lib/site-packages/pygame/examples/macosx/macfont.py b/venv/Lib/site-packages/pygame/examples/macosx/macfont.py deleted file mode 100644 index cce23ce7399c4bb4d6e4dfed065f535812ad9193..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/macosx/macfont.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -EXPERIMENTAL CODE! - -Here we load a .TTF font file, and display it in -a basic pygame window. It demonstrates several of the -Font object attributes. Nothing exciting in here, but -it makes a great example for basic window, event, and -font management. -""" - - -import pygame -import math -from pygame.locals import * -from pygame import Surface -from pygame.surfarray import blit_array, make_surface, pixels3d, pixels2d -import Numeric - -from Foundation import * -from AppKit import * - -def _getColor(color=None): - if color is None: - return NSColor.clearColor() - div255 = (0.00390625).__mul__ - if len(color) == 3: - color = tuple(color) + (255.0,) - return NSColor.colorWithDeviceRed_green_blue_alpha_(*map(div255, color)) - -def _getBitmapImageRep(size, hasAlpha=True): - width, height = map(int, size) - return NSBitmapImageRep.alloc().initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(None, width, height, 8, 4, hasAlpha, False, NSDeviceRGBColorSpace, width*4, 32) - -class SysFont(object): - def __init__(self, name, size): - self._font = NSFont.fontWithName_size_(name, size) - self._isBold = False - self._isOblique = False - self._isUnderline = False - self._family = name - self._size = size - self._setupFont() - - def _setupFont(self): - name = self._family - if self._isBold or self._isOblique: - name = '%s-%s%s' % ( - name, - self._isBold and 'Bold' or '', - self._isOblique and 'Oblique' or '') - self._font = NSFont.fontWithName_size_(name, self._size) - print (name, self._font) - if self._font is None: - if self._isBold: - self._font = NSFont.boldSystemFontOfSize(self._size) - else: - self._font = NSFont.systemFontOfSize_(self._size) - - def get_ascent(self): - return self._font.ascender() - - def get_descent(self): - return -self._font.descender() - - def get_bold(self): - return self._isBold - - def get_height(self): - return self._font.defaultLineHeightForFont() - - def get_italic(self): - return self._isOblique - - def get_linesize(self): - pass - - def get_underline(self): - return self._isUnderline - - def set_bold(self, isBold): - if isBold != self._isBold: - self._isBold = isBold - self._setupFont() - - def set_italic(self, isOblique): - if isOblique != self._isOblique: - self._isOblique = isOblique - self._setupFont() - - def set_underline(self, isUnderline): - self._isUnderline = isUnderline - - def size(self, text): - return tuple(map(int,map(math.ceil, NSString.sizeWithAttributes_(text, { - NSFontAttributeName: self._font, - NSUnderlineStyleAttributeName: self._isUnderline and 1.0 or None, - })))) - - def render(self, text, antialias, forecolor, backcolor=(0,0,0,255)): - size = self.size(text) - img = NSImage.alloc().initWithSize_(size) - img.lockFocus() - - NSString.drawAtPoint_withAttributes_(text, (0.0, 0.0), { - NSFontAttributeName: self._font, - NSUnderlineStyleAttributeName: self._isUnderline and 1.0 or None, - NSBackgroundColorAttributeName: backcolor and _getColor(backcolor) or None, - NSForegroundColorAttributeName: _getColor(forecolor), - }) - - rep = NSBitmapImageRep.alloc().initWithFocusedViewRect_(((0.0, 0.0), size)) - img.unlockFocus() - if rep.samplesPerPixel() == 4: - s = Surface(size, SRCALPHA|SWSURFACE, 32, [-1<<24,0xff<<16,0xff<<8,0xff]) - - a = Numeric.reshape(Numeric.fromstring(rep.bitmapData(), typecode=Numeric.Int32), (size[1], size[0])) - blit_array(s, Numeric.swapaxes(a,0,1)) - return s.convert_alpha() - -if __name__=='__main__': - pygame.init() - screen = pygame.display.set_mode((600, 600)) - s = SysFont('Gill Sans', 36) - s.set_italic(1) - s.set_underline(1) - done = False - surf = s.render('OS X Fonts!', True, (255,0,0,255), (0,0,0,0)) - screen.blit(surf, (0,0)) - screen.blit(surf, (2, 2)) - pygame.display.update() - while not done: - - for e in pygame.event.get(): - if e.type == QUIT or (e.type == KEYUP and e.key == K_ESCAPE): - done = True - break diff --git a/venv/Lib/site-packages/pygame/examples/mask.py b/venv/Lib/site-packages/pygame/examples/mask.py index c7067acf9c78f5e772412c0b2e9d959159b63f07..c0ec097397582b9002e669013cc4bf25698f9d34 100644 --- a/venv/Lib/site-packages/pygame/examples/mask.py +++ b/venv/Lib/site-packages/pygame/examples/mask.py @@ -1,176 +1,202 @@ #!/usr/bin/env python -"""A pgyame.mask collition detection example +""" +pygame.examples.mask -exports main() +A pygame.mask collision detection production. -This module can also be run as a stand-alone program, excepting -one or more image file names as command line arguments. -""" -import sys, random -import pygame, pygame.image, pygame.surface, pygame.time, pygame.display -def maskFromSurface(surface, threshold = 127): - #return pygame.mask.from_surface(surface, threshold) +Brought - mask = pygame.mask.Mask(surface.get_size()) - key = surface.get_colorkey() - if key: - for y in range(surface.get_height()): - for x in range(surface.get_width()): - if surface.get_at((x,y)) != key: - mask.set_at((x,y),1) - else: - for y in range(surface.get_height()): - for x in range (surface.get_width()): - if surface.get_at((x,y))[3] > threshold: - mask.set_at((x,y),1) - return mask + to + you + by + + the -def vadd(x,y): - return [x[0]+y[0],x[1]+y[1]] +pixels + 0000000000000 + and + 111111 -def vsub(x,y): - return [x[0]-y[0],x[1]-y[1]] -def vdot(x,y): - return x[0]*y[0]+x[1]*y[1] +This is 32 bits: + 11111111111111111111111111111111 + +There are 32 or 64 bits in a computer 'word'. +Rather than using one word for a pixel, +the mask module represents 32 or 64 pixels in one word. +As you can imagine, this makes things fast, and saves memory. + +Compute intensive things like collision detection, +and computer vision benefit greatly from this. + + +This module can also be run as a stand-alone program, excepting +one or more image file names as command line arguments. +""" + +import os +import random +import sys + +import pygame as pg + class Sprite: - def __init__(self, surface, mask = None): + """ + Moving Sprite demonstrating pixel-perfect collisions between pg.mask.Mask objects + """ + + def __init__(self, pos, vel, surface, mask=None): + """ + Positional arguments: + pos: Position of the sprite (sequence of 2 integers) + vel: Movement velocity of the sprite (sequence of 2 integers) + surface: Image (as a pg.Surface) of the sprite + mask: pg.mask.Mask object (optional) + """ self.surface = surface - if mask: + self.width, self.height = self.surface.get_size() + if mask is not None: self.mask = mask else: - self.mask = maskFromSurface(self.surface) - self.setPos([0,0]) - self.setVelocity([0,0]) - - def setPos(self,pos): - self.pos = [pos[0],pos[1]] - def setVelocity(self,vel): - self.vel = [vel[0],vel[1]] - def move(self,dr): - self.pos = vadd(self.pos,dr) - def kick(self,impulse): - self.vel[0] += impulse[0] - self.vel[1] += impulse[1] - - def collide(self,s): - """Test if the sprites are colliding and - resolve the collision in this case.""" - offset = [int(x) for x in vsub(s.pos,self.pos)] - overlap = self.mask.overlap_area(s.mask,offset) + self.mask = pg.mask.from_surface(self.surface) + + self.pos = pg.Vector2(pos) + self.vel = pg.Vector2(vel) + + def collide(self, sprite): + """ + Test if the sprites are colliding and + resolve the collision in this case. + + Positional arguments: + sprite: other sprite to test for collisions + """ + offset = [int(x) for x in sprite.pos - self.pos] + overlap = self.mask.overlap_area(sprite.mask, offset) if overlap == 0: return - """Calculate collision normal""" - nx = (self.mask.overlap_area(s.mask,(offset[0]+1,offset[1])) - - self.mask.overlap_area(s.mask,(offset[0]-1,offset[1]))) - ny = (self.mask.overlap_area(s.mask,(offset[0],offset[1]+1)) - - self.mask.overlap_area(s.mask,(offset[0],offset[1]-1))) - if nx == 0 and ny == 0: - """One sprite is inside another""" + # Calculate collision normal + + # Number of collisions + n_collisions = pg.Vector2( + # x axis + self.mask.overlap_area(sprite.mask, (offset[0] + 1, offset[1])) + - self.mask.overlap_area(sprite.mask, (offset[0] - 1, offset[1])), + # y axis + self.mask.overlap_area(sprite.mask, (offset[0], offset[1] + 1)) + - self.mask.overlap_area(sprite.mask, (offset[0], offset[1] - 1)), + ) + if n_collisions.x == 0 and n_collisions.y == 0: + # One sprite is inside another return - n = [nx,ny] - dv = vsub(s.vel,self.vel) - J = vdot(dv,n)/(2*vdot(n,n)) - if J > 0: - """Can scale up to 2*J here to get bouncy collisions""" - J *= 1.9 - self.kick([nx*J,ny*J]) - s.kick([-J*nx,-J*ny]) - return - """Separate the sprites""" - c1 = -overlap/vdot(n,n) - c2 = -c1/2 - self.move([c2*nx,c2*ny]) - s.move([(c1+c2)*nx,(c1+c2)*ny]) - - def update(self,dt): - self.pos[0] += dt*self.vel[0] - self.pos[1] += dt*self.vel[1] + + delta_vel = sprite.vel - self.vel + j = delta_vel * n_collisions / (2 * n_collisions * n_collisions) + if j > 0: + # Can scale up to 2*j here to get bouncy collisions + j *= 1.9 + self.vel += [n_collisions.x * j, n_collisions.y * j] + sprite.vel += [-j * n_collisions.x, -j * n_collisions.y] + + # # Separate the sprites + # c1 = -overlap / (n_collisions * n_collisions) + # c2 = -c1 / 2 + # self.pos += [c2 * n_collisions.x, c2 * n_collisions.y] + # sprite.pos += [(c1 + c2) * n_collisions.x, (c1 + c2) * n_collisions.y] + + def update(self): + """ + Move the sprite + """ + self.pos += self.vel def main(*args): - """Display multiple images bounce off each other using collition detection + """ + Display multiple images bounce off each other using collision detection Positional arguments: one or more image file names. - This pygame.masks demo will display multiple moving sprites bouncing + This pg.masks demo will display multiple moving sprites bouncing off each other. More than one sprite image can be provided. - """ - + if len(args) == 0: raise ValueError("Require at least one image file name: non given") - print ('Press any key to quit') - screen = pygame.display.set_mode((640,480)) - images = [] - masks = [] - for impath in args: - images.append(pygame.image.load(impath).convert_alpha()) - masks.append(maskFromSurface(images[-1])) - - numtimes = 10 - import time - t1 = time.time() - for x in range(numtimes): - m = maskFromSurface(images[-1]) - t2 = time.time() + pg.init() - print ("python maskFromSurface :%s" % (t2-t1)) + screen_size = (640, 480) + screen = pg.display.set_mode(screen_size) + clock = pg.time.Clock() - t1 = time.time() - for x in range(numtimes): - m = pygame.mask.from_surface(images[-1]) - t2 = time.time() - - print ("C pygame.mask.from_surface :%s" % (t2-t1)) + images = [] + masks = [] + for image_path in args: + images.append(pg.image.load(image_path).convert_alpha()) + masks.append(pg.mask.from_surface(images[-1])) sprites = [] for i in range(20): j = i % len(images) - s = Sprite(images[j],masks[j]) - s.setPos((random.uniform(0,screen.get_width()), - random.uniform(0,screen.get_height()))) - s.setVelocity((random.uniform(-5,5),random.uniform(-5,5))) - sprites.append(s) - pygame.time.set_timer(pygame.USEREVENT,33) - while 1: - event = pygame.event.wait() - if event.type == pygame.QUIT: - return - elif event.type == pygame.USEREVENT: - """Do both mechanics and screen update""" - screen.fill((240,220,100)) - for i in range(len(sprites)): - for j in range(i+1,len(sprites)): - sprites[i].collide(sprites[j]) - for s in sprites: - s.update(1) - if s.pos[0] < -s.surface.get_width()-3: - s.pos[0] = screen.get_width() - elif s.pos[0] > screen.get_width()+3: - s.pos[0] = -s.surface.get_width() - if s.pos[1] < -s.surface.get_height()-3: - s.pos[1] = screen.get_height() - elif s.pos[1] > screen.get_height()+3: - s.pos[1] = -s.surface.get_height() - screen.blit(s.surface,s.pos) - pygame.display.update() - elif event.type == pygame.KEYDOWN: - return - -if __name__ == '__main__': + sprite = Sprite( + pos=( + random.uniform(0, screen_size[0]), + random.uniform(0, screen_size[1]), + ), + vel=( + random.uniform(-5, 5), + random.uniform(-5, 5), + ), + surface=images[j], + mask=masks[j], + ) + sprites.append(sprite) + + while True: + for event in pg.event.get(): + if event.type in (pg.QUIT, pg.KEYDOWN): + return + + screen.fill((240, 220, 100)) + + for sprite_index, sprite in enumerate(sprites): + for other_sprite in sprites[sprite_index + 1 :]: + sprite.collide(other_sprite) + + sprite.update() + + # If the sprite is outside of the screen on the left + if sprite.pos.x < -sprite.width: + sprite.pos.x = screen_size[0] + # right + elif sprite.pos.x > screen_size[0]: + sprite.pos.x = -sprite.width + # top + if sprite.pos.y < -sprite.height: + sprite.pos.y = screen_size[1] + # down + elif sprite.pos.y > screen_size[1]: + sprite.pos.y = -sprite.height + + screen.blit(sprite.surface, sprite.pos) + + clock.tick(30) + pg.display.flip() + + +if __name__ == "__main__": if len(sys.argv) < 2: - print ('Usage: mask.py [ ...]') - print ('Let many copies of IMAGE(s) bounce against each other') - print ('Press any key to quit') + print("Usage: mask.py [ ...]") + print("Let many copies of IMAGE(s) bounce against each other") + print("Press any key to quit") + main_dir = os.path.split(os.path.abspath(__file__))[0] + main(os.path.join(main_dir, "data", "alien1.png")) + else: main(*sys.argv[1:]) - - - - + pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/midi.py b/venv/Lib/site-packages/pygame/examples/midi.py index 41d8e5b09fcadd7e291f0f178539878b26e8034a..f087ae6f943a4623320fba811d16e1d4c59c5b7c 100644 --- a/venv/Lib/site-packages/pygame/examples/midi.py +++ b/venv/Lib/site-packages/pygame/examples/midi.py @@ -1,24 +1,25 @@ #!/usr/bin/env python +""" pygame.examples.midi -"""Contains an example of midi input, and a separate example of midi output. +midi input, and a separate example of midi output. By default it runs the output example. -python midi.py --output -python midi.py --input +python -m pygame.examples.midi --output +python -m pygame.examples.midi --input +python -m pygame.examples.midi --input """ +from dataclasses import dataclass import sys import os +from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union -import pygame +import pygame as pg import pygame.midi -from pygame.locals import * -try: # Ensure set available for output example - set -except NameError: - from sets import Set as set +# black and white piano keys use b/w color values directly +BACKGROUNDCOLOR = "slategray" def print_device_info(): @@ -26,8 +27,9 @@ def print_device_info(): _print_device_info() pygame.midi.quit() + def _print_device_info(): - for i in range( pygame.midi.get_count() ): + for i in range(pygame.midi.get_count()): r = pygame.midi.get_device_info(i) (interf, name, input, output, opened) = r @@ -37,45 +39,39 @@ def _print_device_info(): if output: in_out = "(output)" - print ("%2i: interface :%s:, name :%s:, opened :%s: %s" % - (i, interf, name, opened, in_out)) - - + print( + "%2i: interface :%s:, name :%s:, opened :%s: %s" + % (i, interf, name, opened, in_out) + ) -def input_main(device_id = None): - pygame.init() - pygame.fastevent.init() - event_get = pygame.fastevent.get - event_post = pygame.fastevent.post +def input_main(device_id=None): + pg.init() pygame.midi.init() _print_device_info() - if device_id is None: input_id = pygame.midi.get_default_input_id() else: input_id = device_id - print ("using input_id :%s:" % input_id) - i = pygame.midi.Input( input_id ) - - pygame.display.set_mode((1,1)) - + print(f"using input_id :{input_id}:") + i = pygame.midi.Input(input_id) + pg.display.set_mode((1, 1)) going = True while going: - events = event_get() + events = pygame.event.get() for e in events: - if e.type in [QUIT]: + if e.type in [pg.QUIT]: going = False - if e.type in [KEYDOWN]: + if e.type in [pg.KEYDOWN]: going = False if e.type in [pygame.midi.MIDIIN]: - print (e) + print(e) if i.poll(): midi_events = i.read(10) @@ -83,14 +79,13 @@ def input_main(device_id = None): midi_evs = pygame.midi.midis2events(midi_events, i.device_id) for m_e in midi_evs: - event_post( m_e ) + pygame.event.post(m_e) del i pygame.midi.quit() - -def output_main(device_id = None): +def output_main(device_id=None): """Execute a musical keyboard example for the Church Organ instrument This is a piano keyboard example, with a two octave keyboard, starting at @@ -106,7 +101,7 @@ def output_main(device_id = None): Default Midi output, no device_id given, is to the default output device for the computer. - + """ # A note to new pygamers: @@ -131,25 +126,46 @@ def output_main(device_id = None): # to ensure that midi is properly shut down, it is best to do it # explicitly. A try/finally statement is the safest way to do this. # - GRAND_PIANO = 0 + + # GRAND_PIANO = 0 CHURCH_ORGAN = 19 instrument = CHURCH_ORGAN - #instrument = GRAND_PIANO + # instrument = GRAND_PIANO start_note = 53 # F3 (white key note), start_note != 0 n_notes = 24 # Two octaves (14 white keys) - - bg_color = Color('slategray') - - key_mapping = make_key_mapping([K_TAB, K_1, K_q, K_2, K_w, K_3, K_e, K_r, - K_5, K_t, K_6, K_y, K_u, K_8, K_i, K_9, - K_o, K_0, K_p, K_LEFTBRACKET, K_EQUALS, - K_RIGHTBRACKET, K_BACKSPACE, K_BACKSLASH], - start_note) - - - pygame.init() + key_mapping = make_key_mapping( + [ + pg.K_TAB, + pg.K_1, + pg.K_q, + pg.K_2, + pg.K_w, + pg.K_3, + pg.K_e, + pg.K_r, + pg.K_5, + pg.K_t, + pg.K_6, + pg.K_y, + pg.K_u, + pg.K_8, + pg.K_i, + pg.K_9, + pg.K_o, + pg.K_0, + pg.K_p, + pg.K_LEFTBRACKET, + pg.K_EQUALS, + pg.K_RIGHTBRACKET, + pg.K_BACKSPACE, + pg.K_BACKSLASH, + ], + start_note, + ) + + pg.init() pygame.midi.init() _print_device_info() @@ -159,53 +175,49 @@ def output_main(device_id = None): else: port = device_id - print ("using output_id :%s:" % port) - - + print(f"using output_id :{port}:") midi_out = pygame.midi.Output(port, 0) try: midi_out.set_instrument(instrument) keyboard = Keyboard(start_note, n_notes) - screen = pygame.display.set_mode(keyboard.rect.size) - screen.fill(bg_color) - pygame.display.flip() + screen = pg.display.set_mode(keyboard.rect.size) + screen.fill(BACKGROUNDCOLOR) + pg.display.flip() - background = pygame.Surface(screen.get_size()) - background.fill(bg_color) + background = pg.Surface(screen.get_size()) + background.fill(BACKGROUNDCOLOR) dirty_rects = [] keyboard.draw(screen, background, dirty_rects) - pygame.display.update(dirty_rects) + pg.display.update(dirty_rects) - regions = pygame.Surface(screen.get_size()) # initial color (0,0,0) + regions = pg.Surface(screen.get_size()) # initial color (0,0,0) keyboard.map_regions(regions) - pygame.event.set_blocked(MOUSEMOTION) - repeat = 1 + pg.event.set_blocked(pg.MOUSEMOTION) mouse_note = 0 on_notes = set() - while 1: - update_rects = None - e = pygame.event.wait() - if e.type == pygame.MOUSEBUTTONDOWN: - mouse_note, velocity, __, __ = regions.get_at(e.pos) + while True: + e = pg.event.wait() + if e.type == pg.MOUSEBUTTONDOWN: + mouse_note, velocity, __, __ = regions.get_at(e.pos) if mouse_note and mouse_note not in on_notes: keyboard.key_down(mouse_note) midi_out.note_on(mouse_note, velocity) on_notes.add(mouse_note) else: mouse_note = 0 - elif e.type == pygame.MOUSEBUTTONUP: + elif e.type == pg.MOUSEBUTTONUP: if mouse_note: midi_out.note_off(mouse_note) keyboard.key_up(mouse_note) on_notes.remove(mouse_note) mouse_note = 0 - elif e.type == pygame.QUIT: + elif e.type == pg.QUIT: break - elif e.type == pygame.KEYDOWN: - if e.key == pygame.K_ESCAPE: + elif e.type == pg.KEYDOWN: + if e.key == pg.K_ESCAPE: break try: note, velocity = key_mapping[e.key] @@ -216,7 +228,7 @@ def output_main(device_id = None): keyboard.key_down(note) midi_out.note_on(note, velocity) on_notes.add(note) - elif e.type == pygame.KEYUP: + elif e.type == pg.KEYUP: try: note, __ = key_mapping[e.key] except KeyError: @@ -229,27 +241,28 @@ def output_main(device_id = None): dirty_rects = [] keyboard.draw(screen, background, dirty_rects) - pygame.display.update(dirty_rects) + pg.display.update(dirty_rects) finally: del midi_out pygame.midi.quit() -def make_key_mapping(key_list, start_note): + +def make_key_mapping(keys, start_note): """Return a dictionary of (note, velocity) by computer keyboard key code""" - mapping = {} - for i in range(len(key_list)): - mapping[key_list[i]] = (start_note + i, 127) + for i, key in enumerate(keys): + mapping[key] = (start_note + i, 127) return mapping -class NullKey(object): + +class NullKey: """A dummy key that ignores events passed to it by other keys A NullKey instance is the left key instance used by default for the left most keyboard key. """ - + def _right_white_down(self): pass @@ -262,9 +275,173 @@ class NullKey(object): def _right_black_up(self): pass + null_key = NullKey() -def key_class(updates, image_strip, image_rects, is_white_key=True): + +@dataclass +class KeyData: + """Used for passing in data to subclasses of the Key class.""" + + is_white_key: bool + c_width: int + c_height: int + c_down_state_initial: int + c_down_state_rect_initial: pg.Rect + c_notify_down_method: str + c_notify_up_method: str + c_updates: Set[Any] + c_event_down: Dict[int, Tuple[int, pg.Rect]] + c_event_up: Dict[int, Tuple[int, pg.Rect]] + c_image_strip: pg.Surface + c_event_right_white_down: Dict[int, Tuple[int, Union[pg.Rect, None]]] + c_event_right_white_up: Dict[int, Tuple[int, Union[pg.Rect, None]]] + c_event_right_black_down: Dict[int, Tuple[int, Union[pg.Rect, None]]] + c_event_right_black_up: Dict[int, Tuple[int, Union[pg.Rect, None]]] + + +class Key: + """A key widget, maintains key state and draws the key's image + + Constructor arguments: + ident - A unique key identifier. Any immutable type suitable as a key. + posn - The location of the key on the display surface. + key_left - Optional, the adjacent white key to the left. Changes in + up and down state are propagated to that key. + + A key has an associated position and state. Related to state is the + image drawn. State changes are managed with method calls, one method + per event type. The up and down event methods are public. Other + internal methods are for passing on state changes to the key_left + key instance. + + """ + + key_data: KeyData + + def __init__(self, ident, posn, key_left=None): + """Return a new Key instance + + The initial state is up, with all adjacent keys to the right also + up. + + """ + if key_left is None: + key_left = null_key + rect = pg.Rect(posn[0], posn[1], self.key_data.c_width, self.key_data.c_height) + self.rect = rect + self._state = self.key_data.c_down_state_initial + self._source_rect = self.key_data.c_down_state_rect_initial + self._ident = ident + self._hash = hash(ident) + self._notify_down = getattr(key_left, self.key_data.c_notify_down_method) + self._notify_up = getattr(key_left, self.key_data.c_notify_up_method) + self._key_left = key_left + self._background_rect = pg.Rect( + rect.left, rect.bottom - 10, self.key_data.c_width, 10 + ) + self.key_data.c_updates.add(self) + self.is_white = self.key_data.is_white_key + + def down(self): + """Signal that this key has been depressed (is down)""" + + self._state, source_rect = self.key_data.c_event_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + self._notify_down() + + def up(self): + """Signal that this key has been released (is up)""" + + self._state, source_rect = self.key_data.c_event_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + self._notify_up() + + def _right_white_down(self): + """Signal that the adjacent white key has been depressed + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_white_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def _right_white_up(self): + """Signal that the adjacent white key has been released + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_white_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def _right_black_down(self): + """Signal that the adjacent black key has been depressed + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_black_down[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def _right_black_up(self): + """Signal that the adjacent black key has been released + + This method is for internal propagation of events between + key instances. + + """ + + self._state, source_rect = self.key_data.c_event_right_black_up[self._state] + if source_rect is not None: + self._source_rect = source_rect + self.key_data.c_updates.add(self) + + def __eq__(self, other): + """True if same identifiers""" + + return self._ident == other._ident + + def __hash__(self): + """Return the immutable hash value""" + + return self._hash + + def __str__(self): + """Return the key's identifier and position as a string""" + + return "" % (self._ident, self.rect.top, self.rect.left) + + def draw(self, surf, background, dirty_rects): + """Redraw the key on the surface surf + + The background is redrawn. The altered region is added to the + dirty_rects list. + + """ + + surf.blit(background, self._background_rect, self._background_rect) + surf.blit(self.key_data.c_image_strip, self.rect, self._source_rect) + dirty_rects.append(self.rect) + + +def key_class(updates, image_strip, image_rects: List[pg.Rect], is_white_key=True): """Return a keyboard key widget class Arguments: @@ -303,7 +480,7 @@ def key_class(updates, image_strip, image_rects, is_white_key=True): # There can be up to eight key states, representing all permutations # of the three fundamental states of self up/down, adjacent white # right up/down, adjacent black up/down. - # + # down_state_none = 0 down_state_self = 1 down_state_white = down_state_self << 1 @@ -317,21 +494,20 @@ def key_class(updates, image_strip, image_rects, is_white_key=True): # c_down_state_initial = down_state_none c_down_state_rect_initial = image_rects[0] - c_down_state_self = down_state_self c_updates = updates c_image_strip = image_strip c_width, c_height = image_rects[0].size # A key propagates its up/down state change to the adjacent white key on # the left by calling the adjacent key's _right_black_down or - # _right_white_down method. + # _right_white_down method. # if is_white_key: - key_color = 'white' + key_color = "white" else: - key_color = 'black' - c_notify_down_method = "_right_%s_down" % key_color - c_notify_up_method = "_right_%s_up" % key_color + key_color = "black" + c_notify_down_method = f"_right_{key_color}_down" + c_notify_up_method = f"_right_{key_color}_up" # Images: # @@ -349,211 +525,99 @@ def key_class(updates, image_strip, image_rects, is_white_key=True): # Each 'c_event' dictionary maps the current key state to a new key state, # along with corresponding image, for the related event. If no redrawing # is required for the state change then the image rect is simply None. - # - c_event_down = {down_state_none: (down_state_self, image_rects[1])} - c_event_up = {down_state_self: (down_state_none, image_rects[0])} - c_event_right_white_down = { + # + c_event_down: Dict[int, Tuple[int, pygame.Rect]] = { + down_state_none: (down_state_self, image_rects[1]) + } + c_event_up: Dict[int, Tuple[int, pygame.Rect]] = { + down_state_self: (down_state_none, image_rects[0]) + } + c_event_right_white_down: Dict[int, Tuple[int, Union[pygame.Rect, None]]] = { down_state_none: (down_state_none, None), - down_state_self: (down_state_self, None)} + down_state_self: (down_state_self, None), + } c_event_right_white_up = c_event_right_white_down.copy() c_event_right_black_down = c_event_right_white_down.copy() c_event_right_black_up = c_event_right_white_down.copy() if len(image_rects) > 2: - c_event_down[down_state_white] = ( - down_state_self_white, image_rects[2]) + c_event_down[down_state_white] = (down_state_self_white, image_rects[2]) c_event_up[down_state_self_white] = (down_state_white, image_rects[0]) c_event_right_white_down[down_state_none] = (down_state_white, None) c_event_right_white_down[down_state_self] = ( - down_state_self_white, image_rects[2]) + down_state_self_white, + image_rects[2], + ) c_event_right_white_up[down_state_white] = (down_state_none, None) c_event_right_white_up[down_state_self_white] = ( - down_state_self, image_rects[1]) - c_event_right_black_down[down_state_white] = ( - down_state_white, None) - c_event_right_black_down[down_state_self_white] = ( - down_state_self_white, None) - c_event_right_black_up[down_state_white] = ( - down_state_white, None) - c_event_right_black_up[down_state_self_white] = ( - down_state_self_white, None) + down_state_self, + image_rects[1], + ) + c_event_right_black_down[down_state_white] = (down_state_white, None) + c_event_right_black_down[down_state_self_white] = (down_state_self_white, None) + c_event_right_black_up[down_state_white] = (down_state_white, None) + c_event_right_black_up[down_state_self_white] = (down_state_self_white, None) if len(image_rects) > 3: - c_event_down[down_state_black] = ( - down_state_self_black, image_rects[4]) + c_event_down[down_state_black] = (down_state_self_black, image_rects[4]) c_event_down[down_state_white_black] = (down_state_all, image_rects[5]) c_event_up[down_state_self_black] = (down_state_black, image_rects[3]) c_event_up[down_state_all] = (down_state_white_black, image_rects[3]) - c_event_right_white_down[down_state_black] = ( - down_state_white_black, None) + c_event_right_white_down[down_state_black] = (down_state_white_black, None) c_event_right_white_down[down_state_self_black] = ( - down_state_all, image_rects[5]) - c_event_right_white_up[down_state_white_black] = ( - down_state_black, None) - c_event_right_white_up[down_state_all] = ( - down_state_self_black, image_rects[4]) - c_event_right_black_down[down_state_none] = ( - down_state_black, image_rects[3]) + down_state_all, + image_rects[5], + ) + c_event_right_white_up[down_state_white_black] = (down_state_black, None) + c_event_right_white_up[down_state_all] = (down_state_self_black, image_rects[4]) + c_event_right_black_down[down_state_none] = (down_state_black, image_rects[3]) c_event_right_black_down[down_state_self] = ( - down_state_self_black, image_rects[4]) + down_state_self_black, + image_rects[4], + ) c_event_right_black_down[down_state_white] = ( - down_state_white_black, image_rects[3]) + down_state_white_black, + image_rects[3], + ) c_event_right_black_down[down_state_self_white] = ( - down_state_all, image_rects[5]) - c_event_right_black_up[down_state_black] = ( - down_state_none, image_rects[0]) + down_state_all, + image_rects[5], + ) + c_event_right_black_up[down_state_black] = (down_state_none, image_rects[0]) c_event_right_black_up[down_state_self_black] = ( - down_state_self, image_rects[1]) + down_state_self, + image_rects[1], + ) c_event_right_black_up[down_state_white_black] = ( - down_state_white, image_rects[0]) - c_event_right_black_up[down_state_all] = ( - down_state_self_white, image_rects[2]) - - - class Key(object): - """A key widget, maintains key state and draws the key's image - - Constructor arguments: - ident - A unique key identifier. Any immutable type suitable as a key. - posn - The location of the key on the display surface. - key_left - Optional, the adjacent white key to the left. Changes in - up and down state are propagated to that key. - - A key has an associated position and state. Related to state is the - image drawn. State changes are managed with method calls, one method - per event type. The up and down event methods are public. Other - internal methods are for passing on state changes to the key_left - key instance. - - """ - - is_white = is_white_key - - def __init__(self, ident, posn, key_left = None): - """Return a new Key instance - - The initial state is up, with all adjacent keys to the right also - up. - - """ - if key_left is None: - key_left = null_key - rect = Rect(posn[0], posn[1], c_width, c_height) - self.rect = rect - self._state = c_down_state_initial - self._source_rect = c_down_state_rect_initial - self._ident = ident - self._hash = hash(ident) - self._notify_down = getattr(key_left, c_notify_down_method) - self._notify_up = getattr(key_left, c_notify_up_method) - self._key_left = key_left - self._background_rect = Rect(rect.left, rect.bottom - 10, - c_width, 10) - c_updates.add(self) - - def down(self): - """Signal that this key has been depressed (is down)""" - - self._state, source_rect = c_event_down[self._state] - if source_rect is not None: - self._source_rect = source_rect - c_updates.add(self) - self._notify_down() - - def up(self): - """Signal that this key has been released (is up)""" - - self._state, source_rect = c_event_up[self._state] - if source_rect is not None: - self._source_rect = source_rect - c_updates.add(self) - self._notify_up() - - def _right_white_down(self): - """Signal that the adjacent white key has been depressed - - This method is for internal propagation of events between - key instances. - - """ - - self._state, source_rect = c_event_right_white_down[self._state] - if source_rect is not None: - self._source_rect = source_rect - c_updates.add(self) - - def _right_white_up(self): - """Signal that the adjacent white key has been released - - This method is for internal propagation of events between - key instances. - - """ - - self._state, source_rect = c_event_right_white_up[self._state] - if source_rect is not None: - self._source_rect = source_rect - c_updates.add(self) - - def _right_black_down(self): - """Signal that the adjacent black key has been depressed - - This method is for internal propagation of events between - key instances. - - """ - - self._state, source_rect = c_event_right_black_down[self._state] - if source_rect is not None: - self._source_rect = source_rect - c_updates.add(self) - - def _right_black_up(self): - """Signal that the adjacent black key has been released - - This method is for internal propagation of events between - key instances. - - """ - - self._state, source_rect = c_event_right_black_up[self._state] - if source_rect is not None: - self._source_rect = source_rect - c_updates.add(self) - - def __eq__(self, other): - """True if same identifiers""" - - return self._ident == other._ident - - def __hash__(self): - """Return the immutable hash value""" - - return self._hash - - def __str__(self): - """Return the key's identifier and position as a string""" - - return ("" % - (self._ident, self.rect.top, self.rect.left)) - - def draw(self, surf, background, dirty_rects): - """Redraw the key on the surface surf - - The background is redrawn. The altered region is added to the - dirty_rects list. - - """ - - surf.blit(background, self._background_rect, self._background_rect) - surf.blit(c_image_strip, self.rect, self._source_rect) - dirty_rects.append(self.rect) - - return Key - -def key_images(): + down_state_white, + image_rects[0], + ) + c_event_right_black_up[down_state_all] = (down_state_self_white, image_rects[2]) + + class OurKey(Key): + key_data = KeyData( + is_white_key, + c_width, + c_height, + c_down_state_initial, + c_down_state_rect_initial, + c_notify_down_method, + c_notify_up_method, + c_updates, + c_event_down, + c_event_up, + c_image_strip, + c_event_right_white_down, + c_event_right_white_up, + c_event_right_black_down, + c_event_right_black_up, + ) + + return OurKey + + +def key_images() -> Tuple[pg.Surface, Dict[str, pg.Rect]]: """Return a keyboard keys image strip and a mapping of image locations - The return tuple is a surface and a dictionary of rects mapped to key - type. + The return tuple is a pygame.Surface and a dictionary keyed by key name and valued by a pygame.Rect. This function encapsulates the constants relevant to the keyboard image file. There are five key types. One is the black key. The other four @@ -566,31 +630,47 @@ def key_images(): """ my_dir = os.path.split(os.path.abspath(__file__))[0] - strip_file = os.path.join(my_dir, 'data', 'midikeys.png') + strip_file = os.path.join(my_dir, "data", "midikeys.png") white_key_width = 42 white_key_height = 160 black_key_width = 22 black_key_height = 94 - strip = pygame.image.load(strip_file) + strip = pg.image.load(strip_file) names = [ - 'black none', 'black self', - 'white none', 'white self', 'white self-white', - 'white-left none', 'white-left self', 'white-left black', - 'white-left self-black', 'white-left self-white', 'white-left all', - 'white-center none', 'white-center self', - 'white-center black', 'white-center self-black', - 'white-center self-white', 'white-center all', - 'white-right none', 'white-right self', 'white-right self-white'] + "black none", + "black self", + "white none", + "white self", + "white self-white", + "white-left none", + "white-left self", + "white-left black", + "white-left self-black", + "white-left self-white", + "white-left all", + "white-center none", + "white-center self", + "white-center black", + "white-center self-black", + "white-center self-white", + "white-center all", + "white-right none", + "white-right self", + "white-right self-white", + ] rects = {} for i in range(2): - rects[names[i]] = Rect(i * white_key_width, 0, - black_key_width, black_key_height) + rects[names[i]] = pg.Rect( + i * white_key_width, 0, black_key_width, black_key_height + ) for i in range(2, len(names)): - rects[names[i]] = Rect(i * white_key_width, 0, - white_key_width, white_key_height) + rects[names[i]] = pg.Rect( + i * white_key_width, 0, white_key_width, white_key_height + ) return strip, rects -class Keyboard(object): + +class Keyboard: """Musical keyboard widget Constructor arguments: @@ -605,67 +685,78 @@ class Keyboard(object): _image_strip, _rects = key_images() - white_key_width, white_key_height = _rects['white none'].size - black_key_width, black_key_height = _rects['black none'].size + white_key_width, white_key_height = _rects["white none"].size + black_key_width, black_key_height = _rects["black none"].size - _updates = set() + _updates: Set[Any] = set() # There are five key classes, representing key shape: # black key (BlackKey), plain white key (WhiteKey), white key to the left # of a black key (WhiteKeyLeft), white key between two black keys # (WhiteKeyCenter), and white key to the right of a black key # (WhiteKeyRight). - BlackKey = key_class(_updates, - _image_strip, - [_rects['black none'], _rects['black self']], - False) - WhiteKey = key_class(_updates, - _image_strip, - [_rects['white none'], - _rects['white self'], - _rects['white self-white']]) - WhiteKeyLeft = key_class(_updates, - _image_strip, - [_rects['white-left none'], - _rects['white-left self'], - _rects['white-left self-white'], - _rects['white-left black'], - _rects['white-left self-black'], - _rects['white-left all']]) - WhiteKeyCenter = key_class(_updates, - _image_strip, - [_rects['white-center none'], - _rects['white-center self'], - _rects['white-center self-white'], - _rects['white-center black'], - _rects['white-center self-black'], - _rects['white-center all']]) - WhiteKeyRight = key_class(_updates, - _image_strip, - [_rects['white-right none'], - _rects['white-right self'], - _rects['white-right self-white']]) - - def __init__(self, start_note, n_notes): + BlackKey = key_class( + _updates, _image_strip, [_rects["black none"], _rects["black self"]], False + ) + WhiteKey = key_class( + _updates, + _image_strip, + [_rects["white none"], _rects["white self"], _rects["white self-white"]], + ) + WhiteKeyLeft = key_class( + _updates, + _image_strip, + [ + _rects["white-left none"], + _rects["white-left self"], + _rects["white-left self-white"], + _rects["white-left black"], + _rects["white-left self-black"], + _rects["white-left all"], + ], + ) + WhiteKeyCenter = key_class( + _updates, + _image_strip, + [ + _rects["white-center none"], + _rects["white-center self"], + _rects["white-center self-white"], + _rects["white-center black"], + _rects["white-center self-black"], + _rects["white-center all"], + ], + ) + WhiteKeyRight = key_class( + _updates, + _image_strip, + [ + _rects["white-right none"], + _rects["white-right self"], + _rects["white-right self-white"], + ], + ) + + def __init__(self, start_note: int, n_notes: int): """Return a new Keyboard instance with n_note keys""" self._start_note = start_note self._end_note = start_note + n_notes - 1 self._add_keys() - def _add_keys(self): + def _add_keys(self) -> None: """Populate the keyboard with key instances Set the _keys and rect attributes. - + """ # Keys are entered in a list, where index is Midi note. Since there are - # only 128 possible Midi notes the list length is managable. Unassigned + # only 128 possible Midi notes the list length is manageable. Unassigned # note positions should never be accessed, so are set None to ensure # the bug is quickly detected. # - key_map = [None] * 128 + key_map: list[Key | Literal[None]] = [None] * 128 start_note = self._start_note end_note = self._end_note @@ -689,23 +780,23 @@ class Keyboard(object): if note == end_note or is_white_key(note + 1): key = self.WhiteKeyRight(ident, (x, y), prev_white_key) else: - key = self.WhiteKeyCenter(ident, - (x, y), - prev_white_key) + key = self.WhiteKeyCenter(ident, (x, y), prev_white_key) is_prev_white = True x += self.white_key_width prev_white_key = key else: - key = self.BlackKey(ident, - (x - black_offset, y), - prev_white_key) + key = self.BlackKey(ident, (x - black_offset, y), prev_white_key) is_prev_white = False key_map[note] = key self._keys = key_map - kb_width = key_map[self._end_note].rect.right + the_key = key_map[self._end_note] + if the_key is None: + kb_width = 0 + else: + kb_width = the_key.rect.right kb_height = self.white_key_height - self.rect = Rect(0, 0, kb_width, kb_height) + self.rect = pg.Rect(0, 0, kb_width, kb_height) def map_regions(self, regions): """Draw the key regions onto surface regions. @@ -723,29 +814,33 @@ class Keyboard(object): black_keys = [] for note in range(self._start_note, self._end_note + 1): key = self._keys[note] - if key.is_white: + if key is not None and key.is_white: fill_region(regions, note, key.rect, cutoff) else: black_keys.append((note, key)) for note, key in black_keys: - fill_region(regions, note, key.rect, cutoff) + if key is not None: + fill_region(regions, note, key.rect, cutoff) def draw(self, surf, background, dirty_rects): """Redraw all altered keyboard keys""" - + changed_keys = self._updates while changed_keys: changed_keys.pop().draw(surf, background, dirty_rects) def key_down(self, note): """Signal a key down event for note""" - - self._keys[note].down() + key = self._keys[note] + if key is not None: + key.down() def key_up(self, note): """Signal a key up event for note""" + key = self._keys[note] + if key is not None: + key.up() - self._keys[note].up() def fill_region(regions, note, rect, cutoff): """Fill the region defined by rect with a (note, velocity, 0) color @@ -761,27 +856,40 @@ def fill_region(regions, note, rect, cutoff): if cutoff is None: cutoff = height delta_height = cutoff // 3 - regions.fill((note, 42, 0), - (x, y, width, delta_height)) - regions.fill((note, 84, 0), - (x, y + delta_height, width, delta_height)) - regions.fill((note, 127, 0), - (x, y + 2 * delta_height, width, height - 2 * delta_height)) - + regions.fill((note, 42, 0), (x, y, width, delta_height)) + regions.fill((note, 84, 0), (x, y + delta_height, width, delta_height)) + regions.fill( + (note, 127, 0), (x, y + 2 * delta_height, width, height - 2 * delta_height) + ) + + def is_white_key(note): """True if note is represented by a white key""" - - key_pattern = [True, False, True, True, False, True, - False, True, True, False, True, False] + + key_pattern = [ + True, + False, + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ] return key_pattern[(note - 21) % len(key_pattern)] def usage(): - print ("--input [device_id] : Midi message logger") - print ("--output [device_id] : Midi piano keyboard") - print ("--list : list available midi devices") + print("--input [device_id] : Midi message logger") + print("--output [device_id] : Midi piano keyboard") + print("--list : list available midi devices") + -def main(mode='output', device_id=None): +def main(mode="output", device_id=None): """Run a Midi example Arguments: @@ -794,24 +902,24 @@ def main(mode='output', device_id=None): """ - if mode == 'input': + if mode == "input": input_main(device_id) - elif mode == 'output': + elif mode == "output": output_main(device_id) - elif mode == 'list': + elif mode == "list": print_device_info() else: - raise ValueError("Unknown mode option '%s'" % mode) - -if __name__ == '__main__': + raise ValueError(f"Unknown mode option '{mode}'") + +if __name__ == "__main__": + device_id: Optional[int] = None try: - device_id = int( sys.argv[-1] ) - except: + device_id = int(sys.argv[-1]) + except ValueError: device_id = None if "--input" in sys.argv or "-i" in sys.argv: - input_main(device_id) elif "--output" in sys.argv or "-o" in sys.argv: @@ -820,3 +928,5 @@ if __name__ == '__main__': print_device_info() else: usage() + + pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/moveit.py b/venv/Lib/site-packages/pygame/examples/moveit.py index 194cd12423bac8d465b81b59b6b2ae5e9da0d63b..ffcc7f9e016c5d0ff0ac8a96c19c819741f27961 100644 --- a/venv/Lib/site-packages/pygame/examples/moveit.py +++ b/venv/Lib/site-packages/pygame/examples/moveit.py @@ -1,73 +1,119 @@ #!/usr/bin/env python +""" pygame.examples.moveit -""" This is the full and final example from the Pygame Tutorial, "How Do I Make It Move". It creates 10 objects and animates them on the screen. +It also has a separate player character that can be controlled with arrow keys. + Note it's a bit scant on error checking, but it's easy to read. :] Fortunately, this is python, and we needn't wrestle with a pile of error codes. """ +import os +import pygame as pg +main_dir = os.path.split(os.path.abspath(__file__))[0] -#import everything -import os, pygame -from pygame.locals import * +# Height and Width of screen +WIDTH = 640 +HEIGHT = 480 +# Height and width of the sprite +SPRITE_WIDTH = 80 +SPRITE_HEIGHT = 60 -main_dir = os.path.split(os.path.abspath(__file__))[0] -#our game object class +# our game object class class GameObject: def __init__(self, image, height, speed): self.speed = speed self.image = image self.pos = image.get_rect().move(0, height) - def move(self): - self.pos = self.pos.move(self.speed, 0) - if self.pos.right > 600: + + # move the object. + def move(self, up=False, down=False, left=False, right=False): + if right: + self.pos.right += self.speed + if left: + self.pos.right -= self.speed + if down: + self.pos.top += self.speed + if up: + self.pos.top -= self.speed + + # controls the object such that it cannot leave the screen's viewpoint + if self.pos.right > WIDTH: self.pos.left = 0 + if self.pos.top > HEIGHT - SPRITE_HEIGHT: + self.pos.top = 0 + if self.pos.right < SPRITE_WIDTH: + self.pos.right = WIDTH + if self.pos.top < 0: + self.pos.top = HEIGHT - SPRITE_HEIGHT -#quick function to load an image +# quick function to load an image def load_image(name): - path = os.path.join(main_dir, 'data', name) - return pygame.image.load(path).convert() + path = os.path.join(main_dir, "data", name) + return pg.image.load(path).convert() -#here's the full code +# here's the full code def main(): - pygame.init() - screen = pygame.display.set_mode((640, 480)) + pg.init() + clock = pg.time.Clock() + screen = pg.display.set_mode((WIDTH, HEIGHT)) - player = load_image('player1.gif') - background = load_image('liquid.bmp') + player = load_image("player1.gif") + entity = load_image("alien1.gif") + background = load_image("liquid.bmp") # scale the background image so that it fills the window and - # successfully overwrites the old sprite position. - background = pygame.transform.scale2x(background) - background = pygame.transform.scale2x(background) + # successfully overwrites the old sprite position. + background = pg.transform.scale2x(background) + background = pg.transform.scale2x(background) screen.blit(background, (0, 0)) objects = [] + p = GameObject(player, 10, 3) for x in range(10): - o = GameObject(player, x*40, x) + o = GameObject(entity, x * 40, x) objects.append(o) - while 1: - for event in pygame.event.get(): - if event.type in (QUIT, KEYDOWN): + pg.display.set_caption("Move It!") + + # This is a simple event handler that enables player input. + while True: + # Get all keys currently pressed, and move when an arrow key is held. + keys = pg.key.get_pressed() + if keys[pg.K_UP]: + p.move(up=True) + if keys[pg.K_DOWN]: + p.move(down=True) + if keys[pg.K_LEFT]: + p.move(left=True) + if keys[pg.K_RIGHT]: + p.move(right=True) + + # Draw the background + screen.blit(background, (0, 0)) + for e in pg.event.get(): + # quit upon screen exit + if e.type == pg.QUIT: return - for o in objects: screen.blit(background, o.pos, o.pos) for o in objects: - o.move() + o.move(right=True) screen.blit(o.image, o.pos) - - pygame.display.update() - + screen.blit(p.image, p.pos) + clock.tick(60) + pg.display.update() + pg.time.delay(100) -if __name__ == '__main__': main() +if __name__ == "__main__": + main() + pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/oldalien.py b/venv/Lib/site-packages/pygame/examples/oldalien.py deleted file mode 100644 index d1f6d9fbbb422982c46f1905ea6ee981907c6bb4..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/oldalien.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python - -"""This is a much simpler version of the aliens.py -example. It makes a good place for beginners to get -used to the way pygame works. Gameplay is pretty similar, -but there are a lot less object types to worry about, -and it makes no attempt at using the optional pygame -modules. -It does provide a good method for using the updaterects -to only update the changed parts of the screen, instead of -the entire screen surface. This has large speed benefits -and should be used whenever the fullscreen isn't being changed.""" - - -#import -import random, os.path, sys -import pygame -from pygame.locals import * - -if not pygame.image.get_extended(): - raise SystemExit("Requires the extended image loading from SDL_image") - - -#constants -FRAMES_PER_SEC = 40 -PLAYER_SPEED = 12 -MAX_SHOTS = 2 -SHOT_SPEED = 10 -ALIEN_SPEED = 12 -ALIEN_ODDS = 45 -EXPLODE_TIME = 6 -SCREENRECT = Rect(0, 0, 640, 480) - - -#some globals for friendly access -dirtyrects = [] # list of update_rects -next_tick = 0 # used for timing -class Img: pass # container for images -main_dir = os.path.split(os.path.abspath(__file__))[0] # Program's diretory - - -#first, we define some utility functions - -def load_image(file, transparent): - "loads an image, prepares it for play" - file = os.path.join(main_dir, 'data', file) - try: - surface = pygame.image.load(file) - except pygame.error: - raise SystemExit('Could not load image "%s" %s' % - (file, pygame.get_error())) - if transparent: - corner = surface.get_at((0, 0)) - surface.set_colorkey(corner, RLEACCEL) - return surface.convert() - - - -# The logic for all the different sprite types - -class Actor: - "An enhanced sort of sprite class" - def __init__(self, image): - self.image = image - self.rect = image.get_rect() - - def update(self): - "update the sprite state for this frame" - pass - - def draw(self, screen): - "draws the sprite into the screen" - r = screen.blit(self.image, self.rect) - dirtyrects.append(r) - - def erase(self, screen, background): - "gets the sprite off of the screen" - r = screen.blit(background, self.rect, self.rect) - dirtyrects.append(r) - - -class Player(Actor): - "Cheer for our hero" - def __init__(self): - Actor.__init__(self, Img.player) - self.alive = 1 - self.reloading = 0 - self.rect.centerx = SCREENRECT.centerx - self.rect.bottom = SCREENRECT.bottom - 10 - - def move(self, direction): - self.rect = self.rect.move(direction*PLAYER_SPEED, 0).clamp(SCREENRECT) - - -class Alien(Actor): - "Destroy him or suffer" - def __init__(self): - Actor.__init__(self, Img.alien) - self.facing = random.choice((-1,1)) * ALIEN_SPEED - if self.facing < 0: - self.rect.right = SCREENRECT.right - - def update(self): - global SCREENRECT - self.rect[0] = self.rect[0] + self.facing - if not SCREENRECT.contains(self.rect): - self.facing = -self.facing; - self.rect.top = self.rect.bottom + 3 - self.rect = self.rect.clamp(SCREENRECT) - - -class Explosion(Actor): - "Beware the fury" - def __init__(self, actor): - Actor.__init__(self, Img.explosion) - self.life = EXPLODE_TIME - self.rect.center = actor.rect.center - - def update(self): - self.life = self.life - 1 - - -class Shot(Actor): - "The big payload" - def __init__(self, player): - Actor.__init__(self, Img.shot) - self.rect.centerx = player.rect.centerx - self.rect.top = player.rect.top - 10 - - def update(self): - self.rect.top = self.rect.top - SHOT_SPEED - - - - -def main(): - "Run me for adrenaline" - global dirtyrects - - # Initialize SDL components - pygame.init() - screen = pygame.display.set_mode(SCREENRECT.size, 0) - clock = pygame.time.Clock() - - # Load the Resources - Img.background = load_image('background.gif', 0) - Img.shot = load_image('shot.gif', 1) - Img.bomb = load_image('bomb.gif', 1) - Img.danger = load_image('danger.gif', 1) - Img.alien = load_image('alien1.gif', 1) - Img.player = load_image('oldplayer.gif', 1) - Img.explosion = load_image('explosion1.gif', 1) - - # Create the background - background = pygame.Surface(SCREENRECT.size) - for x in range(0, SCREENRECT.width, Img.background.get_width()): - background.blit(Img.background, (x, 0)) - screen.blit(background, (0,0)) - pygame.display.flip() - - # Initialize Game Actors - player = Player() - aliens = [Alien()] - shots = [] - explosions = [] - - # Main loop - while player.alive or explosions: - clock.tick(FRAMES_PER_SEC) - - # Gather Events - pygame.event.pump() - keystate = pygame.key.get_pressed() - if keystate[K_ESCAPE] or pygame.event.peek(QUIT): - break - - # Clear screen and update actors - for actor in [player] + aliens + shots + explosions: - actor.erase(screen, background) - actor.update() - - # Clean Dead Explosions and Bullets - for e in explosions: - if e.life <= 0: - explosions.remove(e) - for s in shots: - if s.rect.top <= 0: - shots.remove(s) - - # Move the player - direction = keystate[K_RIGHT] - keystate[K_LEFT] - player.move(direction) - - # Create new shots - if not player.reloading and keystate[K_SPACE] and len(shots) < MAX_SHOTS: - shots.append(Shot(player)) - player.reloading = keystate[K_SPACE] - - # Create new alien - if not int(random.random() * ALIEN_ODDS): - aliens.append(Alien()) - - # Detect collisions - alienrects = [] - for a in aliens: - alienrects.append(a.rect) - - hit = player.rect.collidelist(alienrects) - if hit != -1: - alien = aliens[hit] - explosions.append(Explosion(alien)) - explosions.append(Explosion(player)) - aliens.remove(alien) - player.alive = 0 - for shot in shots: - hit = shot.rect.collidelist(alienrects) - if hit != -1: - alien = aliens[hit] - explosions.append(Explosion(alien)) - shots.remove(shot) - aliens.remove(alien) - break - - # Draw everybody - for actor in [player] + aliens + shots + explosions: - actor.draw(screen) - - pygame.display.update(dirtyrects) - dirtyrects = [] - - pygame.time.wait(50) - - -#if python says run, let's run! -if __name__ == '__main__': - main() - diff --git a/venv/Lib/site-packages/pygame/examples/overlay.py b/venv/Lib/site-packages/pygame/examples/overlay.py deleted file mode 100644 index 8329071f66bcece321268d2d9f13b958ad5ae1aa..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/overlay.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -import sys -import pygame -from pygame.compat import xrange_ - -SR= (800,600) -ovl= None - -######################################################################## -# Simple video player -def vPlayer( fName ): - global ovl - f= open( fName, 'rb' ) - fmt= f.readline().strip() - res= f.readline().strip() - col= f.readline().strip() - if fmt!= "P5": - print ('Unknown format( len %d ). Exiting...' % len( fmt )) - return - - w,h= [ int(x) for x in res.split( ' ' ) ] - h= ( h* 2 )/ 3 - # Read into strings - y= f.read( w*h ) - u= [] - v= [] - for i in xrange_( 0, h/2 ): - u.append( f.read( w/2 )) - v.append( f.read( w/2 )) - - u= ''.join(u) - v= ''.join(v) - - # Open overlay with the resolution specified - ovl= pygame.Overlay(pygame.YV12_OVERLAY, (w,h)) - ovl.set_location(0, 0, w, h) - - ovl.display((y,u,v)) - while 1: - pygame.time.wait(10) - for ev in pygame.event.get(): - if ev.type in (pygame.KEYDOWN, pygame.QUIT): - return - - -def main(fname): - """play video file fname""" - pygame.init() - try: - pygame.display.set_mode(SR) - vPlayer(fname) - finally: - pygame.quit() - -# Test all modules -if __name__== '__main__': - if len( sys.argv )!= 2: - print ("Usage: play_file ") - else: - main(sys.argv[1]) - diff --git a/venv/Lib/site-packages/pygame/examples/pixelarray.py b/venv/Lib/site-packages/pygame/examples/pixelarray.py index 3092b188b0009894a48fbd1be65c66a95d9a7504..6f5d58d04f8e8f745aba788d0228dfa5e0a9506a 100644 --- a/venv/Lib/site-packages/pygame/examples/pixelarray.py +++ b/venv/Lib/site-packages/pygame/examples/pixelarray.py @@ -1,123 +1,142 @@ #!/usr/bin/env python -import os, pygame -from pygame.compat import xrange_ +""" pygame.examples.pixelarray + +PixelArray does array processing of pixels. +Sort of like another array processor called 'numpy' - But for pixels. + + Flip it, + stripe it, + rotate it. + +Controls +-------- + +To see different effects - press a key or click a mouse. +""" +import os +import pygame as pg + main_dir = os.path.split(os.path.abspath(__file__))[0] -data_dir = os.path.join(main_dir, 'data') - -def show (image): - screen = pygame.display.get_surface() - screen.fill ((255, 255, 255)) - screen.blit (image, (0, 0)) - pygame.display.flip () - while 1: - event = pygame.event.wait () - if event.type == pygame.QUIT: +data_dir = os.path.join(main_dir, "data") + + +def show(image): + screen = pg.display.get_surface() + screen.fill((255, 255, 255)) + screen.blit(image, (0, 0)) + pg.display.flip() + while True: + event = pg.event.wait() + if event.type == pg.QUIT: + pg.quit() raise SystemExit - if event.type == pygame.MOUSEBUTTONDOWN: + if event.type in [pg.MOUSEBUTTONDOWN, pg.KEYDOWN]: break + def main(): - pygame.init () + pg.init() - pygame.display.set_mode ((255, 255)) - surface = pygame.Surface ((255, 255)) + pg.display.set_mode((255, 255)) + surface = pg.Surface((255, 255)) - pygame.display.flip () + pg.display.flip() # Create the PixelArray. - ar = pygame.PixelArray (surface) - r, g, b = 0, 0, 0 + ar = pg.PixelArray(surface) + # Do some easy gradient effect. - for y in xrange_ (255): + for y in range(255): r, g, b = y, y, y - ar[:,y] = (r, g, b) + ar[:, y] = (r, g, b) del ar - show (surface) + show(surface) # We have made some gradient effect, now flip it. - ar = pygame.PixelArray (surface) - ar[:] = ar[:,::-1] + ar = pg.PixelArray(surface) + ar[:] = ar[:, ::-1] del ar - show (surface) + show(surface) # Every second column will be made blue - ar = pygame.PixelArray (surface) + ar = pg.PixelArray(surface) ar[::2] = (0, 0, 255) del ar - show (surface) + show(surface) # Every second row will be made green - ar = pygame.PixelArray (surface) - ar[:,::2] = (0, 255, 0) + ar = pg.PixelArray(surface) + ar[:, ::2] = (0, 255, 0) del ar - show (surface) + show(surface) # Manipulate the image. Flip it around the y axis. - surface = pygame.image.load (os.path.join (data_dir, 'arraydemo.bmp')) - ar = pygame.PixelArray (surface) - ar[:] = ar[:,::-1] + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + ar[:] = ar[:, ::-1] del ar - show (surface) + show(surface) # Flip the image around the x axis. - ar = pygame.PixelArray (surface) - ar[:] = ar[::-1,:] + ar = pg.PixelArray(surface) + ar[:] = ar[::-1, :] del ar - show (surface) + show(surface) # Every second column will be made white. - ar = pygame.PixelArray (surface) + ar = pg.PixelArray(surface) ar[::2] = (255, 255, 255) del ar - show (surface) + show(surface) - # Flip the image around both axes, restoring it's original layout. - ar = pygame.PixelArray (surface) - ar[:] = ar[::-1,::-1] + # Flip the image around both axes, restoring its original layout. + ar = pg.PixelArray(surface) + ar[:] = ar[::-1, ::-1] del ar - show (surface) + show(surface) # Rotate 90 degrees clockwise. - w, h = surface.get_size () - surface2 = pygame.Surface ((h, w), surface.get_flags (), surface) - ar = pygame.PixelArray (surface) - ar2 = pygame.PixelArray (surface2) - ar2[...] = ar.transpose ()[::-1,:] + w, h = surface.get_size() + surface2 = pg.Surface((h, w), surface.get_flags(), surface) + ar = pg.PixelArray(surface) + ar2 = pg.PixelArray(surface2) + ar2[...] = ar.transpose()[::-1, :] del ar, ar2 - show (surface2) - + show(surface2) + # Scale it by throwing each second pixel away. - surface = pygame.image.load (os.path.join (data_dir, 'arraydemo.bmp')) - ar = pygame.PixelArray (surface) - sf2 = ar[::2,::2].make_surface () + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + sf2 = ar[::2, ::2].make_surface() del ar - show (sf2) + show(sf2) # Replace anything looking like the blue color from the text. - ar = pygame.PixelArray (surface) - ar.replace ((60, 60, 255), (0, 255, 0), 0.06) + ar = pg.PixelArray(surface) + ar.replace((60, 60, 255), (0, 255, 0), 0.06) del ar - show (surface) + show(surface) # Extract anything which might be somewhat black. - surface = pygame.image.load (os.path.join (data_dir, 'arraydemo.bmp')) - ar = pygame.PixelArray (surface) - ar2 = ar.extract ((0, 0, 0), 0.07) + surface = pg.image.load(os.path.join(data_dir, "arraydemo.bmp")) + ar = pg.PixelArray(surface) + ar2 = ar.extract((0, 0, 0), 0.07) sf2 = ar2.surface del ar, ar2 - show (sf2) + show(sf2) # Compare two images. - surface = pygame.image.load (os.path.join (data_dir, 'alien1.gif')) - surface2 = pygame.image.load (os.path.join (data_dir, 'alien2.gif')) - ar1 = pygame.PixelArray (surface) - ar2 = pygame.PixelArray (surface2) - ar3 = ar1.compare (ar2, 0.07) + surface = pg.image.load(os.path.join(data_dir, "alien1.gif")) + surface2 = pg.image.load(os.path.join(data_dir, "alien2.gif")) + ar1 = pg.PixelArray(surface) + ar2 = pg.PixelArray(surface2) + ar3 = ar1.compare(ar2, 0.07) sf3 = ar3.surface del ar1, ar2, ar3 - show (sf3) + show(sf3) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() + pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/playmus.py b/venv/Lib/site-packages/pygame/examples/playmus.py index 4b4e46da9cb0499a2ca28be1156d54258c29dab7..8c677333561d28b30bea50b2bf91e7496833e520 100644 --- a/venv/Lib/site-packages/pygame/examples/playmus.py +++ b/venv/Lib/site-packages/pygame/examples/playmus.py @@ -1,20 +1,29 @@ #!/usr/bin/env python +""" pygame.examples.playmus -"""A simple music player. +A simple music player. - Use pygame.mixer.music to play an audio file. A window is - created to handle keyboard events for playback commands. + Use pygame.mixer.music to play an audio file. + +A window is created to handle keyboard events for playback commands. + + +Keyboard Controls +----------------- + +space - play/pause toggle +r - rewind +f - fade out +q - stop """ +import sys -from __future__ import print_function -import pygame +import pygame as pg import pygame.freetype -from pygame.locals import * -import sys -import os -class Window(object): + +class Window: """The application's Pygame window A Window instance manages the creation of and drawing to a @@ -30,15 +39,17 @@ class Window(object): if Window.instance is not None: return Window.instance self = object.__new__(cls) - pygame.display.init() - self.screen = pygame.display.set_mode((600, 400)) + pg.display.init() + self.screen = pg.display.set_mode((600, 400)) Window.instance = self return self def __init__(self, title): - pygame.display.set_caption(title) - self.screen.fill(Color('white')) - pygame.display.flip() + pg.display.set_caption(title) + self.text_color = (254, 231, 21, 255) + self.background_color = (16, 24, 32, 255) + self.screen.fill(self.background_color) + pg.display.flip() pygame.freetype.init() self.font = pygame.freetype.Font(None, 20) @@ -47,10 +58,14 @@ class Window(object): self.descender = int(self.font.get_sized_descender() * 1.5) self.line_height = self.ascender - self.descender - self.write_lines("'q', ESCAPE or close this window to quit\n" - "SPACE to play/pause\n" - "'r' to rewind\n" - "'f' to faid out over 5 seconds\n", 0) + self.write_lines( + "\nPress 'q' or 'ESCAPE' or close this window to quit\n" + "Press 'SPACE' to play / pause\n" + "Press 'r' to rewind to the beginning (restart)\n" + "Press 'f' to fade music out over 5 seconds\n\n" + "Window will quit automatically when music ends\n", + 0, + ) def __enter__(self): return self @@ -60,7 +75,7 @@ class Window(object): return False def close(self): - pygame.display.quit() + pg.display.quit() Window.instance = None def write_lines(self, text, line=0): @@ -69,72 +84,82 @@ class Window(object): nlines = h // line_height if line < 0: line = nlines + line - for i, text_line in enumerate(text.split('\n'), line): + for i, text_line in enumerate(text.split("\n"), line): y = i * line_height + self.ascender # Clear the line first. - self.screen.fill(Color('white'), - (0, i * line_height, w, line_height)) - + self.screen.fill( + self.background_color, (0, i * line_height, w, line_height) + ) # Write new text. - self.font.render_to(self.screen, (15, y), text_line, Color('blue')) - pygame.display.flip() + self.font.render_to(self.screen, (15, y), text_line, self.text_color) + pg.display.flip() def show_usage_message(): print("Usage: python playmus.py ") print(" python -m pygame.examples.playmus ") + def main(file_path): - """Play an audio file with pygame.mixer.music""" + """Play an audio file with pg.mixer.music""" with Window(file_path) as win: - win.write_lines('Loading ...', -1) - pygame.mixer.init(frequency=44100) + win.write_lines("Loading ...", -1) + pg.mixer.init(frequency=44100) try: paused = False - pygame.mixer.music.load(file_path) + pg.mixer.music.load(file_path) # Make sure the event loop ticks over at least every 0.5 seconds. - pygame.time.set_timer(USEREVENT, 500) + pg.time.set_timer(pg.USEREVENT, 500) - pygame.mixer.music.play() + pg.mixer.music.play() win.write_lines("Playing ...\n", -1) - while pygame.mixer.music.get_busy(): - e = pygame.event.wait() - if e.type == pygame.KEYDOWN: + while pg.mixer.music.get_busy() or paused: + e = pg.event.wait() + if e.type == pg.KEYDOWN: key = e.key - if key == K_SPACE: + if key == pg.K_SPACE: if paused: - pygame.mixer.music.unpause() + pg.mixer.music.unpause() paused = False win.write_lines("Playing ...\n", -1) else: - pygame.mixer.music.pause() + pg.mixer.music.pause() paused = True win.write_lines("Paused ...\n", -1) - elif key == K_r: - pygame.mixer.music.rewind() + elif key == pg.K_r: + if file_path[-3:].lower() in ("ogg", "mp3", "mod"): + status = "Rewound." + pg.mixer.music.rewind() + else: + status = "Restarted." + pg.mixer.music.play() if paused: - win.write_lines("Rewound.", -1) - elif key == K_f: - win.write_lines("Faiding out ...\n", -1) - pygame.mixer.music.fadeout(5000) - # when finished get_busy() will return 0. - elif key in [K_q, K_ESCAPE]: - pygame.mixer.music.stop() - # get_busy() will now return 0. - elif e.type == QUIT: - pygame.mixer.music.stop() - # get_busy() will now return 0. - pygame.time.set_timer(USEREVENT, 0) + pg.mixer.music.pause() + win.write_lines(status, -1) + elif key == pg.K_f: + win.write_lines("Fading out ...\n", -1) + pg.mixer.music.fadeout(5000) + # when finished get_busy() will return False. + elif key in [pg.K_q, pg.K_ESCAPE]: + paused = False + pg.mixer.music.stop() + # get_busy() will now return False. + elif e.type == pg.QUIT: + paused = False + pg.mixer.music.stop() + # get_busy() will now return False. + pg.time.set_timer(pg.USEREVENT, 0) finally: - pygame.mixer.quit() + pg.mixer.quit() + pg.quit() + -if __name__ == '__main__': -# Check the only command line argument, a file path +if __name__ == "__main__": + # Check the only command line argument, a file path if len(sys.argv) != 2: show_usage_message() else: main(sys.argv[1]) - diff --git a/venv/Lib/site-packages/pygame/examples/prevent_display_stretching.py b/venv/Lib/site-packages/pygame/examples/prevent_display_stretching.py deleted file mode 100644 index 9e728a676dcc43d6e66ca601a37ceb3540b1ea58..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/examples/prevent_display_stretching.py +++ /dev/null @@ -1,89 +0,0 @@ -# coding: ascii -"""Prevent display stretching - -On some computers, the display environment can be configured to stretch -all windows so that they will not appear too small on the screen for -the user. This configuration is especially common on high-DPI displays. -pygame graphics appear distorted when automatically stretched by the -display environment. This script demonstrates a technique for preventing -this stretching and distortion. - -Limitations: -This script makes an API call that is only available on Windows (versions -Vista and newer). ctypes must be installed. - -""" - -# Ensure that the computer is running Windows Vista or newer -import os, sys -if os.name != "nt" or sys.getwindowsversion()[0] < 6: - raise NotImplementedError('this script requires Windows Vista or newer') - -# Ensure that ctypes is installed. It is included with Python 2.5 and newer, -# but Python 2.4 users must install ctypes manually. -try: - import ctypes -except ImportError: - print('install ctypes from http://sourceforge.net/projects/ctypes/files/ctypes') - raise - -import pygame - -# Determine whether or not the user would like to prevent stretching -if os.path.basename(sys.executable) == 'pythonw.exe': - selection = 'y' -else: - from pygame.compat import raw_input_ - selection = None - while selection not in ('y', 'n'): - selection = raw_input_('Prevent stretching? (y/n): ').strip().lower() - -if selection == 'y': - msg = 'Stretching is prevented.' -else: - msg = 'Stretching is not prevented.' - -# Prevent stretching -if selection == 'y': - user32 = ctypes.windll.user32 - user32.SetProcessDPIAware() - -# Show screen -pygame.display.init() -RESOLUTION = (350, 350) -screen = pygame.display.set_mode(RESOLUTION) - -# Render message onto a surface -pygame.font.init() -font = pygame.font.Font(None, 36) -msg_surf = font.render(msg, 1, pygame.Color('green')) -res_surf = font.render('Intended resolution: %ix%i' % RESOLUTION, 1, pygame.Color('green')) - -# Control loop -running = True -clock = pygame.time.Clock() -counter = 0 -while running: - - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - screen.fill(pygame.Color('black')) - - # Draw lines which will be blurry if the window is stretched - # or clear if the window is not stretched. - pygame.draw.line(screen, pygame.Color('white'), (0, counter), (RESOLUTION[0] - 1, counter)) - pygame.draw.line(screen, pygame.Color('white'), (counter, 0), (counter, RESOLUTION[1] - 1)) - - # Blit message onto screen surface - msg_blit_rect = screen.blit(msg_surf, (0, 0)) - screen.blit(res_surf, (0, msg_blit_rect.bottom)) - - clock.tick(10) - - pygame.display.flip() - - counter += 1 - if counter == RESOLUTION[0]: - counter = 0 diff --git a/venv/Lib/site-packages/pygame/examples/scaletest.py b/venv/Lib/site-packages/pygame/examples/scaletest.py index a38ad8107355ea3fa453ede162b96e0a7c328a3d..0a79f6ff0a7880fff01731a7022d845fb73ec0f2 100644 --- a/venv/Lib/site-packages/pygame/examples/scaletest.py +++ b/venv/Lib/site-packages/pygame/examples/scaletest.py @@ -1,7 +1,13 @@ #!/usr/bin/env python +""" pygame.examples.scaletest + +Shows an interactive image scaler. + +""" +import sys +import time +import pygame as pg -import sys, time -import pygame def main(imagefile, convert_alpha=False, run_speed_test=False): """show an interactive image scaler @@ -10,30 +16,29 @@ def main(imagefile, convert_alpha=False, run_speed_test=False): imagefile - name of source image (required) convert_alpha - use convert_alpha() on the surf (default False) run_speed_test - (default False) - """ # initialize display - pygame.display.init() + pg.display.init() # load background image - background = pygame.image.load(imagefile) + background = pg.image.load(imagefile) if run_speed_test: if convert_alpha: # convert_alpha() requires the display mode to be set - pygame.display.set_mode((1, 1)) + pg.display.set_mode((1, 1)) background = background.convert_alpha() SpeedTest(background) return # start fullscreen mode - screen = pygame.display.set_mode((1024, 768), pygame.FULLSCREEN) + screen = pg.display.set_mode((1024, 768), pg.FULLSCREEN) if convert_alpha: background = background.convert_alpha() # turn off the mouse pointer - pygame.mouse.set_visible(0) + pg.mouse.set_visible(0) # main loop bRunning = True bUp = False @@ -41,40 +46,52 @@ def main(imagefile, convert_alpha=False, run_speed_test=False): bLeft = False bRight = False cursize = [background.get_width(), background.get_height()] - while(bRunning): - image = pygame.transform.smoothscale(background, cursize) + while bRunning: + image = pg.transform.smoothscale(background, cursize) imgpos = image.get_rect(centerx=512, centery=384) - screen.fill((255,255,255)) + screen.fill((255, 255, 255)) screen.blit(image, imgpos) - pygame.display.flip() - for event in pygame.event.get(): - if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): + pg.display.flip() + for event in pg.event.get(): + if event.type == pg.QUIT or ( + event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE + ): bRunning = False - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_UP: bUp = True - if event.key == pygame.K_DOWN: bDown = True - if event.key == pygame.K_LEFT: bLeft = True - if event.key == pygame.K_RIGHT: bRight = True - if event.type == pygame.KEYUP: - if event.key == pygame.K_UP: bUp = False - if event.key == pygame.K_DOWN: bDown = False - if event.key == pygame.K_LEFT: bLeft = False - if event.key == pygame.K_RIGHT: bRight = False + if event.type == pg.KEYDOWN: + if event.key == pg.K_UP: + bUp = True + if event.key == pg.K_DOWN: + bDown = True + if event.key == pg.K_LEFT: + bLeft = True + if event.key == pg.K_RIGHT: + bRight = True + if event.type == pg.KEYUP: + if event.key == pg.K_UP: + bUp = False + if event.key == pg.K_DOWN: + bDown = False + if event.key == pg.K_LEFT: + bLeft = False + if event.key == pg.K_RIGHT: + bRight = False if bUp: cursize[1] -= 2 - if cursize[1] < 1: cursize[1] = 1 + if cursize[1] < 1: + cursize[1] = 1 if bDown: cursize[1] += 2 if bLeft: cursize[0] -= 2 - if cursize[0] < 1: cursize[0] = 1 + if cursize[0] < 1: + cursize[0] = 1 if bRight: cursize[0] += 2 + pg.quit() def SpeedTest(image): - print("\nImage Scaling Speed Test - Image Size %s\n" % str( - image.get_size())) + print(f"\nImage Scaling Speed Test - Image Size {str(image.get_size())}\n") imgsize = [image.get_width(), image.get_height()] duration = 0.0 @@ -82,59 +99,56 @@ def SpeedTest(image): shrinkx = (imgsize[0] * i) // 128 shrinky = (imgsize[1] * i) // 128 start = time.time() - tempimg = pygame.transform.smoothscale(image, (shrinkx, shrinky)) - duration += (time.time() - start) + tempimg = pg.transform.smoothscale(image, (shrinkx, shrinky)) + duration += time.time() - start del tempimg - print("Average transform.smoothscale shrink time: %.4f ms." % ( - duration / 128 * 1000)) + print(f"Average transform.smoothscale shrink time: {duration / 128 * 1000:.4f} ms.") - duration = 0 + duration = 0.0 for i in range(128): expandx = (imgsize[0] * (i + 129)) // 128 expandy = (imgsize[1] * (i + 129)) // 128 start = time.time() - tempimg = pygame.transform.smoothscale(image, (expandx, expandy)) - duration += (time.time() - start) + tempimg = pg.transform.smoothscale(image, (expandx, expandy)) + duration += time.time() - start del tempimg - print("Average transform.smoothscale expand time: %.4f ms." % ( - duration / 128 * 1000)) + print(f"Average transform.smoothscale expand time: {duration / 128 * 1000:.4f} ms.") duration = 0.0 for i in range(128): shrinkx = (imgsize[0] * i) // 128 shrinky = (imgsize[1] * i) // 128 start = time.time() - tempimg = pygame.transform.scale(image, (shrinkx, shrinky)) - duration += (time.time() - start) + tempimg = pg.transform.scale(image, (shrinkx, shrinky)) + duration += time.time() - start del tempimg - print("Average transform.scale shrink time: %.4f ms." % ( - duration / 128 * 1000)) + print(f"Average transform.scale shrink time: {duration / 128 * 1000:.4f} ms.") - duration = 0 + duration = 0.0 for i in range(128): expandx = (imgsize[0] * (i + 129)) // 128 expandy = (imgsize[1] * (i + 129)) // 128 start = time.time() - tempimg = pygame.transform.scale(image, (expandx, expandy)) - duration += (time.time() - start) + tempimg = pg.transform.scale(image, (expandx, expandy)) + duration += time.time() - start del tempimg - print("Average transform.scale expand time: %.4f ms." % ( - duration / 128 * 1000)) + print(f"Average transform.scale expand time: {duration / 128 * 1000:.4f} ms.") -if __name__ == '__main__': +if __name__ == "__main__": # check input parameters if len(sys.argv) < 2: - print("\nUsage: %s imagefile [-t] [-convert_alpha]" % sys.argv[0]) + print(f"\nUsage: {sys.argv[0]} imagefile [-t] [-convert_alpha]") print(" imagefile image filename (required)") print(" -t run speed test") - print(" -convert_alpha use convert_alpha() on the image's " - "surface\n") + print(" -convert_alpha use convert_alpha() on the image's " "surface\n") else: - main(sys.argv[1], - convert_alpha = '-convert_alpha' in sys.argv, - run_speed_test = '-t' in sys.argv) + main( + sys.argv[1], + convert_alpha="-convert_alpha" in sys.argv, + run_speed_test="-t" in sys.argv, + ) diff --git a/venv/Lib/site-packages/pygame/examples/scrap_clipboard.py b/venv/Lib/site-packages/pygame/examples/scrap_clipboard.py index 0fc6be98d56b6cf49ee131753d97ab9f28943375..e6b9773b7701c04d6f2b6e60d11f989ba529f398 100644 --- a/venv/Lib/site-packages/pygame/examples/scrap_clipboard.py +++ b/venv/Lib/site-packages/pygame/examples/scrap_clipboard.py @@ -1,87 +1,94 @@ #!/usr/bin/env python -""" +""" pygame.examples.scrap_clipboard + Demonstrates the clipboard capabilities of pygame. + +Copy/paste! + + +Keyboard Controls +----------------- + +g - get and print types in clipboard. If, image blit to screen. +p - place some text into clipboard +a - print types available in the clipboard +i - put image into the clipboard """ import os -import pygame -from pygame.locals import * +import pygame as pg import pygame.scrap as scrap -from pygame.compat import as_bytes -BytesIO = pygame.compat.get_BytesIO() -def usage (): - print ("Press the 'g' key to get all of the current clipboard data") - print ("Press the 'p' key to put a string into the clipboard") - print ("Press the 'a' key to get a list of the currently available types") - print ("Press the 'i' key to put an image into the clipboard") +from io import BytesIO + + +def usage(): + print("Press the 'g' key to get all of the current clipboard data") + print("Press the 'p' key to put a string into the clipboard") + print("Press the 'a' key to get a list of the currently available types") + print("Press the 'i' key to put an image into the clipboard") + main_dir = os.path.split(os.path.abspath(__file__))[0] -pygame.init () -screen = pygame.display.set_mode ((200, 200)) -c = pygame.time.Clock () +pg.init() +screen = pg.display.set_mode((200, 200)) +c = pg.time.Clock() going = True # Initialize the scrap module and use the clipboard mode. -scrap.init () -scrap.set_mode (SCRAP_CLIPBOARD) +scrap.init() +scrap.set_mode(pg.SCRAP_CLIPBOARD) -usage () +usage() while going: - for e in pygame.event.get (): - if e.type == QUIT or (e.type == KEYDOWN and e.key == K_ESCAPE): + for e in pg.event.get(): + if e.type == pg.QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE): going = False - elif e.type == KEYDOWN and e.key == K_g: + elif e.type == pg.KEYDOWN and e.key == pg.K_g: # This means to look for data. - print ("Getting the different clipboard data..") - for t in scrap.get_types (): - r = scrap.get (t) - if r and len (r) > 500: - print ("Type %s : (large %i byte buffer)" % (t, len(r))) + print("Getting the different clipboard data..") + for t in scrap.get_types(): + r = scrap.get(t) + if r and len(r) > 500: + print(f"Type {t} : (large {len(r)} byte buffer)") elif r is None: - print ("Type %s : None" % (t,)) + print(f"Type {t} : None") else: - print ("Type %s : '%s'" % (t, r.decode('ascii', 'ignore'))) + print(f"Type {t} : '{r.decode('ascii', 'ignore')}'") if "image" in t: namehint = t.split("/")[1] - if namehint in ['bmp', 'png', 'jpg']: + if namehint in ["bmp", "png", "jpg"]: f = BytesIO(r) - loaded_surf = pygame.image.load(f, "." + namehint) - screen.blit(loaded_surf, (0,0)) + loaded_surf = pg.image.load(f, "." + namehint) + screen.blit(loaded_surf, (0, 0)) - - elif e.type == KEYDOWN and e.key == K_p: + elif e.type == pg.KEYDOWN and e.key == pg.K_p: # Place some text into the selection. - print ("Placing clipboard text.") - scrap.put (SCRAP_TEXT, - as_bytes("Hello. This is a message from scrap.")) + print("Placing clipboard text.") + scrap.put(pg.SCRAP_TEXT, b"Hello. This is a message from scrap.") - elif e.type == KEYDOWN and e.key == K_a: + elif e.type == pg.KEYDOWN and e.key == pg.K_a: # Get all available types. - print ("Getting the available types from the clipboard.") - types = scrap.get_types () - print (types) - if len (types) > 0: - print ("Contains %s: %s" % - (types[0], scrap.contains (types[0]))) - print ("Contains _INVALID_: ", scrap.contains ("_INVALID_")) - - elif e.type == KEYDOWN and e.key == K_i: - print ("Putting image into the clipboard.") - scrap.set_mode (SCRAP_CLIPBOARD) - fp = open (os.path.join(main_dir, 'data', 'liquid.bmp'), 'rb') - buf = fp.read () - scrap.put ("image/bmp", buf) - fp.close () - - elif e.type in (KEYDOWN, MOUSEBUTTONDOWN): - usage () - pygame.display.flip() + print("Getting the available types from the clipboard.") + types = scrap.get_types() + print(types) + if len(types) > 0: + print(f"Contains {types[0]}: {scrap.contains(types[0])}") + print("Contains _INVALID_: ", scrap.contains("_INVALID_")) + + elif e.type == pg.KEYDOWN and e.key == pg.K_i: + print("Putting image into the clipboard.") + scrap.set_mode(pg.SCRAP_CLIPBOARD) + fp = open(os.path.join(main_dir, "data", "liquid.bmp"), "rb") + buf = fp.read() + scrap.put("image/bmp", buf) + fp.close() + + elif e.type in (pg.KEYDOWN, pg.MOUSEBUTTONDOWN): + usage() + pg.display.flip() c.tick(40) - - - - +pg.quit() diff --git a/venv/Lib/site-packages/pygame/examples/scroll.py b/venv/Lib/site-packages/pygame/examples/scroll.py index 1d4a9d6f93f057204b0751b424be7d54ea1f91d0..7c61ebbd9c7a85fd2827c67199ac6299db637538 100644 --- a/venv/Lib/site-packages/pygame/examples/scroll.py +++ b/venv/Lib/site-packages/pygame/examples/scroll.py @@ -1,6 +1,7 @@ #!/usr/bin/env python +""" pygame.examples.scroll -"""An zoomed image viewer that demonstrates Surface.scroll +An zoomed image viewer that demonstrates Surface.scroll This example shows a scrollable image that has a zoom factor of eight. It uses the Surface.scroll function to shift the image on the display @@ -12,18 +13,16 @@ is provided a default image file is used. When running click on a black triangle to move one pixel in the direction the triangle points. Or use the arrow keys. Close the window or press ESC to quit. - """ - import sys import os -import pygame +import pygame as pg from pygame.transform import scale -from pygame.locals import * main_dir = os.path.dirname(os.path.abspath(__file__)) + DIR_UP = 1 DIR_DOWN = 2 DIR_LEFT = 3 @@ -31,29 +30,28 @@ DIR_RIGHT = 4 zoom_factor = 8 -def draw_arrow(surf, color, posn, direction): + +def draw_arrow(surf, color, posn, direction: int): x, y = posn if direction == DIR_UP: - pointlist = ((x - 29, y + 30), (x + 30, y + 30), - (x + 1, y - 29), (x, y - 29)) + pointlist = ((x - 29, y + 30), (x + 30, y + 30), (x + 1, y - 29), (x, y - 29)) elif direction == DIR_DOWN: - pointlist = ((x - 29, y - 29), (x + 30, y - 29), - (x + 1, y + 30), (x, y + 30)) + pointlist = ((x - 29, y - 29), (x + 30, y - 29), (x + 1, y + 30), (x, y + 30)) elif direction == DIR_LEFT: - pointlist = ((x + 30, y - 29), (x + 30, y + 30), - (x - 29, y + 1), (x - 29, y)) + pointlist = ((x + 30, y - 29), (x + 30, y + 30), (x - 29, y + 1), (x - 29, y)) else: - pointlist = ((x - 29, y - 29), (x - 29, y + 30), - (x + 30, y + 1), (x + 30, y)) - pygame.draw.polygon(surf, color, pointlist) + pointlist = ((x - 29, y - 29), (x - 29, y + 30), (x + 30, y + 1), (x + 30, y)) + pg.draw.polygon(surf, color, pointlist) + def add_arrow_button(screen, regions, posn, direction): - draw_arrow(screen, Color('black'), posn, direction) + draw_arrow(screen, "black", posn, direction) draw_arrow(regions, (direction, 0, 0), posn, direction) -def scroll_view(screen, image, direction, view_rect): - dx = dy = 0 + +def scroll_view(screen, image: pg.Surface, direction: int, view_rect): src_rect = None + dst_rect = None zoom_view_rect = screen.get_clip() image_w, image_h = image.get_size() if direction == DIR_UP: @@ -92,91 +90,97 @@ def scroll_view(screen, image, direction, view_rect): dst_rect = zoom_view_rect.copy() dst_rect.w = zoom_factor dst_rect.right = zoom_view_rect.right - if src_rect is not None: - scale(image.subsurface(src_rect), - dst_rect.size, - screen.subsurface(dst_rect)) - pygame.display.update(zoom_view_rect) + + if src_rect is not None and dst_rect is not None: + scale(image.subsurface(src_rect), dst_rect.size, screen.subsurface(dst_rect)) + pg.display.update(zoom_view_rect) + def main(image_file=None): if image_file is None: - image_file = os.path.join(main_dir, 'data', 'arraydemo.bmp') + image_file = os.path.join(main_dir, "data", "arraydemo.bmp") margin = 80 view_size = (30, 20) - zoom_view_size = (view_size[0] * zoom_factor, - view_size[1] * zoom_factor) - win_size = (zoom_view_size[0] + 2 * margin, - zoom_view_size[1] + 2 * margin) - background_color = Color('beige') + zoom_view_size = (view_size[0] * zoom_factor, view_size[1] * zoom_factor) + win_size = (zoom_view_size[0] + 2 * margin, zoom_view_size[1] + 2 * margin) + background_color = pg.Color("beige") - pygame.init() + pg.init() + pg.display.set_caption("Scroll Example") # set up key repeating so we can hold down the key to scroll. - old_k_delay, old_k_interval = pygame.key.get_repeat () - pygame.key.set_repeat (500, 30) + old_k_delay, old_k_interval = pg.key.get_repeat() + pg.key.set_repeat(500, 30) try: - screen = pygame.display.set_mode(win_size) + screen = pg.display.set_mode(win_size) screen.fill(background_color) - pygame.display.flip() + pg.display.flip() - image = pygame.image.load(image_file).convert() + image = pg.image.load(image_file).convert() image_w, image_h = image.get_size() if image_w < view_size[0] or image_h < view_size[1]: - print ("The source image is too small for this example.") - print ("A %i by %i or larger image is required." % zoom_view_size) + print("The source image is too small for this example.") + print("A %i by %i or larger image is required." % zoom_view_size) return - regions = pygame.Surface(win_size, 0, 24) - add_arrow_button(screen, regions, - (40, win_size[1] // 2), DIR_LEFT) - add_arrow_button(screen, regions, - (win_size[0] - 40, win_size[1] // 2), DIR_RIGHT) - add_arrow_button(screen, regions, - (win_size[0] // 2, 40), DIR_UP) - add_arrow_button(screen, regions, - (win_size[0] // 2, win_size[1] - 40), DIR_DOWN) - pygame.display.flip() + regions = pg.Surface(win_size, 0, 24) + add_arrow_button(screen, regions, (40, win_size[1] // 2), DIR_LEFT) + add_arrow_button( + screen, regions, (win_size[0] - 40, win_size[1] // 2), DIR_RIGHT + ) + add_arrow_button(screen, regions, (win_size[0] // 2, 40), DIR_UP) + add_arrow_button( + screen, regions, (win_size[0] // 2, win_size[1] - 40), DIR_DOWN + ) + pg.display.flip() screen.set_clip((margin, margin, zoom_view_size[0], zoom_view_size[1])) - view_rect = Rect(0, 0, view_size[0], view_size[1]) - - scale(image.subsurface(view_rect), zoom_view_size, - screen.subsurface(screen.get_clip())) - pygame.display.flip() + view_rect = pg.Rect(0, 0, view_size[0], view_size[1]) + scale( + image.subsurface(view_rect), + zoom_view_size, + screen.subsurface(screen.get_clip()), + ) + pg.display.flip() # the direction we will scroll in. direction = None - clock = pygame.time.Clock() + clock = pg.time.Clock() clock.tick() going = True + while going: # wait for events before doing anything. - #events = [pygame.event.wait()] + pygame.event.get() - events = pygame.event.get() + # events = [pg.event.wait()] + pg.event.get() + events = pg.event.get() + + # During the loop, if a key is held, scroll the view. + keys = pg.key.get_pressed() + if keys[pg.K_UP]: + scroll_view(screen, image, DIR_UP, view_rect) + if keys[pg.K_DOWN]: + scroll_view(screen, image, DIR_DOWN, view_rect) + if keys[pg.K_LEFT]: + scroll_view(screen, image, DIR_LEFT, view_rect) + if keys[pg.K_RIGHT]: + scroll_view(screen, image, DIR_RIGHT, view_rect) for e in events: - if e.type == KEYDOWN: - if e.key == K_ESCAPE: - going = False - elif e.key == K_DOWN: - scroll_view(screen, image, DIR_DOWN, view_rect) - elif e.key == K_UP: - scroll_view(screen, image, DIR_UP, view_rect) - elif e.key == K_LEFT: - scroll_view(screen, image, DIR_LEFT, view_rect) - elif e.key == K_RIGHT: - scroll_view(screen, image, DIR_RIGHT, view_rect) - elif e.type == QUIT: + # quit if the event is quit. + if e.type == pg.QUIT: going = False - elif e.type == MOUSEBUTTONDOWN: + + # handle mouse button presses on arrows. + elif e.type == pg.MOUSEBUTTONDOWN: direction = regions.get_at(e.pos)[0] - elif e.type == MOUSEBUTTONUP: + + elif e.type == pg.MOUSEBUTTONUP: direction = None if direction: @@ -184,12 +188,10 @@ def main(image_file=None): clock.tick(30) finally: - pygame.key.set_repeat (old_k_delay, old_k_interval) - pygame.quit() + pg.key.set_repeat(old_k_delay, old_k_interval) + pg.quit() -if __name__ == '__main__': - if len(sys.argv) > 1: - image_file = sys.argv[1] - else: - image_file = None + +if __name__ == "__main__": + image_file = sys.argv[1] if len(sys.argv) > 1 else None main(image_file) diff --git a/venv/Lib/site-packages/pygame/examples/sound.py b/venv/Lib/site-packages/pygame/examples/sound.py index 6fa32f9c50c12b193df89d00234aa786cbf6fb7b..c5a23b97c06bce89aa0e4ed7c2df065650b5a954 100644 --- a/venv/Lib/site-packages/pygame/examples/sound.py +++ b/venv/Lib/site-packages/pygame/examples/sound.py @@ -1,57 +1,45 @@ #!/usr/bin/env python +""" pygame.examples.sound -"""extremely simple demonstration playing a soundfile -and waiting for it to finish. you'll need the pygame.mixer -module for this to work. Note how in this simple example we -don't even bother loading all of the pygame package. Just -pick the mixer for sound and time for the delay function. - -Optional command line argument: - the name of an audio file. - +Playing a soundfile and waiting for it to finish. You'll need the +pygame.mixer module for this to work. Note how in this simple example +we don't even bother loading all of the pygame package. +Just pick the mixer for sound and time for the delay function. +Optional command line argument: audio file name """ - -import os.path, sys -import pygame.mixer, pygame.time -mixer = pygame.mixer -time = pygame.time +import os +import sys +import pygame as pg main_dir = os.path.split(os.path.abspath(__file__))[0] + def main(file_path=None): """Play an audio file as a buffered sound sample - Option argument: - the name of an audio file (default data/secosmic_low.wav - + :param str file_path: audio file (default data/secosmic_low.wav) """ - if file_path is None: - file_path = os.path.join(main_dir, - 'data', - 'secosmic_lo.wav') - - #choose a desired audio format - mixer.init(11025) #raises exception on fail - - - #load the sound - sound = mixer.Sound(file_path) + # choose a desired audio format + pg.mixer.init(11025) # raises exception on fail + # load the sound + sound = pg.mixer.Sound(file_path) - #start playing - print ('Playing Sound...') + # start playing + print("Playing Sound...") channel = sound.play() + # poll until finished + while channel.get_busy(): # still playing + print(" ...still going...") + pg.time.wait(1000) + print("...Finished") + pg.quit() - #poll until finished - while channel.get_busy(): #still playing - print (' ...still going...') - time.wait(1000) - print ('...Finished') -if __name__ == '__main__': +if __name__ == "__main__": if len(sys.argv) > 1: main(sys.argv[1]) else: - main() + main(os.path.join(main_dir, "data", "secosmic_lo.wav")) diff --git a/venv/Lib/site-packages/pygame/examples/sound_array_demos.py b/venv/Lib/site-packages/pygame/examples/sound_array_demos.py index 142ed7b7607a9e15021d9895eb1e5efa4c109bef..855808b5f968808153b402a3c128c14bd2eb4ea1 100644 --- a/venv/Lib/site-packages/pygame/examples/sound_array_demos.py +++ b/venv/Lib/site-packages/pygame/examples/sound_array_demos.py @@ -1,262 +1,217 @@ #!/usr/bin/env python -""" -Creates an echo effect an any Sound object. +""" pygame.examples.sound_array_demos + +Creates an echo effect on any Sound object. -Uses sndarray and MumPy ( or Numeric) to create offset faded copies of the +Uses sndarray and numpy to create offset faded copies of the original sound. Currently it just uses hardcoded values for the -number of echos and the delay. Easy for you to recreate as -needed. The array packaged used can be specified by an optional ---numpy or --numeric command line option. +number of echos and the delay. Easy for you to recreate as +needed. version 2. changes: - Should work with different sample rates now. - put into a function. -- Uses NumPy by default, but falls back on Numeric. - +- Uses numpy by default, but falls back on Numeric. """ - -__author__ = "Pete 'ShredWheat' Shinners, Rene Dudfield" -__copyright__ = "Copyright (C) 2004 Pete Shinners, Copyright (C) 2005 Rene Dudfield" -__license__ = "Public Domain" -__version__ = "2.0" - - -import os.path -import pygame.mixer, pygame.time, pygame.sndarray, pygame -import pygame.surfarray, pygame.transform -from pygame import sndarray, mixer - +import os +import pygame as pg from numpy import zeros, int32, int16 - import time -#mixer.init(44100, -16, 0) -mixer.init() -#mixer.init(11025, -16, 0) -#mixer.init(11025) +# pg.mixer.init(44100, -16, 0) +pg.mixer.init() +# pg.mixer.init(11025, -16, 0) +# pg.mixer.init(11025) - -def make_echo(sound, samples_per_second, mydebug = True): - """ returns a sound which is echoed of the last one. - """ +def make_echo(sound, samples_per_second, mydebug=True): + """returns a sound which is echoed of the last one.""" echo_length = 3.5 - a1 = sndarray.array(sound) + a1 = pg.sndarray.array(sound) if mydebug: - print ('SHAPE1: %s' % (a1.shape,)) + print(f"SHAPE1: {a1.shape}") length = a1.shape[0] - #myarr = zeros(length+12000) + # myarr = zeros(length+12000) myarr = zeros(a1.shape, int32) if len(a1.shape) > 1: - mult = a1.shape[1] + # mult = a1.shape[1] size = (a1.shape[0] + int(echo_length * a1.shape[0]), a1.shape[1]) - #size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)), a1.shape[1]) + # size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)), a1.shape[1]) else: - mult = 1 + # mult = 1 size = (a1.shape[0] + int(echo_length * a1.shape[0]),) - #size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)),) + # size = (a1.shape[0] + int(a1.shape[0] + (echo_length * 3000)),) if mydebug: - print (int(echo_length * a1.shape[0])) + print(int(echo_length * a1.shape[0])) myarr = zeros(size, int32) - - if mydebug: - print ("size %s" % (size,)) - print (myarr.shape) + print(f"size {size}") + print(myarr.shape) myarr[:length] = a1 - #print (myarr[3000:length+3000]) - #print (a1 >> 1) - #print ("a1.shape %s" % (a1.shape,)) - #c = myarr[3000:length+(3000*mult)] - #print ("c.shape %s" % (c.shape,)) + # print(myarr[3000:length+3000]) + # print(a1 >> 1) + # print("a1.shape %s" % (a1.shape,)) + # c = myarr[3000:length+(3000*mult)] + # print("c.shape %s" % (c.shape,)) incr = int(samples_per_second / echo_length) gap = length - - myarr[incr:gap+incr] += a1>>1 - myarr[incr*2:gap+(incr*2)] += a1>>2 - myarr[incr*3:gap+(incr*3)] += a1>>3 - myarr[incr*4:gap+(incr*4)] += a1>>4 + myarr[incr : gap + incr] += a1 >> 1 + myarr[incr * 2 : gap + (incr * 2)] += a1 >> 2 + myarr[incr * 3 : gap + (incr * 3)] += a1 >> 3 + myarr[incr * 4 : gap + (incr * 4)] += a1 >> 4 if mydebug: - print ('SHAPE2: %s' % (myarr.shape,)) - + print(f"SHAPE2: {myarr.shape}") - sound2 = sndarray.make_sound(myarr.astype(int16)) + sound2 = pg.sndarray.make_sound(myarr.astype(int16)) return sound2 def slow_down_sound(sound, rate): - """ returns a sound which is a slowed down version of the original. - rate - at which the sound should be slowed down. eg. 0.5 would be half speed. + """returns a sound which is a slowed down version of the original. + rate - at which the sound should be slowed down. eg. 0.5 would be half speed. """ raise NotImplementedError() - grow_rate = 1 / rate - + # grow_rate = 1 / rate # make it 1/rate times longer. - - a1 = sndarray.array(sound) - - surf = pygame.surfarray.make_surface(a1) - print (a1.shape[0] * grow_rate) - scaled_surf = pygame.transform.scale(surf, (int(a1.shape[0] * grow_rate), a1.shape[1])) - print (scaled_surf) - print (surf) - - a2 = a1 * rate - print (a1.shape) - print (a2.shape) - print (a2) - sound2 = sndarray.make_sound(a2.astype(int16)) - return sound2 - - - - -def sound_from_pos(sound, start_pos, samples_per_second = None, inplace = 1): - """ returns a sound which begins at the start_pos. - start_pos - in seconds from the begining. - samples_per_second - + # a1 = pg.sndarray.array(sound) + # surf = pg.surfarray.make_surface(a1) + # print(a1.shape[0] * grow_rate) + # scaled_surf = pg.transform.scale(surf, (int(a1.shape[0] * grow_rate), a1.shape[1])) + # print(scaled_surf) + # print(surf) + + # a2 = a1 * rate + # print(a1.shape) + # print(a2.shape) + # print(a2) + # sound2 = pg.sndarray.make_sound(a2.astype(int16)) + # return sound2 + + +def sound_from_pos(sound, start_pos, samples_per_second=None, inplace=1): + """returns a sound which begins at the start_pos. + start_pos - in seconds from the beginning. + samples_per_second - """ # see if we want to reuse the sound data or not. if inplace: - a1 = pygame.sndarray.samples(sound) + a1 = pg.sndarray.samples(sound) else: - a1 = pygame.sndarray.array(sound) + a1 = pg.sndarray.array(sound) - # see if samples per second has been given. If not, query the mixer. + # see if samples per second has been given. If not, query the pg.mixer. # eg. it might be set to 22050 if samples_per_second is None: - samples_per_second = pygame.mixer.get_init()[0] + samples_per_second = pg.mixer.get_init()[0] # figure out the start position in terms of samples. start_pos_in_samples = int(start_pos * samples_per_second) - # cut the begining off the sound at the start position. + # cut the beginning off the sound at the start position. a2 = a1[start_pos_in_samples:] # make the Sound instance from the array. - sound2 = pygame.sndarray.make_sound(a2) + sound2 = pg.sndarray.make_sound(a2) return sound2 - - - -def main(arraytype=None): - """play various sndarray effects - If arraytype is provided then use that array package. Valid - values are 'numeric' or 'numpy'. Otherwise default to NumPy, - or fall back on Numeric if NumPy is not installed. - - """ +def main(): + """play various sndarray effects""" main_dir = os.path.split(os.path.abspath(__file__))[0] + print(f"mixer.get_init {pg.mixer.get_init()}") - if arraytype not in ('numpy', None): - raise ValueError('Array type not supported: %r' % arraytype) - - print ("Using %s array package" % sndarray.get_arraytype()) - print ("mixer.get_init %s" % (mixer.get_init(),)) - inited = mixer.get_init() + samples_per_second = pg.mixer.get_init()[0] - samples_per_second = pygame.mixer.get_init()[0] + print(("-" * 30) + "\n") + print("loading sound") + sound = pg.mixer.Sound(os.path.join(main_dir, "data", "car_door.wav")) - - - print (("-" * 30) + "\n") - print ("loading sound") - sound = mixer.Sound(os.path.join(main_dir, 'data', 'car_door.wav')) - - - - print ("-" * 30) - print ("start positions") - print ("-" * 30) + print("-" * 30) + print("start positions") + print("-" * 30) start_pos = 0.1 sound2 = sound_from_pos(sound, start_pos, samples_per_second) - print ("sound.get_length %s" % (sound.get_length(),)) - print ("sound2.get_length %s" % (sound2.get_length(),)) + print(f"sound.get_length {sound.get_length()}") + print(f"sound2.get_length {sound2.get_length()}") sound2.play() - while mixer.get_busy(): - pygame.time.wait(200) + while pg.mixer.get_busy(): + pg.time.wait(200) - print ("waiting 2 seconds") - pygame.time.wait(2000) - print ("playing original sound") + print("waiting 2 seconds") + pg.time.wait(2000) + print("playing original sound") sound.play() - while mixer.get_busy(): - pygame.time.wait(200) - - print ("waiting 2 seconds") - pygame.time.wait(2000) - - - - if 0: - #TODO: this is broken. - print (("-" * 30) + "\n") - print ("Slow down the original sound.") - rate = 0.2 - slowed_sound = slow_down_sound(sound, rate) - - slowed_sound.play() - while mixer.get_busy(): - pygame.time.wait(200) - - - print ("-" * 30) - print ("echoing") - print ("-" * 30) + while pg.mixer.get_busy(): + pg.time.wait(200) + + print("waiting 2 seconds") + pg.time.wait(2000) + + # if 0: + # #TODO: this is broken. + # print(("-" * 30) + "\n") + # print("Slow down the original sound.") + # rate = 0.2 + # slowed_sound = slow_down_sound(sound, rate) + # slowed_sound.play() + # while pg.mixer.get_busy(): + # pg.time.wait(200) + + print("-" * 30) + print("echoing") + print("-" * 30) t1 = time.time() sound2 = make_echo(sound, samples_per_second) - print ("time to make echo %i" % (time.time() - t1,)) + print("time to make echo %i" % (time.time() - t1,)) - - print ("original sound") + print("original sound") sound.play() - while mixer.get_busy(): - pygame.time.wait(200) + while pg.mixer.get_busy(): + pg.time.wait(200) - print ("echoed sound") + print("echoed sound") sound2.play() - while mixer.get_busy(): - pygame.time.wait(200) - + while pg.mixer.get_busy(): + pg.time.wait(200) - sound = mixer.Sound(os.path.join(main_dir, 'data', 'secosmic_lo.wav')) + sound = pg.mixer.Sound(os.path.join(main_dir, "data", "secosmic_lo.wav")) t1 = time.time() sound3 = make_echo(sound, samples_per_second) - print ("time to make echo %i" % (time.time() - t1,)) + print("time to make echo %i" % (time.time() - t1,)) - print ("original sound") + print("original sound") sound.play() - while mixer.get_busy(): - pygame.time.wait(200) - + while pg.mixer.get_busy(): + pg.time.wait(200) - print ("echoed sound") + print("echoed sound") sound3.play() - while mixer.get_busy(): - pygame.time.wait(200) + while pg.mixer.get_busy(): + pg.time.wait(200) + + pg.quit() + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/stars.py b/venv/Lib/site-packages/pygame/examples/stars.py index 22e5ef69bd93355755b0084b74da42bc88797a46..f154511eb7431e3e91b86585d6094745cf5d8a06 100644 --- a/venv/Lib/site-packages/pygame/examples/stars.py +++ b/venv/Lib/site-packages/pygame/examples/stars.py @@ -1,47 +1,48 @@ #!/usr/bin/env python +""" pg.examples.stars -"""A simple starfield example. Note you can move the 'center' of + We are all in the gutter, + but some of us are looking at the stars. + -- Oscar Wilde + +A simple starfield example. Note you can move the 'center' of the starfield by leftclicking in the window. This example show the basics of creating a window, simple pixel plotting, and input -event management""" - - -import random, math, pygame -from pygame.locals import * +event management. +""" +import random +import math +import pygame as pg -#constants +# constants WINSIZE = [640, 480] WINCENTER = [320, 240] NUMSTARS = 150 -def init_star(): +def init_star(steps=-1): "creates new star values" dir = random.randrange(100000) - velmult = random.random()*.6+.4 + steps_velocity = 1 if steps == -1 else steps * 0.09 + velmult = steps_velocity * (random.random() * 0.6 + 0.4) vel = [math.sin(dir) * velmult, math.cos(dir) * velmult] - return vel, WINCENTER[:] + + if steps is None: + return [vel, [WINCENTER[0] + (vel[0] * steps), WINCENTER[1] + (vel[1] * steps)]] + return [vel, WINCENTER[:]] def initialize_stars(): "creates a new starfield" - stars = [] - for x in range(NUMSTARS): - star = init_star() - vel, pos = star - steps = random.randint(0, WINCENTER[0]) - pos[0] = pos[0] + (vel[0] * steps) - pos[1] = pos[1] + (vel[1] * steps) - vel[0] = vel[0] * (steps * .09) - vel[1] = vel[1] * (steps * .09) - stars.append(star) + random.seed() + stars = [init_star(steps=random.randint(0, WINCENTER[0])) for _ in range(NUMSTARS)] move_stars(stars) return stars - + def draw_stars(surface, stars, color): "used to draw (and clear) the stars" - for vel, pos in stars: + for _, pos in stars: pos = (int(pos[0]), int(pos[1])) surface.set_at(pos, color) @@ -56,40 +57,44 @@ def move_stars(stars): else: vel[0] = vel[0] * 1.05 vel[1] = vel[1] * 1.05 - + def main(): "This is the starfield code" - #create our starfield - random.seed() + # create our starfield stars = initialize_stars() - clock = pygame.time.Clock() - #initialize and prepare screen - pygame.init() - screen = pygame.display.set_mode(WINSIZE) - pygame.display.set_caption('pygame Stars Example') + + # initialize and prepare screen + pg.init() + screen = pg.display.set_mode(WINSIZE) + pg.display.set_caption("pygame Stars Example") white = 255, 240, 200 black = 20, 20, 40 screen.fill(black) - #main game loop + clock = pg.time.Clock() + + # main game loop done = 0 while not done: draw_stars(screen, stars, black) move_stars(stars) draw_stars(screen, stars, white) - pygame.display.update() - for e in pygame.event.get(): - if e.type == QUIT or (e.type == KEYUP and e.key == K_ESCAPE): + pg.display.update() + for e in pg.event.get(): + if e.type == pg.QUIT or (e.type == pg.KEYUP and e.key == pg.K_ESCAPE): done = 1 break - elif e.type == MOUSEBUTTONDOWN and e.button == 1: + if e.type == pg.MOUSEBUTTONDOWN and e.button == 1: WINCENTER[:] = list(e.pos) clock.tick(50) + pg.quit() -# if python says run, then we should run -if __name__ == '__main__': +# So `python -m pygame.example.stars` will work. +if __name__ == "__main__": main() - + # I prefer the time of insects to the time of stars. + # + # -- Wisława Szymborska diff --git a/venv/Lib/site-packages/pygame/examples/testsprite.py b/venv/Lib/site-packages/pygame/examples/testsprite.py index 123c0c11790aa9995906efa77902b0f6db7a5eea..40469b7dc3cd651d6884a28fee871993e2d4089b 100644 --- a/venv/Lib/site-packages/pygame/examples/testsprite.py +++ b/venv/Lib/site-packages/pygame/examples/testsprite.py @@ -1,31 +1,25 @@ #!/usr/bin/env python -# like the testsprite.c that comes with sdl, this pygame version shows -# lots of sprites moving around. +""" pg.examples.testsprite +Like the testsprite.c that comes with libsdl, this pygame version shows +lots of sprites moving around. -import pygame, sys, os -from pygame.locals import * -from random import randint -from time import time -import pygame.joystick -from pygame.compat import xrange_ +It is an abomination of ugly code, and mostly used for testing. -##import FastRenderGroup as FRG -import pygame.sprite as FRG - -if "-psyco" in sys.argv: - try: - import psyco - psyco.full() - except Exception: - print ("No psyco for you! psyco failed to import and run.") - -main_dir = os.path.split(os.path.abspath(__file__))[0] -data_dir = os.path.join(main_dir, 'data') +See pg.examples.aliens for some prettyier code. +""" +import sys +import os +from random import randint +from time import time +from typing import List +import pygame as pg +main_dir = os.path.split(os.path.abspath(__file__))[0] +data_dir = os.path.join(main_dir, "data") # use this to use update rects or not. @@ -41,51 +35,50 @@ if "-static" in sys.argv: use_static = True -use_FastRenderGroup = False -if "-FastRenderGroup" in sys.argv: +use_layered_dirty = False +if "-layered_dirty" in sys.argv: update_rects = True - use_FastRenderGroup = True + use_layered_dirty = True flags = 0 if "-flip" in sys.argv: - flags ^= DOUBLEBUF + flags ^= pg.DOUBLEBUF if "-fullscreen" in sys.argv: - flags ^= FULLSCREEN + flags ^= pg.FULLSCREEN if "-sw" in sys.argv: - flags ^= SWSURFACE + flags ^= pg.SWSURFACE use_rle = True if "-hw" in sys.argv: - flags ^= HWSURFACE + flags ^= pg.HWSURFACE use_rle = False +if "-scaled" in sys.argv: + flags ^= pg.SCALED screen_dims = [640, 480] if "-height" in sys.argv: i = sys.argv.index("-height") - screen_dims[1] = int(sys.argv[i+1]) + screen_dims[1] = int(sys.argv[i + 1]) if "-width" in sys.argv: i = sys.argv.index("-width") - screen_dims[0] = int(sys.argv[i+1]) + screen_dims[0] = int(sys.argv[i + 1]) -if "-alpha" in sys.argv: - use_alpha = True -else: - use_alpha = False +use_alpha = "-alpha" in sys.argv -print (screen_dims) +print(screen_dims) -##class Thingy(pygame.sprite.Sprite): +##class Thingy(pg.sprite.Sprite): ## images = None ## def __init__(self): -## pygame.sprite.Sprite.__init__(self) +## pg.sprite.Sprite.__init__(self) ## self.image = Thingy.images[0] ## self.rect = self.image.get_rect() ## self.rect.x = randint(0, screen_dims[0]) @@ -101,16 +94,18 @@ print (screen_dims) ## nv = self.rect[i] + self.vel[i] ## self.rect[i] = nv -class Thingy(FRG.DirtySprite): - images = None + +class Thingy(pg.sprite.DirtySprite): + images: List[pg.Surface] = [] + def __init__(self): -## pygame.sprite.Sprite.__init__(self) - FRG.DirtySprite.__init__(self) + ## pg.sprite.Sprite.__init__(self) + pg.sprite.DirtySprite.__init__(self) self.image = Thingy.images[0] self.rect = self.image.get_rect() self.rect.x = randint(0, screen_dims[0]) self.rect.y = randint(0, screen_dims[1]) - #self.vel = [randint(-10, 10), randint(-10, 10)] + # self.vel = [randint(-10, 10), randint(-10, 10)] self.vel = [randint(-1, 1), randint(-1, 1)] self.dirty = 2 @@ -122,69 +117,66 @@ class Thingy(FRG.DirtySprite): nv = self.rect[i] + self.vel[i] self.rect[i] = nv -class Static(FRG.DirtySprite): - images = None + +class Static(pg.sprite.DirtySprite): + images: List[pg.Surface] = [] + def __init__(self): - FRG.DirtySprite.__init__(self) + pg.sprite.DirtySprite.__init__(self) self.image = Static.images[0] self.rect = self.image.get_rect() - self.rect.x = randint(0, 3*screen_dims[0]/4) - self.rect.y = randint(0, 3*screen_dims[1]/4) - - - -def main(update_rects = True, - use_static = False, - use_FastRenderGroup = False, - screen_dims = [640, 480], - use_alpha = False, - flags = 0, - ): + self.rect.x = randint(0, 3 * screen_dims[0] // 4) + self.rect.y = randint(0, 3 * screen_dims[1] // 4) + + +def main( + update_rects=True, + use_static=False, + use_layered_dirty=False, + screen_dims=(640, 480), + use_alpha=False, + flags=0, +): """Show lots of sprites moving around Optional keyword arguments: update_rects - use the RenderUpdate sprite group class (default True) use_static - include non-moving images (default False) - use_FastRenderGroup - Use the FastRenderGroup sprite group (default False) + use_layered_dirty - Use the FastRenderGroup sprite group (default False) screen_dims - Pygame window dimensions (default [640, 480]) use_alpha - use alpha blending (default False) - flags - additional display mode flags (default no addiontal flags) + flags - additional display mode flags (default no additional flags) """ - if use_FastRenderGroup: + if use_layered_dirty: update_rects = True + pg.init() # needed to initialise time module for get_ticks() + pg.display.init() - #pygame.init() - pygame.display.init() - - - - #if "-fast" in sys.argv: - - screen = pygame.display.set_mode(screen_dims, flags) + # if "-fast" in sys.argv: + screen = pg.display.set_mode(screen_dims, flags, vsync="-vsync" in sys.argv) # this is mainly for GP2X, so it can quit. - pygame.joystick.init() - num_joysticks = pygame.joystick.get_count() + pg.joystick.init() + num_joysticks = pg.joystick.get_count() if num_joysticks > 0: - stick = pygame.joystick.Joystick(0) - stick.init() # now we will receive events for the joystick + stick = pg.joystick.Joystick(0) + stick.init() # now we will receive events for the joystick - - screen.fill([0,0,0]) - pygame.display.flip() - sprite_surface = pygame.image.load(os.path.join(data_dir, "asprite.bmp")) - sprite_surface2 = pygame.image.load(os.path.join(data_dir, "static.png")) + screen.fill([0, 0, 0]) + pg.display.flip() + sprite_surface = pg.image.load(os.path.join(data_dir, "asprite.bmp")) + sprite_surface2 = pg.image.load(os.path.join(data_dir, "static.png")) if use_rle: - sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], SRCCOLORKEY|RLEACCEL) - sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], SRCCOLORKEY|RLEACCEL) + sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY | pg.RLEACCEL) + sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY | pg.RLEACCEL) else: - sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], SRCCOLORKEY) - sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], SRCCOLORKEY) + sprite_surface.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY) + sprite_surface2.set_colorkey([0xFF, 0xFF, 0xFF], pg.SRCCOLORKEY) if use_alpha: sprite_surface = sprite_surface.convert_alpha() @@ -196,7 +188,7 @@ def main(update_rects = True, Thingy.images = [sprite_surface] if use_static: Static.images = [sprite_surface2] - + if len(sys.argv) > 1: try: numsprites = int(sys.argv[-1]) @@ -205,35 +197,34 @@ def main(update_rects = True, else: numsprites = 100 sprites = None - if use_FastRenderGroup: -## sprites = FRG.FastRenderGroup() - sprites = FRG.LayeredDirty() + if use_layered_dirty: + ## sprites = pg.sprite.FastRenderGroup() + sprites = pg.sprite.LayeredDirty() else: if update_rects: - sprites = pygame.sprite.RenderUpdates() + sprites = pg.sprite.RenderUpdates() else: - sprites = pygame.sprite.Group() + sprites = pg.sprite.Group() - for i in xrange_(0, numsprites): - if use_static and i%2==0: + for i in range(0, numsprites): + if use_static and i % 2 == 0: sprites.add(Static()) sprites.add(Thingy()) - done = False frames = 0 start = time() - background = pygame.Surface(screen.get_size()) + background = pg.Surface(screen.get_size()) background = background.convert() - background.fill([0,0,0]) - + background.fill([0, 0, 0]) - while not done: + going = True + while going: if not update_rects: - screen.fill([0,0,0]) + screen.fill([0, 0, 0]) -## for sprite in sprites: -## sprite.move() + ## for sprite in sprites: + ## sprite.move() if update_rects: sprites.clear(screen, background) @@ -241,27 +232,19 @@ def main(update_rects = True, rects = sprites.draw(screen) if update_rects: - pygame.display.update(rects) + pg.display.update(rects) else: - pygame.display.flip() - - - for event in pygame.event.get(): - if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]: - done = True + pg.display.flip() + for event in pg.event.get(): + if event.type in [pg.QUIT, pg.KEYDOWN, pg.QUIT, pg.JOYBUTTONDOWN]: + going = False frames += 1 end = time() - print ("FPS: %f" % (frames / ((end - start)))) - pygame.quit() - + print(f"FPS: {frames / (end - start):f}") + pg.quit() if __name__ == "__main__": - main( update_rects, - use_static, - use_FastRenderGroup, - screen_dims, - use_alpha, - flags ) + main(update_rects, use_static, use_layered_dirty, screen_dims, use_alpha, flags) diff --git a/venv/Lib/site-packages/pygame/examples/textinput.py b/venv/Lib/site-packages/pygame/examples/textinput.py index 6bb61246248b60fab6a7dcca378d7b13fa6b95ce..8852a8fabf67c4765ac49f346c16b8a862484edd 100644 --- a/venv/Lib/site-packages/pygame/examples/textinput.py +++ b/venv/Lib/site-packages/pygame/examples/textinput.py @@ -1,151 +1,235 @@ -import pygame, sys -import pygame.freetype - -###CONSTS -# Set to true or add 'showevent' in argv to see IME and KEYDOWN events -PRINT_EVENT = False -# frames per second, the general speed of the program -FPS = 50 -# size of window -WINDOWWIDTH, WINDOWHEIGHT = 640, 480 -BGCOLOR = (0, 0, 0) - -# position of chatlist and chatbox -CHATLIST_POS = pygame.Rect(0, 20, WINDOWWIDTH, 400) -CHATBOX_POS = pygame.Rect(0, 440, WINDOWWIDTH, 40) -CHATLIST_MAXSIZE = 20 - -TEXTCOLOR = (0,255,0) - -#Add fontname for each language, otherwise some text can't be correctly displayed. -FONTNAMES = ["notosanscjktcregular", "notosansmonocjktcregular" , - "notosansregular,", +#!/usr/bin/env python +""" pg.examples.textinput + +A little "console" where you can write in text. + +Shows how to use the TEXTEDITING and TEXTINPUT events. +""" +import sys +import os +from typing import List + +import pygame +import pygame as pg +import pygame.freetype as freetype + +# This environment variable is important +# If not added the candidate list will not show +os.environ["SDL_IME_SHOW_UI"] = "1" + + +class TextInput: + """ + A simple TextInput class that allows you to receive inputs in pygame. + """ + + # Add font name for each language, + # otherwise some text can't be correctly displayed. + FONT_NAMES = ",".join( + str(x) + for x in [ + "notosanscjktcregular", + "notosansmonocjktcregular", + "notosansregular,", "microsoftjhengheimicrosoftjhengheiuilight", "microsoftyaheimicrosoftyaheiuilight", "msgothicmsuigothicmspgothic", "msmincho", - "Arial"] - -#Version check -if (pygame.get_sdl_version() < (2,0,0)): - raise Exception("This example requires SDL2.") - -#Initalize -pygame.init() -Screen = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT)) -pygame.display.set_caption("TextInput example") -FPSClock = pygame.time.Clock() - -#Freetype -#"The font name can be a comma separated list of font names to search for." -FONTNAMES = ",".join(str(x) for x in FONTNAMES) -Font = pygame.freetype.SysFont(FONTNAMES, 24) -FontSmall = pygame.freetype.SysFont(FONTNAMES, 16) -print("Using font: " + Font.name) - -#Main loop process -def main(): - global BGCOLOR, PRINT_EVENT, CHATBOX_POS, CHATLIST_POS, CHATLIST_MAXSIZE - global FPSClock , Font, Screen - - """ - https://wiki.libsdl.org/SDL_HINT_IME_INTERNAL_EDITING - https://wiki.libsdl.org/Tutorials/TextInput - Candidate list not showing due to SDL2 problem ;w; - """ - pygame.key.start_text_input() - input_rect = pygame.Rect(80,80,320,40) - pygame.key.set_text_input_rect(input_rect) - - _IMEEditing = False - _IMEText = "" - _IMETextPos = 0 - _IMEEditingText = "" - _IMEEditingPos = 0 - ChatList = [] - - while True: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - pygame.quit() - return - - elif event.type == pygame.KEYDOWN: - if (PRINT_EVENT): + "Arial", + ] + ) + + def __init__( + self, prompt: str, pos, screen_dimensions, print_event: bool, text_color="white" + ) -> None: + self.prompt = prompt + self.print_event = print_event + # position of chatlist and chatbox + self.CHAT_LIST_POS = pg.Rect((pos[0], pos[1] + 50), (screen_dimensions[0], 400)) + self.CHAT_BOX_POS = pg.Rect(pos, (screen_dimensions[1], 40)) + self.CHAT_LIST_MAXSIZE = 20 + + self._ime_editing = False + self._ime_text = "" + self._ime_text_pos = 0 + self._ime_editing_text = "" + self._ime_editing_pos = 0 + self.chat_list: List[str] = [] + + # Freetype + # The font name can be a comma separated list + # of font names to search for. + self.font = freetype.SysFont(self.FONT_NAMES, 24) + self.font_small = freetype.SysFont(self.FONT_NAMES, 16) + self.text_color = text_color + + print("Using font: " + self.font.name) + + def update(self, events) -> None: + """ + Updates the text input widget + """ + for event in events: + if event.type == pg.KEYDOWN: + if self.print_event: print(event) - if _IMEEditing: - if (len(_IMEEditingText) == 0): - _IMEEditing = False + if self._ime_editing: + if len(self._ime_editing_text) == 0: + self._ime_editing = False continue - - if event.key == pygame.K_BACKSPACE: - if (len(_IMEText) > 0 and _IMETextPos > 0): - _IMEText = _IMEText[0:_IMETextPos-1] + _IMEText[_IMETextPos:] - _IMETextPos = max(0,_IMETextPos-1) - - elif event.key == pygame.K_DELETE: - _IMEText = _IMEText[0:_IMETextPos] + _IMEText[_IMETextPos+1:] - elif event.key == pygame.K_LEFT: - _IMETextPos = max(0,_IMETextPos-1) - elif event.key == pygame.K_RIGHT: - _IMETextPos = min(len(_IMEText),_IMETextPos+1) - - elif event.key in [pygame.K_RETURN, pygame.K_KP_ENTER] and len(event.unicode) == 0: - #Block if we have no text to append - if len(_IMEText) == 0: + + if event.key == pg.K_BACKSPACE: + if len(self._ime_text) > 0 and self._ime_text_pos > 0: + self._ime_text = ( + self._ime_text[0 : self._ime_text_pos - 1] + + self._ime_text[self._ime_text_pos :] + ) + self._ime_text_pos = max(0, self._ime_text_pos - 1) + + elif event.key == pg.K_DELETE: + self._ime_text = ( + self._ime_text[0 : self._ime_text_pos] + + self._ime_text[self._ime_text_pos + 1 :] + ) + elif event.key == pg.K_LEFT: + self._ime_text_pos = max(0, self._ime_text_pos - 1) + elif event.key == pg.K_RIGHT: + self._ime_text_pos = min( + len(self._ime_text), self._ime_text_pos + 1 + ) + # Handle ENTER key + elif event.key in [pg.K_RETURN, pg.K_KP_ENTER]: + # Block if we have no text to append + if len(self._ime_text) == 0: continue - #Append chat list - ChatList.append(_IMEText) - if (len(ChatList) > CHATLIST_MAXSIZE): - ChatList.pop(0) - _IMEText = "" - _IMETextPos = 0 - - elif event.type == pygame.TEXTEDITING: - if (PRINT_EVENT): + # Append chat list + self.chat_list.append(self._ime_text) + if len(self.chat_list) > self.CHAT_LIST_MAXSIZE: + self.chat_list.pop(0) + self._ime_text = "" + self._ime_text_pos = 0 + + elif event.type == pg.TEXTEDITING: + if self.print_event: print(event) - _IMEEditing = True - _IMEEditingText = event.text - _IMEEditingPos = event.start + self._ime_editing = True + self._ime_editing_text = event.text + self._ime_editing_pos = event.start - elif event.type == pygame.TEXTINPUT: - if (PRINT_EVENT): + elif event.type == pg.TEXTINPUT: + if self.print_event: print(event) - _IMEEditing = False - _IMEEditingText = "" - _IMEText = _IMEText[0:_IMETextPos] + event.text + _IMEText[_IMETextPos:] - _IMETextPos += len(event.text) - - #Screen updates - Screen.fill(BGCOLOR) - - #Chat List updates - chat_height = CHATLIST_POS.height / CHATLIST_MAXSIZE - for i in range(len(ChatList)): - FontSmall.render_to(Screen, (CHATLIST_POS.x, CHATLIST_POS.y + i*chat_height), ChatList[i], TEXTCOLOR) - - #Chat box updates - start_pos = CHATBOX_POS.copy() - ime_textL = ">" + _IMEText[0:_IMETextPos] - ime_textM = _IMEEditingText[0:_IMEEditingPos] + "|" + _IMEEditingText[_IMEEditingPos:] - ime_textR = _IMEText[_IMETextPos:] - - rect_textL = Font.render_to(Screen, start_pos, ime_textL, TEXTCOLOR) - start_pos.x += rect_textL.width - - #Editing texts should be underlined - rect_textM = Font.render_to(Screen, start_pos, ime_textM, TEXTCOLOR, None, pygame.freetype.STYLE_UNDERLINE) - start_pos.x += rect_textM.width - rect_textr = Font.render_to(Screen, start_pos, ime_textR, TEXTCOLOR) - - pygame.display.update() - - FPSClock.tick(FPS) - -if __name__ == '__main__': - if 'showevent' in sys.argv: - PRINT_EVENT = True - + self._ime_editing = False + self._ime_editing_text = "" + self._ime_text = ( + self._ime_text[0 : self._ime_text_pos] + + event.text + + self._ime_text[self._ime_text_pos :] + ) + self._ime_text_pos += len(event.text) + + def draw(self, screen: pygame.Surface) -> None: + """ + Draws the text input widget onto the provided surface + """ + + # Chat List updates + chat_height = self.CHAT_LIST_POS.height / self.CHAT_LIST_MAXSIZE + for i, chat in enumerate(self.chat_list): + self.font_small.render_to( + screen, + (self.CHAT_LIST_POS.x, self.CHAT_LIST_POS.y + i * chat_height), + chat, + self.text_color, + ) + + # Chat box updates + start_pos = self.CHAT_BOX_POS.copy() + ime_text_l = self.prompt + self._ime_text[0 : self._ime_text_pos] + ime_text_m = ( + self._ime_editing_text[0 : self._ime_editing_pos] + + "|" + + self._ime_editing_text[self._ime_editing_pos :] + ) + ime_text_r = self._ime_text[self._ime_text_pos :] + + rect_text_l = self.font.render_to( + screen, start_pos, ime_text_l, self.text_color + ) + start_pos.x += rect_text_l.width + + # Editing texts should be underlined + rect_text_m = self.font.render_to( + screen, + start_pos, + ime_text_m, + self.text_color, + None, + freetype.STYLE_UNDERLINE, + ) + start_pos.x += rect_text_m.width + self.font.render_to(screen, start_pos, ime_text_r, self.text_color) + + +class Game: + """ + A class that handles the game's events, mainloop etc. + """ + + # CONSTANTS + # Frames per second, the general speed of the program + FPS = 50 + # Size of window + SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480 + BG_COLOR = "black" + + def __init__(self, caption: str) -> None: + # Initialize + pg.init() + self.screen = pg.display.set_mode((self.SCREEN_WIDTH, self.SCREEN_HEIGHT)) + pg.display.set_caption(caption) + self.clock = pg.time.Clock() + + # Text input + # Set to true or add 'showevent' in argv to see IME and KEYDOWN events + self.print_event = "showevent" in sys.argv + self.text_input = TextInput( + prompt="> ", + pos=(0, 20), + screen_dimensions=(self.SCREEN_WIDTH, self.SCREEN_HEIGHT), + print_event=self.print_event, + text_color="green", + ) + + def main_loop(self) -> None: + pg.key.start_text_input() + input_rect = pg.Rect(80, 80, 320, 40) + pg.key.set_text_input_rect(input_rect) + + while True: + events = pg.event.get() + for event in events: + if event.type == pg.QUIT: + pg.quit() + return + + self.text_input.update(events) + + # Screen updates + self.screen.fill(self.BG_COLOR) + self.text_input.draw(self.screen) + + pg.display.update() + self.clock.tick(self.FPS) + + +# Main loop process +def main(): + game = Game("Text Input Example") + game.main_loop() + + +if __name__ == "__main__": main() diff --git a/venv/Lib/site-packages/pygame/examples/vgrade.py b/venv/Lib/site-packages/pygame/examples/vgrade.py index ff0a6b3aa8164e3b0da3162004767cead82a3566..06202c24b5e6e4e1f0be2d26021d54fc6807bb42 100644 --- a/venv/Lib/site-packages/pygame/examples/vgrade.py +++ b/venv/Lib/site-packages/pygame/examples/vgrade.py @@ -1,8 +1,9 @@ #!/usr/bin/env python +""" pg.examples.vgrade -"""This example demonstrates creating an image with numpy +This example demonstrates creating an image with numpy python, and displaying that through SDL. You can look at the -method of importing numpy and pygame.surfarray. This method +method of importing numpy and pg.surfarray. This method will fail 'gracefully' if it is not available. I've tried mixing in a lot of comments where the code might not be self explanatory, nonetheless it may still seem a bit @@ -20,84 +21,82 @@ Just so you know how this breaks down. For each sampling of time, 30% goes to each creating the gradient and blitting the array. The final 40% goes to flipping/updating the display surface -If using an SDL version at least 1.1.8 the window will have -no border decorations. +The window will have no border decorations. -The code also demonstrates use of the timer events.""" +The code also demonstrates use of the timer events. +""" -import os, pygame -from pygame.locals import * +import os +import pygame as pg try: - from numpy import * - from numpy.random import * + import numpy as np + import numpy.random as np_random except ImportError: - raise SystemExit('This example requires numpy and the pygame surfarray module') - -pygame.surfarray.use_arraytype('numpy') + raise SystemExit("This example requires numpy and the pygame surfarray module") timer = 0 -def stopwatch(message = None): + + +def stopwatch(message=None): "simple routine to time python code" global timer if not message: - timer = pygame.time.get_ticks() + timer = pg.time.get_ticks() return - now = pygame.time.get_ticks() - runtime = (now - timer)/1000.0 + .001 - print ("%s %s %s" % - (message, runtime, ('seconds\t(%.2ffps)'%(1.0/runtime)))) + now = pg.time.get_ticks() + runtime = (now - timer) / 1000.0 + 0.001 + print(f"{message} {runtime} seconds\t{(1.0 / runtime):.2f}fps") timer = now - def VertGradientColumn(surf, topcolor, bottomcolor): "creates a new 3d vertical gradient array" - topcolor = array(topcolor, copy=0) - bottomcolor = array(bottomcolor, copy=0) + topcolor = np.array(topcolor, copy=False) + bottomcolor = np.array(bottomcolor, copy=False) diff = bottomcolor - topcolor width, height = surf.get_size() # create array from 0.0 to 1.0 triplets - column = arange(height, dtype='float')/height - column = repeat(column[:, newaxis], [3], 1) + column = np.arange(height, dtype="float") / height + column = np.repeat(column[:, np.newaxis], [3], 1) # create a single column of gradient - column = topcolor + (diff * column).astype('int') + column = topcolor + (diff * column).astype("int") # make the column a 3d image column by adding X - column = column.astype('uint8')[newaxis,:,:] - #3d array into 2d array - return pygame.surfarray.map_array(surf, column) - + column = column.astype("uint8")[np.newaxis, :, :] + # 3d array into 2d array + return pg.surfarray.map_array(surf, column) def DisplayGradient(surf): "choose random colors and show them" stopwatch() - colors = randint(0, 255, (2, 3)) + colors = np_random.randint(0, 255, (2, 3)) column = VertGradientColumn(surf, colors[0], colors[1]) - pygame.surfarray.blit_array(surf, column) - pygame.display.flip() - stopwatch('Gradient:') - + pg.surfarray.blit_array(surf, column) + pg.display.flip() + stopwatch("Gradient:") def main(): - pygame.init() - pygame.mixer.quit() # remove ALSA underflow messages for Debian squeeze + pg.init() + pg.mixer.quit() # remove ALSA underflow messages for Debian squeeze size = 600, 400 - os.environ['SDL_VIDEO_CENTERED'] = '1' - screen = pygame.display.set_mode(size, NOFRAME, 0) + os.environ["SDL_VIDEO_CENTERED"] = "1" + screen = pg.display.set_mode(size, pg.NOFRAME, 0) - pygame.event.set_blocked(MOUSEMOTION) #keep our queue cleaner - pygame.time.set_timer(USEREVENT, 500) + pg.event.set_blocked(pg.MOUSEMOTION) # keep our queue cleaner + pg.time.set_timer(pg.USEREVENT, 500) - while 1: - event = pygame.event.wait() - if event.type in (QUIT, KEYDOWN, MOUSEBUTTONDOWN): + while True: + event = pg.event.wait() + if event.type in (pg.QUIT, pg.KEYDOWN, pg.MOUSEBUTTONDOWN): break - elif event.type == USEREVENT: + elif event.type == pg.USEREVENT: DisplayGradient(screen) + pg.quit() -if __name__ == '__main__': main() +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/pygame/examples/video.py b/venv/Lib/site-packages/pygame/examples/video.py index 21bf4e1eba820ebf11818d5bea006afd102942af..7104f47349235fef9b377ad379b2b936f47d2083 100644 --- a/venv/Lib/site-packages/pygame/examples/video.py +++ b/venv/Lib/site-packages/pygame/examples/video.py @@ -1,100 +1,158 @@ -import pygame +#!/usr/bin/env python +""" pg.examples.video +Experimental! -if pygame.get_sdl_version()[0] < 2: - raise SystemExit('This example requires pygame 2 and SDL2.') - +* dialog message boxes with messagebox. +* multiple windows with Window +* driver selection +* Renderer, Texture, and Image classes +* Drawing lines, rects, and such onto Renderers. +""" import os -data_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], - 'data') +import pygame as pg +from pygame._sdl2 import Window, Texture, Image, Renderer, get_drivers, messagebox + +data_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data") -from pygame._sdl2 import ( - Window, - Texture, - Renderer, - get_drivers, - messagebox, -) def load_img(file): - return pygame.image.load(os.path.join(data_dir, file)) + return pg.image.load(os.path.join(data_dir, file)) + -pygame.display.init() -pygame.key.set_repeat(1000, 10) +pg.display.init() +pg.key.set_repeat(1000, 10) for driver in get_drivers(): print(driver) import random -answer = messagebox("I will open two windows! Continue?", "Hello!", info=True, - buttons=('Yes', 'No', 'Chance'), - return_button=0, escape_button=1) -if answer == 1 or (answer == 2 and random.random() < .5): - import sys - sys.exit(0) - -win = Window('asdf', resizable=True) + +try: + answer = messagebox( + "I will open two windows! Continue?", + "Hello!", + info=True, + buttons=("Yes", "No", "Chance"), + return_button=0, + escape_button=1, + ) + if answer == 1 or (answer == 2 and random.random() < 0.5): + import sys + + sys.exit(0) +except: + pass + +win = Window("asdf", resizable=True) renderer = Renderer(win) -tex = Texture.from_surface(renderer, load_img('alien1.gif')) +tex = Texture.from_surface(renderer, load_img("alien1.gif")) +img = Image(tex) running = True x, y = 250, 50 -clock = pygame.time.Clock() +clock = pg.time.Clock() -backgrounds = [(255,0,0,255), (0,255,0,255), (0,0,255,255)] +backgrounds = [(255, 0, 0, 255), (0, 255, 0, 255), (0, 0, 255, 255)] bg_index = 0 renderer.draw_color = backgrounds[bg_index] -win2 = Window('2nd window', size=(256, 256), always_on_top=True) +win2 = Window("2nd window", size=(256, 256), always_on_top=True) win2.opacity = 0.5 -win2.set_icon(load_img('bomb.gif')) +win2.set_icon(load_img("bomb.gif")) renderer2 = Renderer(win2) -tex2 = Texture.from_surface(renderer2, load_img('asprite.bmp')) +tex2 = Texture.from_surface(renderer2, load_img("asprite.bmp")) renderer2.clear() -renderer2.copy(tex2) +tex2.draw() renderer2.present() del tex2 full = 0 -srcrect = (0, 0, tex.width, tex.height) +surf = pg.Surface((64, 64)) +streamtex = Texture(renderer, (64, 64), streaming=True) +tex_update_interval = 1000 +next_tex_update = pg.time.get_ticks() + while running: - for event in pygame.event.get(): - if event.type == pygame.QUIT: + for event in pg.event.get(): + if event.type == pg.QUIT: running = False - elif getattr(event, 'window', None) == win2: - if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE or\ - event.type == pygame.WINDOWEVENT and event.event == pygame.WINDOWEVENT_CLOSE: + elif getattr(event, "window", None) == win2: + if ( + event.type == pg.KEYDOWN + and event.key == pg.K_ESCAPE + or event.type == pg.WINDOWCLOSE + ): win2.destroy() - elif event.type == pygame.KEYDOWN: - if event.key == pygame.K_ESCAPE: + elif event.type == pg.KEYDOWN: + if event.key == pg.K_ESCAPE: running = False - elif event.key == pygame.K_LEFT: + elif event.key == pg.K_LEFT: x -= 5 - elif event.key == pygame.K_RIGHT: + elif event.key == pg.K_RIGHT: x += 5 - elif event.key == pygame.K_DOWN: + elif event.key == pg.K_DOWN: y += 5 - elif event.key == pygame.K_UP: + elif event.key == pg.K_UP: y -= 5 - elif event.key == pygame.K_f: + elif event.key == pg.K_f: if full == 0: win.set_fullscreen(True) full = 1 else: win.set_windowed() full = 0 - elif event.key == pygame.K_SPACE: + elif event.key == pg.K_s: + readsurf = renderer.to_surface() + pg.image.save(readsurf, "test.png") + + elif event.key == pg.K_SPACE: bg_index = (bg_index + 1) % len(backgrounds) renderer.draw_color = backgrounds[bg_index] - dstrect = (x, y, tex.width, tex.height) renderer.clear() - renderer.copy(tex, srcrect, dstrect) + + # update texture + curtime = pg.time.get_ticks() + if curtime >= next_tex_update: + for x_ in range(streamtex.width // 4): + for y_ in range(streamtex.height // 4): + newcol = ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + 255, + ) + area = (4 * x_, 4 * y_, 4, 4) + surf.fill(newcol, area) + streamtex.update(surf) + next_tex_update = curtime + tex_update_interval + streamtex.draw(dstrect=pg.Rect(64, 128, 64, 64)) + + img.draw(dstrect=(x, y)) + + # TODO: should these be? + # - line instead of draw_line + # - point instead of draw_point + # - rect(rect, width=1)->draw 1 pixel, instead of draw_rect + # - rect(rect, width=0)->filled ? , instead of fill_rect + # + # TODO: should these work with pg.draw.line(renderer, ...) functions? + renderer.draw_color = (255, 255, 255, 255) + renderer.draw_line((0, 0), (64, 64)) + renderer.draw_line((64, 64), (128, 0)) + renderer.draw_point((72, 32)) + renderer.draw_rect(pg.Rect(0, 64, 64, 64)) + renderer.fill_rect(pg.Rect(0, 128, 64, 64)) + renderer.draw_color = backgrounds[bg_index] + renderer.present() clock.tick(60) - win.title = str('FPS: {}'.format(clock.get_fps())) + win.title = str(f"FPS: {clock.get_fps()}") + +pg.quit() diff --git a/venv/Lib/site-packages/pygame/fastevent.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/fastevent.cp37-win_amd64.pyd deleted file mode 100644 index 0bf6486a4b9e13e35daf6e9db0e290b567805cf5..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/fastevent.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/font.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/font.cp37-win_amd64.pyd deleted file mode 100644 index 0a21aab490463185542ee7950c497c41f5e87032..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/font.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/freetype.py b/venv/Lib/site-packages/pygame/freetype.py index 1a90753a52acf4d26213a703e99942c806b0a887..50a63cf113b11078012f22ac8ca0759bbe24d53b 100644 --- a/venv/Lib/site-packages/pygame/freetype.py +++ b/venv/Lib/site-packages/pygame/freetype.py @@ -1,41 +1,74 @@ """Enhanced Pygame module for loading and rendering computer fonts""" -import sys from pygame._freetype import ( - Font, - STYLE_NORMAL, STYLE_OBLIQUE, STYLE_STRONG, STYLE_UNDERLINE, STYLE_WIDE, - STYLE_DEFAULT, - init, quit, get_init, - was_init, get_cache_size, get_default_font, get_default_resolution, - get_error, get_version, set_default_resolution, - _PYGAME_C_API, __PYGAMEinit__, - ) + Font, + STYLE_NORMAL, + STYLE_OBLIQUE, + STYLE_STRONG, + STYLE_UNDERLINE, + STYLE_WIDE, + STYLE_DEFAULT, + init, + quit, + get_init, + was_init, + get_cache_size, + get_default_font, + get_default_resolution, + get_error, + get_version, + set_default_resolution, +) from pygame.sysfont import match_font, get_fonts, SysFont as _SysFont -from pygame import compat -def SysFont(name, size, bold=0, italic=0, constructor=None): +__all__ = [ + "Font", + "STYLE_NORMAL", + "STYLE_OBLIQUE", + "STYLE_STRONG", + "STYLE_UNDERLINE", + "STYLE_WIDE", + "STYLE_DEFAULT", + "init", + "quit", + "get_init", + "was_init", + "get_cache_size", + "get_default_font", + "get_default_resolution", + "get_error", + "get_version", + "set_default_resolution", + "match_font", + "get_fonts", +] + + +def SysFont(name, size, bold=False, italic=False, constructor=None): """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font - create a pygame Font from system font resources + Create a pygame Font from system font resources. - This will search the system fonts for the given font - name. You can also enable bold or italic styles, and - the appropriate system font will be selected if available. + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. - This will always return a valid Font object, and will - fallback on the builtin pygame font if the given font - is not found. + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. - Name can also be a comma separated list of names, in - which case set of names will be searched in order. Pygame - uses a small set of common font aliases, if the specific - font you ask for is not available, a reasonable alternative - may be used. + Name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated + font names, in which case the set of names will be searched + in order. Pygame uses a small set of common font aliases. If the + specific font you ask for is not available, a reasonable + alternative may be used. - if optional contructor is provided, it must be a function with - signature constructor(fontpath, size, bold, italic) which returns - a Font instance. If None, a pygame.freetype.Font object is created. + If optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.freetype.Font object is created. """ if constructor is None: + def constructor(fontpath, size, bold, italic): font = Font(fontpath, size) font.strong = bold diff --git a/venv/Lib/site-packages/pygame/ftfont.py b/venv/Lib/site-packages/pygame/ftfont.py index 78c935773af60deb5e357ac771da4a175d3e9cbd..e060291a5bc6c0dd37d2a66deccb87fbe872ab99 100644 --- a/venv/Lib/site-packages/pygame/ftfont.py +++ b/venv/Lib/site-packages/pygame/ftfont.py @@ -1,50 +1,57 @@ """pygame module for loading and rendering fonts (freetype alternative)""" -__all__ = ['Font', 'init', 'quit', 'get_default_font', 'get_init', 'SysFont'] +__all__ = [ + "Font", + "init", + "quit", + "get_default_font", + "get_init", + "SysFont", + "match_font", + "get_fonts", +] from pygame._freetype import init, Font as _Font, get_default_resolution from pygame._freetype import quit, get_default_font, get_init as _get_init -from pygame._freetype import __PYGAMEinit__ +from pygame._freetype import _internal_mod_init from pygame.sysfont import match_font, get_fonts, SysFont as _SysFont from pygame import encode_file_path -from pygame.compat import bytes_, unicode_, as_unicode, as_bytes -from pygame import Surface as _Surface, Color as _Color, SRCALPHA as _SRCALPHA + class Font(_Font): """Font(filename, size) -> Font - Font(object, size) -> Font - create a new Font object from a file (freetype alternative) + Font(object, size) -> Font + create a new Font object from a file (freetype alternative) - This Font type differs from font.Font in that it can render glyphs - for Unicode code points in the supplementary planes (> 0xFFFF). - """ + This Font type differs from font.Font in that it can render glyphs + for Unicode code points in the supplementary planes (> 0xFFFF). + """ __encode_file_path = staticmethod(encode_file_path) __get_default_resolution = staticmethod(get_default_resolution) __default_font = encode_file_path(get_default_font()) - __unull = as_unicode(r"\x00") - __bnull = as_bytes("\x00") + __unull = "\x00" + __bnull = b"\x00" - def __init__(self, file, size=-1): - if size <= 1: - size = 1 - if isinstance(file, unicode_): + def __init__(self, file=None, size=-1): + size = max(size, 1) + if isinstance(file, str): try: bfile = self.__encode_file_path(file, ValueError) except ValueError: - bfile = '' + bfile = "" else: bfile = file - if isinstance(bfile, bytes_) and bfile == self.__default_font: + if isinstance(bfile, bytes) and bfile == self.__default_font: file = None if file is None: resolution = int(self.__get_default_resolution() * 0.6875) if resolution == 0: - kwds['resolution'] = 1 + resolution = 1 else: resolution = 0 - super(Font, self).__init__(file, size=size, resolution=resolution) + super().__init__(file, size=size, resolution=resolution) self.strength = 1.0 / 12.0 self.kerning = False self.origin = True @@ -54,127 +61,136 @@ class Font(_Font): def render(self, text, antialias, color, background=None): """render(text, antialias, color, background=None) -> Surface - draw text on a new Surface""" + draw text on a new Surface""" if text is None: text = "" - if (isinstance(text, unicode_) and # conditional and - self.__unull in text): + if isinstance(text, str) and self.__unull in text: raise ValueError("A null character was found in the text") - if (isinstance(text, bytes_) and # conditional and - self.__bnull in text): + if isinstance(text, bytes) and self.__bnull in text: raise ValueError("A null character was found in the text") - save_antialiased = self.antialiased + save_antialiased = ( + self.antialiased # pylint: disable = access-member-before-definition + ) self.antialiased = bool(antialias) try: - s, r = super(Font, self).render(text, color, background) + s, _ = super().render(text, color, background) return s finally: self.antialiased = save_antialiased def set_bold(self, value): """set_bold(bool) -> None - enable fake rendering of bold text""" + enable fake rendering of bold text""" self.wide = bool(value) def get_bold(self): """get_bold() -> bool - check if text will be rendered bold""" - + check if text will be rendered bold""" + return self.wide + bold = property(get_bold, set_bold) + def set_italic(self, value): """set_italic(bool) -> None - enable fake rendering of italic text""" + enable fake rendering of italic text""" self.oblique = bool(value) def get_italic(self): """get_italic() -> bool - check if the text will be rendered italic""" + check if the text will be rendered italic""" return self.oblique + italic = property(get_italic, set_italic) + def set_underline(self, value): """set_underline(bool) -> None - control if text is rendered with an underline""" + control if text is rendered with an underline""" self.underline = bool(value) def get_underline(self): """set_bold(bool) -> None - enable fake rendering of bold text""" + enable fake rendering of bold text""" return self.underline def metrics(self, text): """metrics(text) -> list - Gets the metrics for each character in the pased string.""" + Gets the metrics for each character in the passed string.""" return self.get_metrics(text) def get_ascent(self): """get_ascent() -> int - get the ascent of the font""" + get the ascent of the font""" return self.get_sized_ascender() def get_descent(self): """get_descent() -> int - get the descent of the font""" + get the descent of the font""" return self.get_sized_descender() def get_height(self): """get_height() -> int - get the height of the font""" + get the height of the font""" return self.get_sized_ascender() - self.get_sized_descender() + 1 def get_linesize(self): """get_linesize() -> int - get the line space of the font text""" + get the line space of the font text""" - return self.get_sized_height(); + return self.get_sized_height() def size(self, text): """size(text) -> (width, height) - determine the amount of space needed to render text""" + determine the amount of space needed to render text""" return self.get_rect(text).size + FontType = Font + def get_init(): - """get_init() -> bool - true if the font module is initialized""" + """get_init() -> bool + true if the font module is initialized""" + + return _get_init() - return _get_init() def SysFont(name, size, bold=0, italic=0, constructor=None): """pygame.ftfont.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font - create a pygame Font from system font resources (freetype alternative) - - This will search the system fonts for the given font - name. You can also enable bold or italic styles, and - the appropriate system font will be selected if available. - - This will always return a valid Font object, and will - fallback on the builtin pygame font if the given font - is not found. - - Name can also be a comma separated list of names, in - which case set of names will be searched in order. Pygame - uses a small set of common font aliases, if the specific - font you ask for is not available, a reasonable alternative - may be used. - - if optional contructor is provided, it must be a function with - signature constructor(fontpath, size, bold, italic) which returns - a Font instance. If None, a pygame.ftfont.Font object is created. + Create a pygame Font from system font resources. + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated + font names, in which case the set of names will be searched + in order. Pygame uses a small set of common font aliases. If the + specific font you ask for is not available, a reasonable + alternative may be used. + + If optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.ftfont.Font object is created. """ if constructor is None: + def constructor(fontpath, size, bold, italic): font = Font(fontpath, size) font.set_bold(bold) @@ -183,5 +199,5 @@ def SysFont(name, size, bold=0, italic=0, constructor=None): return _SysFont(name, size, bold, italic, constructor) -del _Font, get_default_resolution, encode_file_path, as_unicode, as_bytes +del _Font, get_default_resolution, encode_file_path diff --git a/venv/Lib/site-packages/pygame/gfxdraw.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/gfxdraw.cp37-win_amd64.pyd deleted file mode 100644 index b6a095e71c4e51371fa8a712b393103a276c19ed..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/gfxdraw.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/image.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/image.cp37-win_amd64.pyd deleted file mode 100644 index aeb6d375f0d267d0df380332210359f6b65e92e4..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/image.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/imageext.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/imageext.cp37-win_amd64.pyd deleted file mode 100644 index 281805943cbb41d1e367581c3480a55f307145b4..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/imageext.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/joystick.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/joystick.cp37-win_amd64.pyd deleted file mode 100644 index b537c306bfeb5e24c5decc696105ec29afdb2068..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/joystick.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/key.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/key.cp37-win_amd64.pyd deleted file mode 100644 index 41f5647205daed833783f0d4faddea18e63869f5..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/key.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libfreetype-6.dll b/venv/Lib/site-packages/pygame/libfreetype-6.dll deleted file mode 100644 index a6e5f74f2a9b3dbb8f6675d1400fd487735807ad..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libfreetype-6.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libgcc_s_sjlj-1.dll b/venv/Lib/site-packages/pygame/libgcc_s_sjlj-1.dll deleted file mode 100644 index 5df3238f92a4e60595d8758740fc6fada884d7c4..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libgcc_s_sjlj-1.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libjpeg-8.dll b/venv/Lib/site-packages/pygame/libjpeg-8.dll deleted file mode 100644 index 26fda46a106653fc2d0c082f417109600f24dbf2..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libjpeg-8.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libmpg123-0.dll b/venv/Lib/site-packages/pygame/libmpg123-0.dll deleted file mode 100644 index 6eff5e6150918da30916ff3e60b031e2ad8a60ae..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libmpg123-0.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libogg-0.dll b/venv/Lib/site-packages/pygame/libogg-0.dll index 9466d9f0430a3920afaf8f88627bed150698e796..72733eb62139f0c96cc6a76e7eb3237f2c275b16 100644 Binary files a/venv/Lib/site-packages/pygame/libogg-0.dll and b/venv/Lib/site-packages/pygame/libogg-0.dll differ diff --git a/venv/Lib/site-packages/pygame/libpng16-16.dll b/venv/Lib/site-packages/pygame/libpng16-16.dll index 4dbd22b430360125da76fe9a9b2023b350090487..709f724459a3f036811cf7cfbac64f3c900ea739 100644 Binary files a/venv/Lib/site-packages/pygame/libpng16-16.dll and b/venv/Lib/site-packages/pygame/libpng16-16.dll differ diff --git a/venv/Lib/site-packages/pygame/libstdc++-6.dll b/venv/Lib/site-packages/pygame/libstdc++-6.dll deleted file mode 100644 index bdaa9cfdcda6b0328b3b94745377d90817410e2c..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libstdc++-6.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libtiff-5.dll b/venv/Lib/site-packages/pygame/libtiff-5.dll index 67ebfb019ac2c3f17b152e116a47a82983f2c7bc..fc8a7c0ca04ebc95ae2549d80f4516f771003731 100644 Binary files a/venv/Lib/site-packages/pygame/libtiff-5.dll and b/venv/Lib/site-packages/pygame/libtiff-5.dll differ diff --git a/venv/Lib/site-packages/pygame/libvorbis-0.dll b/venv/Lib/site-packages/pygame/libvorbis-0.dll deleted file mode 100644 index e73d89757cfccdc1c6dfbc36bd65bc4679dfc235..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libvorbis-0.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libvorbisfile-3.dll b/venv/Lib/site-packages/pygame/libvorbisfile-3.dll deleted file mode 100644 index 2eeaa3e200104dfa6d428491c02c3d6460618c42..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libvorbisfile-3.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/libwebp-5.dll b/venv/Lib/site-packages/pygame/libwebp-5.dll deleted file mode 100644 index d9bdbbf96e3e95f9d9f987f0314a11bfb6117635..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/libwebp-5.dll and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/locals.py b/venv/Lib/site-packages/pygame/locals.py index 9b1f2fba6bfeb7027e51b2c615c01010294cdcd9..38801d5fc37309d6cf7674904170e8a64b425b14 100644 --- a/venv/Lib/site-packages/pygame/locals.py +++ b/venv/Lib/site-packages/pygame/locals.py @@ -1,30 +1,30 @@ -## pygame - Python Game Library -## Copyright (C) 2000-2003 Pete Shinners -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Library General Public -## License as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Library General Public License for more details. -## -## You should have received a copy of the GNU Library General Public -## License along with this library; if not, write to the Free -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -## -## Pete Shinners -## pete@shinners.org +# pygame - Python Game Library +# Copyright (C) 2000-2003 Pete Shinners +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org - -"""Set of functions from PyGame that are handy to have in +"""Set of functions from pygame that are handy to have in the local namespace for your module""" -from pygame.constants import * +import pygame +from pygame.constants import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] from pygame.rect import Rect -import pygame.color as color -Color = color.Color +from pygame.color import Color +__all__ = pygame.constants.__all__ + ["Rect", "Color"] diff --git a/venv/Lib/site-packages/pygame/macosx.py b/venv/Lib/site-packages/pygame/macosx.py index dfa35fb08c5f6f22ff3b162a8a5718c19db6fb41..0a16d317be1a6272a84f28f72e47216ac8ce0a28 100644 --- a/venv/Lib/site-packages/pygame/macosx.py +++ b/venv/Lib/site-packages/pygame/macosx.py @@ -1,22 +1,13 @@ import platform import os import sys -from pygame.pkgdata import getResource -from pygame import sdlmain_osx -__all__ = ['Video_AutoInit'] +__all__ = ["Video_AutoInit"] + def Video_AutoInit(): """Called from the base.c just before display module is initialized.""" - if 'Darwin' in platform.platform(): - if not sdlmain_osx.RunningFromBundleWithNSApplication(): - try: - default_icon_data = getResource('pygame_icon.tiff').read() - except IOError: - default_icon_data = None - except NotImplementedError: - default_icon_data = None - sdlmain_osx.InstallNSApplication(default_icon_data) - if (os.getcwd() == '/') and len(sys.argv) > 1: + if "Darwin" in platform.platform(): + if (os.getcwd() == "/") and len(sys.argv) > 1: os.chdir(os.path.dirname(sys.argv[0])) return True diff --git a/venv/Lib/site-packages/pygame/mask.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/mask.cp37-win_amd64.pyd deleted file mode 100644 index 579762d4d8592ba1f851097ba6e6394316833afb..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/mask.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/math.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/math.cp37-win_amd64.pyd deleted file mode 100644 index 707f18a3f6d0913fe205d5741cb36724a90d17df..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/math.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/midi.py b/venv/Lib/site-packages/pygame/midi.py index 1d0ab2e93846e269c252502a4c33bea7828edbab..24830f65f3ae7f18fc29e0ee413e3b80793eaa4d 100644 --- a/venv/Lib/site-packages/pygame/midi.py +++ b/venv/Lib/site-packages/pygame/midi.py @@ -15,31 +15,24 @@ point in the future. The pyportmidi bindings are included with pygame. New in pygame 1.9.0. """ - -#TODO: -# - finish writing tests. -# - likely as interactive tests... so you'd need to plug in a midi device. -# - create a background thread version for input threads. +# TODO: finish writing tests. +# - likely as interactive tests... so you'd need to plug in +# a midi device. +# TODO: create a background thread version for input threads. # - that can automatically inject input into the event queue # once the input object is running. Like joysticks. - - -import atexit import math +import atexit import pygame import pygame.locals +import pygame.pypm as _pypm - -# -MIDIIN = pygame.locals.USEREVENT + 10 -MIDIOUT = pygame.locals.USEREVENT + 11 - -_init = False -_pypm = None - +# For backward compatibility. +MIDIIN = pygame.locals.MIDIIN +MIDIOUT = pygame.locals.MIDIOUT __all__ = [ "Input", @@ -64,6 +57,20 @@ __all__ = [ __theclasses__ = ["Input", "Output"] +def _module_init(state=None): + # this is a sneaky dodge to store module level state in a non-public + # function. Helps us dodge using globals. + if state is not None: + _module_init.value = state + return state + + try: + _module_init.value + except AttributeError: + return False + return _module_init.value + + def init(): """initialize the midi module pygame.midi.init(): return None @@ -72,17 +79,13 @@ def init(): It is safe to call this more than once. """ - global _init, _pypm - if not _init: - import pygame.pypm - _pypm = pygame.pypm - + if not _module_init(): _pypm.Initialize() - _init = True + _module_init(True) atexit.register(quit) -def quit(): +def quit(): # pylint: disable=redefined-builtin """uninitialize the midi module pygame.midi.quit(): return None @@ -91,13 +94,10 @@ def quit(): It is safe to call this function more than once. """ - global _init, _pypm - if _init: + if _module_init(): # TODO: find all Input and Output classes and close them first? _pypm.Terminate() - _init = False - del _pypm - #del pygame._pypm + _module_init(False) def get_init(): @@ -108,13 +108,14 @@ def get_init(): New in pygame 1.9.5. """ - return _init + return _module_init() def _check_init(): - if not _init: + if not _module_init(): raise RuntimeError("pygame.midi not initialised.") + def get_count(): """gets the number of devices. pygame.midi.get_count(): return num_devices @@ -126,15 +127,13 @@ def get_count(): return _pypm.CountDevices() - - def get_default_input_id(): """gets default input device number pygame.midi.get_default_input_id(): return default_id Return the default device ID or -1 if there are no devices. - The result can be passed to the Input()/Ouput() class. + The result can be passed to the Input()/Output() class. On the PC, the user can specify a default device by setting an environment variable. For example, to use device #1. @@ -174,18 +173,17 @@ def get_default_input_id(): Note: in the current release, the default is simply the first device (the input or output device with the lowest PmDeviceID). """ + _check_init() return _pypm.GetDefaultInputDeviceID() - - def get_default_output_id(): """gets default output device number pygame.midi.get_default_output_id(): return default_id Return the default device ID or -1 if there are no devices. - The result can be passed to the Input()/Ouput() class. + The result can be passed to the Input()/Output() class. On the PC, the user can specify a default device by setting an environment variable. For example, to use device #1. @@ -230,11 +228,13 @@ def get_default_output_id(): def get_device_info(an_id): - """ returns information about a midi device - pygame.midi.get_device_info(an_id): return (interf, name, input, output, opened) + """returns information about a midi device + pygame.midi.get_device_info(an_id): return (interf, name, + input, output, + opened) - interf - a text string describing the device interface, eg 'ALSA'. - name - a text string for the name of the device, eg 'Midi Through Port-0' + interf - a byte string describing the device interface, eg b'ALSA'. + name - a byte string for the name of the device, eg b'Midi Through Port-0' input - 0, or 1 if the device is an input device. output - 0, or 1 if the device is an output device. opened - 0, or 1 if the device is opened. @@ -245,7 +245,7 @@ def get_device_info(an_id): return _pypm.GetDeviceInfo(an_id) -class Input(object): +class Input: """Input is used to get midi input from midi devices. Input(device_id) Input(device_id, buffer_size) @@ -262,56 +262,55 @@ class Input(object): _check_init() if device_id == -1: - raise MidiException("Device id is -1, not a valid output id. -1 usually means there were no default Output devices.") + raise MidiException( + "Device id is -1, not a valid output id. " + "-1 usually means there were no default " + "Output devices." + ) try: - r = get_device_info(device_id) + result = get_device_info(device_id) except TypeError: raise TypeError("an integer is required") except OverflowError: raise OverflowError("long int too large to convert to int") # and now some nasty looking error checking, to provide nice error - # messages to the kind, lovely, midi using people of whereever. - if r: - interf, name, input, output, opened = r - if input: + # messages to the kind, lovely, midi using people of wherever. + if result: + _, _, is_input, is_output, _ = result + if is_input: try: self._input = _pypm.Input(device_id, buffer_size) except TypeError: raise TypeError("an integer is required") self.device_id = device_id - elif output: - raise MidiException("Device id given is not a valid input id, it is an output id.") + elif is_output: + raise MidiException( + "Device id given is not a valid input id, it is an output id." + ) else: raise MidiException("Device id given is not a valid input id.") else: raise MidiException("Device id invalid, out of range.") - - - def _check_open(self): if self._input is None: raise MidiException("midi not open.") - - def close(self): - """ closes a midi stream, flushing any pending buffers. + """closes a midi stream, flushing any pending buffers. Input.close(): return None PortMidi attempts to close open streams when the application exits -- this is particularly difficult under Windows. """ _check_init() - if not (self._input is None): + if self._input is not None: self._input.Close() self._input = None - - def read(self, num_events): """reads num_events midi events from the buffer. Input.read(num_events): return midi_event_list @@ -324,7 +323,6 @@ class Input(object): self._check_open() return self._input.Read(num_events) - def poll(self): """returns true if there's data, or false if not. Input.poll(): return Bool @@ -334,19 +332,18 @@ class Input(object): _check_init() self._check_open() - r = self._input.Poll() - if r == _pypm.TRUE: + result = self._input.Poll() + if result == _pypm.TRUE: return True - elif r == _pypm.FALSE: - return False - else: - err_text = GetErrorText(r) - raise MidiException( (r, err_text) ) + if result == _pypm.FALSE: + return False + err_text = _pypm.GetErrorText(result) + raise MidiException((result, err_text)) -class Output(object): +class Output: """Output is used to send midi to an output device Output(device_id) Output(device_id, latency = 0) @@ -374,7 +371,7 @@ class Output(object): """ - def __init__(self, device_id, latency = 0, buffer_size = 4096): + def __init__(self, device_id, latency=0, buffer_size=256): """Output(device_id) Output(device_id, latency = 0) Output(device_id, buffer_size = 4096) @@ -404,28 +401,34 @@ class Output(object): self._aborted = 0 if device_id == -1: - raise MidiException("Device id is -1, not a valid output id. -1 usually means there were no default Output devices.") + raise MidiException( + "Device id is -1, not a valid output id." + " -1 usually means there were no default " + "Output devices." + ) try: - r = get_device_info(device_id) + result = get_device_info(device_id) except TypeError: raise TypeError("an integer is required") except OverflowError: raise OverflowError("long int too large to convert to int") # and now some nasty looking error checking, to provide nice error - # messages to the kind, lovely, midi using people of whereever. - if r: - interf, name, input, output, opened = r - if output: + # messages to the kind, lovely, midi using people of wherever. + if result: + _, _, is_input, is_output, _ = result + if is_output: try: - self._output = _pypm.Output(device_id, latency) + self._output = _pypm.Output(device_id, latency, buffer_size) except TypeError: raise TypeError("an integer is required") self.device_id = device_id - elif input: - raise MidiException("Device id given is not a valid output id, it is an input id.") + elif is_input: + raise MidiException( + "Device id given is not a valid output id, it is an input id." + ) else: raise MidiException("Device id given is not a valid output id.") else: @@ -438,16 +441,15 @@ class Output(object): if self._aborted: raise MidiException("midi aborted.") - def close(self): - """ closes a midi stream, flushing any pending buffers. + """closes a midi stream, flushing any pending buffers. Output.close(): return None PortMidi attempts to close open streams when the application exits -- this is particularly difficult under Windows. """ _check_init() - if not (self._output is None): + if self._output is not None: self._output.Close() self._output = None @@ -467,10 +469,6 @@ class Output(object): self._output.Abort() self._aborted = 1 - - - - def write(self, data): """writes a list of midi data to the Output Output.write(data) @@ -522,7 +520,7 @@ class Output(object): Output.write_sys_ex(when, msg) msg - can be a *list* or a *string* - when - a timestamp in miliseconds + when - a timestamp in milliseconds example: (assuming o is an onput MIDI stream) o.write_sys_ex(0,'\\xF0\\x7D\\x10\\x11\\x12\\x13\\xF7') @@ -545,7 +543,7 @@ class Output(object): Turn a note on in the output stream. The note must already be off for this to work correctly. """ - if not (0 <= channel <= 15): + if not 0 <= channel <= 15: raise ValueError("Channel not between 0 and 15.") self.write_short(0x90 + channel, note, velocity) @@ -561,25 +559,24 @@ class Output(object): Turn a note off in the output stream. The note must already be on for this to work correctly. """ - if not (0 <= channel <= 15): + if not 0 <= channel <= 15: raise ValueError("Channel not between 0 and 15.") self.write_short(0x80 + channel, note, velocity) - def set_instrument(self, instrument_id, channel=0): """select an instrument for a channel, with a value between 0 and 127 Output.set_instrument(instrument_id, channel=0) Also called "patch change" or "program change". """ - if not (0 <= instrument_id <= 127): - raise ValueError("Undefined instrument id: %d" % instrument_id) + if not 0 <= instrument_id <= 127: + raise ValueError(f"Undefined instrument id: {instrument_id}") - if not (0 <= channel <= 15): + if not 0 <= channel <= 15: raise ValueError("Channel not between 0 and 15.") - self.write_short(0xc0 + channel, instrument_id) + self.write_short(0xC0 + channel, instrument_id) def pitch_bend(self, value=0, channel=0): """modify the pitch of a channel. @@ -593,36 +590,33 @@ class Output(object): If no value is given, the pitch bend is returned to "no change". """ - if not (0 <= channel <= 15): + if not 0 <= channel <= 15: raise ValueError("Channel not between 0 and 15.") - if not (-8192 <= value <= 8191): - raise ValueError("Pitch bend value must be between " - "-8192 and +8191, not %d." % value) + if not -8192 <= value <= 8191: + raise ValueError( + f"Pitch bend value must be between -8192 and +8191, not {value}." + ) # "The 14 bit value of the pitch bend is defined so that a value of # 0x2000 is the center corresponding to the normal pitch of the note # (no pitch change)." so value=0 should send 0x2000 value = value + 0x2000 - LSB = value & 0x7f # keep least 7 bits - MSB = value >> 7 - self.write_short(0xe0 + channel, LSB, MSB) - + lsb = value & 0x7F # keep least 7 bits + msb = value >> 7 + self.write_short(0xE0 + channel, lsb, msb) -""" -MIDI commands - - 0x80 Note Off (note_off) - 0x90 Note On (note_on) - 0xA0 Aftertouch - 0xB0 Continuous controller - 0xC0 Patch change (set_instrument?) - 0xD0 Channel Pressure - 0xE0 Pitch bend - 0xF0 (non-musical commands) -""" - +# MIDI commands +# +# 0x80 Note Off (note_off) +# 0x90 Note On (note_on) +# 0xA0 Aftertouch +# 0xB0 Continuous controller +# 0xC0 Patch change (set_instrument?) +# 0xD0 Channel Pressure +# 0xE0 Pitch bend +# 0xF0 (non-musical commands) def time(): @@ -631,10 +625,10 @@ def time(): The time is reset to 0, when the module is inited. """ + _check_init() return _pypm.Time() - def midis2events(midis, device_id): """converts midi events to pygame events pygame.midi.midis2events(midis, device_id): return [Event, ...] @@ -643,38 +637,37 @@ def midis2events(midis, device_id): """ evs = [] for midi in midis: - - ((status,data1,data2,data3),timestamp) = midi - - e = pygame.event.Event(MIDIIN, - status=status, - data1=data1, - data2=data2, - data3=data3, - timestamp=timestamp, - vice_id = device_id) - evs.append( e ) - + ((status, data1, data2, data3), timestamp) = midi + + event = pygame.event.Event( + MIDIIN, + status=status, + data1=data1, + data2=data2, + data3=data3, + timestamp=timestamp, + vice_id=device_id, + ) + evs.append(event) return evs - - - class MidiException(Exception): """exception that pygame.midi functions and classes can raise MidiException(errno) """ + def __init__(self, value): + super().__init__(value) self.parameter = value + def __str__(self): return repr(self.parameter) - -def frequency_to_midi(freqency): - """ converts a frequency into a MIDI note. +def frequency_to_midi(frequency): + """converts a frequency into a MIDI note. Rounds to the closest midi note. @@ -687,16 +680,11 @@ def frequency_to_midi(freqency): >>> frequency_to_midi(4186.0) 108 """ - return int( - round( - 69 + ( - 12 * math.log(freqency / 440.0) - ) / math.log(2) - ) - ) + return int(round(69 + (12 * math.log(frequency / 440.0)) / math.log(2))) + def midi_to_frequency(midi_note): - """ Converts a midi note to a frequency. + """Converts a midi note to a frequency. ::Examples:: @@ -707,10 +695,11 @@ def midi_to_frequency(midi_note): >>> midi_to_frequency(108) 4186.0 """ - return round(440.0 * 2 ** ((midi_note - 69) * (1./12.)), 1) + return round(440.0 * 2 ** ((midi_note - 69) * (1.0 / 12.0)), 1) + def midi_to_ansi_note(midi_note): - """ returns the Ansi Note name for a midi number. + """returns the Ansi Note name for a midi number. ::Examples:: @@ -721,8 +710,8 @@ def midi_to_ansi_note(midi_note): >>> midi_to_ansi_note(108) 'C8' """ - notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'] + notes = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"] num_notes = 12 - note_name = notes[int(((midi_note - 21) % num_notes))] - note_number = int(round(((midi_note - 21) / 11.0))) - return '%s%s' % (note_name, note_number) + note_name = notes[int((midi_note - 21) % num_notes)] + note_number = (midi_note - 12) // num_notes + return f"{note_name}{note_number}" diff --git a/venv/Lib/site-packages/pygame/mixer.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/mixer.cp37-win_amd64.pyd deleted file mode 100644 index 82eb04c74e1b54da6dd560bc948cdef6a51ec924..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/mixer.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/mixer_music.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/mixer_music.cp37-win_amd64.pyd deleted file mode 100644 index 5f07da65726437893e20ba985f01f0abba6cdb10..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/mixer_music.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/mouse.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/mouse.cp37-win_amd64.pyd deleted file mode 100644 index 6abb06676efa391cba55441736204ba6dc252e5a..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/mouse.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/newbuffer.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/newbuffer.cp37-win_amd64.pyd deleted file mode 100644 index 0dd972bf495827a03e04657266464aff009a772b..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/newbuffer.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/overlay.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/overlay.cp37-win_amd64.pyd deleted file mode 100644 index 76059f4e1d6b9631e99963d54bb080f8d8533a7b..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/overlay.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/pixelarray.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/pixelarray.cp37-win_amd64.pyd deleted file mode 100644 index 3c25c2336c2488389b99c176f8a508b50f1a3352..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/pixelarray.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/pixelcopy.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/pixelcopy.cp37-win_amd64.pyd deleted file mode 100644 index 23ec0dd56cc9c0da73def205c57c8288caf65366..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/pixelcopy.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/pkgdata.py b/venv/Lib/site-packages/pygame/pkgdata.py index 25ad64ac0da4c2753bc0f589490e15fe924551c5..767cbdf76cdb9a5b9c0ccea5e63fee6768e0471a 100644 --- a/venv/Lib/site-packages/pygame/pkgdata.py +++ b/venv/Lib/site-packages/pygame/pkgdata.py @@ -1,10 +1,10 @@ """ -pkgdata is a simple, extensible way for a package to acquire data file +pkgdata is a simple, extensible way for a package to acquire data file resources. The getResource function is equivalent to the standard idioms, such as the following minimal implementation: - + import sys, os def getResource(identifier, pkgname=__name__): @@ -17,20 +17,31 @@ getResource to its get_data implementation and return it as a file-like object (such as StringIO). """ -__all__ = ['getResource'] +__all__ = ["getResource"] import sys import os -from pygame.compat import get_BytesIO -BytesIO = get_BytesIO() try: from pkg_resources import resource_stream, resource_exists except ImportError: - def resource_exists(package_or_requirement, resource_name): + + def resource_exists(_package_or_requirement, _resource_name): + """ + A stub for when we fail to import this function. + + :return: Always returns False + """ return False - def resource_stream(package_of_requirement, resource_name): + + def resource_stream(_package_of_requirement, _resource_name): + """ + A stub for when we fail to import this function. + + Always raises a NotImplementedError when called. + """ raise NotImplementedError + def getResource(identifier, pkgname=__name__): """ Acquire a readable object for a given package name and identifier. @@ -47,21 +58,20 @@ def getResource(identifier, pkgname=__name__): rather than use it as a file-like object. For example, you may be handing data off to a C API. """ - if resource_exists(pkgname, identifier): - return resource_stream(pkgname, identifier) + + # When pyinstaller (or similar tools) are used, resource_exists may raise + # NotImplemented error + try: + if resource_exists(pkgname, identifier): + return resource_stream(pkgname, identifier) + except NotImplementedError: + pass mod = sys.modules[pkgname] - fn = getattr(mod, '__file__', None) - if fn is None: - raise IOError("%s has no __file__!" % repr(mod)) - path = os.path.join(os.path.dirname(fn), identifier) - if sys.version_info < (3, 3): - loader = getattr(mod, '__loader__', None) - if loader is not None: - try: - data = loader.get_data(path) - except IOError: - pass - else: - return BytesIO(data) - return open(os.path.normpath(path), 'rb') + path_to_file = getattr(mod, "__file__", None) + if path_to_file is None: + raise OSError(f"{repr(mod)} has no __file__!") + path = os.path.join(os.path.dirname(path_to_file), identifier) + + # pylint: disable=consider-using-with + return open(os.path.normpath(path), "rb") diff --git a/venv/Lib/site-packages/pygame/pygame_icon.icns b/venv/Lib/site-packages/pygame/pygame_icon.icns index 2610a8d7dcdd48e07954c4eb6c165fa15c1af2d9..44a67bbdf93d265b58a8d385db24acde4e6cff88 100644 Binary files a/venv/Lib/site-packages/pygame/pygame_icon.icns and b/venv/Lib/site-packages/pygame/pygame_icon.icns differ diff --git a/venv/Lib/site-packages/pygame/pygame_icon.svg b/venv/Lib/site-packages/pygame/pygame_icon.svg deleted file mode 100644 index bbee79dba72358b625302599fdaf1bd0b5e19978..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/pygame_icon.svg +++ /dev/null @@ -1,259 +0,0 @@ - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/venv/Lib/site-packages/pygame/pygame_icon.tiff b/venv/Lib/site-packages/pygame/pygame_icon.tiff deleted file mode 100644 index e7791433dc8ad319af932b29cc01abffd37d60ba..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/pygame_icon.tiff and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/pypm.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/pypm.cp37-win_amd64.pyd deleted file mode 100644 index e4254c2fa5507c8a718137230f499a90d9afcb41..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/pypm.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/rect.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/rect.cp37-win_amd64.pyd deleted file mode 100644 index 344277f3fa1b067fcdbf0be36ab5711c61e7711f..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/rect.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/rwobject.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/rwobject.cp37-win_amd64.pyd deleted file mode 100644 index fdaf7cc52b5f9006e13dfdb5c28e8651bc3bb455..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/rwobject.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/scrap.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/scrap.cp37-win_amd64.pyd deleted file mode 100644 index ec1e19b39548d551bce67f6ed628fb724e7d7eef..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/scrap.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/sndarray.py b/venv/Lib/site-packages/pygame/sndarray.py index d45517a00b7c4abcc5eb539744573b04e034353c..99ac4c7fe2704aab6c7088b83987471cd26f9b71 100644 --- a/venv/Lib/site-packages/pygame/sndarray.py +++ b/venv/Lib/site-packages/pygame/sndarray.py @@ -20,9 +20,9 @@ """pygame module for accessing sound sample data -Functions to convert between numpy arrays and Sound -objects. This module will only be available when pygame can use the -external numpy package. +Functions to convert between NumPy arrays and Sound objects. This module +will only be functional when pygame can use the external NumPy package. +If NumPy can't be imported, surfarray becomes a MissingModule object. Sound data is made of thousands of samples per second, and each sample is the amplitude of the wave at a particular moment in time. For @@ -33,20 +33,27 @@ Each sample is an 8-bit or 16-bit integer, depending on the data format. A stereo sound file has two values per sample, while a mono sound file only has one. -Supported array systems are - - numpy - -The array type to use can be changed at runtime using the use_arraytype() -method, which requires one of the above types as string. - Sounds with 16-bit data will be treated as unsigned integers, if the sound sample type requests this. """ -import pygame._numpysndarray as numpysnd +from pygame import mixer +import numpy + +import warnings -def array (sound): + +__all__ = [ + "array", + "samples", + "make_sound", + "use_arraytype", + "get_arraytype", + "get_arraytypes", +] + + +def array(sound): """pygame.sndarray.array(Sound): return array Copy Sound samples into an array. @@ -55,9 +62,11 @@ def array (sound): array will always be in the format returned from pygame.mixer.get_init(). """ - return numpysnd.array (sound) -def samples (sound): + return numpy.array(sound, copy=True) + + +def samples(sound): """pygame.sndarray.samples(Sound): return array Reference Sound samples into an array. @@ -66,38 +75,65 @@ def samples (sound): object. Modifying the array will change the Sound. The array will always be in the format returned from pygame.mixer.get_init(). """ - return numpysnd.samples (sound) -def make_sound (array): + return numpy.array(sound, copy=False) + + +def make_sound(array): """pygame.sndarray.make_sound(array): return Sound Convert an array into a Sound object. - + Create a new playable Sound object from an array. The mixer module must be initialized and the array format must be similar to the mixer audio format. """ - return numpysnd.make_sound (array) -def use_arraytype (arraytype): - """pygame.sndarray.use_arraytype (arraytype): return None + return mixer.Sound(array=array) + + +def use_arraytype(arraytype): + """pygame.sndarray.use_arraytype(arraytype): return None DEPRECATED - only numpy arrays are now supported. """ - arraytype = arraytype.lower () - if arraytype != 'numpy': + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + arraytype = arraytype.lower() + if arraytype != "numpy": raise ValueError("invalid array type") -def get_arraytype (): - """pygame.sndarray.get_arraytype (): return str + +def get_arraytype(): + """pygame.sndarray.get_arraytype(): return str DEPRECATED - only numpy arrays are now supported. """ - return 'numpy' + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + return "numpy" + -def get_arraytypes (): - """pygame.sndarray.get_arraytypes (): return tuple +def get_arraytypes(): + """pygame.sndarray.get_arraytypes(): return tuple DEPRECATED - only numpy arrays are now supported. """ - return ('numpy',) + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + return ("numpy",) diff --git a/venv/Lib/site-packages/pygame/sprite.py b/venv/Lib/site-packages/pygame/sprite.py index 6eea6ba4f72a6ab2c0614f30e55b2394951fdc0d..b268759bce24d137af29a5ccf7cc1c40427f581e 100644 --- a/venv/Lib/site-packages/pygame/sprite.py +++ b/venv/Lib/site-packages/pygame/sprite.py @@ -1,22 +1,22 @@ -## pygame - Python Game Library -## Copyright (C) 2000-2003, 2007 Pete Shinners -## (C) 2004 Joe Wreschnig -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Library General Public -## License as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Library General Public License for more details. -## -## You should have received a copy of the GNU Library General Public -## License along with this library; if not, write to the Free -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -## -## Pete Shinners -## pete@shinners.org +# pygame - Python Game Library +# Copyright (C) 2000-2003, 2007 Pete Shinners +# (C) 2004 Joe Wreschnig +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Pete Shinners +# pete@shinners.org """pygame module with basic game object classes @@ -55,7 +55,7 @@ in the game. Sprites and Groups manage their relationships with the add() and remove() methods. These methods can accept a single or multiple group arguments for membership. The default initializers for these classes also take a -single group or list of groups as argments for initial membership. It is safe +single group or list of groups as arguments for initial membership. It is safe to repeatedly add and remove the same Sprite from a Group. While it is possible to design sprite and group classes that don't derive @@ -66,44 +66,36 @@ Sprites are not thread safe, so lock them yourself if using threads. """ -##todo -## a group that holds only the 'n' most recent elements. -## sort of like the GroupSingle class, but holding more -## than one sprite -## -## drawing groups that can 'automatically' store the area -## underneath so they can "clear" without needing a background -## function. obviously a little slower than normal, but nice -## to use in many situations. (also remember it must "clear" -## in the reverse order that it draws :]) -## -## the drawing groups should also be able to take a background -## function, instead of just a background surface. the function -## would take a surface and a rectangle on that surface to erase. -## -## perhaps more types of collision functions? the current two -## should handle just about every need, but perhaps more optimized -## specific ones that aren't quite so general but fit into common -## specialized cases. +# TODO: a group that holds only the 'n' most recent elements. +# sort of like the GroupSingle class, but holding more +# than one sprite +# +# drawing groups that can 'automatically' store the area +# underneath so they can "clear" without needing a background +# function. obviously a little slower than normal, but nice +# to use in many situations. (also remember it must "clear" +# in the reverse order that it draws :]) +# +# the drawing groups should also be able to take a background +# function, instead of just a background surface. the function +# would take a surface and a rectangle on that surface to erase. +# +# perhaps more types of collision functions? the current two +# should handle just about every need, but perhaps more optimized +# specific ones that aren't quite so general but fit into common +# specialized cases. + +from weakref import WeakSet +from warnings import warn import pygame -from pygame import Rect -from pygame.time import get_ticks -from operator import truth - -# Python 3 does not have the callable function, but an equivalent can be made -# with the hasattr function. -if 'callable' not in dir(__builtins__): - callable = lambda obj: hasattr(obj, '__call__') -# Don't depend on pygame.mask if it's not there... -try: - from pygame.mask import from_surface -except: - pass +from pygame.rect import Rect +from pygame.time import get_ticks +from pygame.mask import from_surface -class Sprite(object): +class Sprite: """simple base class for visible game objects pygame.sprite.Sprite(*groups): return Sprite @@ -119,7 +111,7 @@ class Sprite(object): """ def __init__(self, *groups): - self.__g = {} # The groups the sprite is in + self.__g = set() # The groups the sprite is in if groups: self.add(*groups) @@ -134,7 +126,7 @@ class Sprite(object): """ has = self.__g.__contains__ for group in groups: - if hasattr(group, '_spritegroup'): + if hasattr(group, "_spritegroup"): if not has(group): group.add_internal(self) self.add_internal(group) @@ -152,7 +144,7 @@ class Sprite(object): """ has = self.__g.__contains__ for group in groups: - if hasattr(group, '_spritegroup'): + if hasattr(group, "_spritegroup"): if has(group): group.remove_internal(self) self.remove_internal(group) @@ -160,15 +152,25 @@ class Sprite(object): self.remove(*group) def add_internal(self, group): - self.__g[group] = 0 + """ + For adding this sprite to a group internally. + + :param group: The group we are adding to. + """ + self.__g.add(group) def remove_internal(self, group): - del self.__g[group] + """ + For removing this sprite from a group internally. - def update(self, *args): + :param group: The group we are removing from. + """ + self.__g.remove(group) + + def update(self, *args, **kwargs): """method to control sprite behavior - Sprite.update(*args): + Sprite.update(*args, **kwargs): The default implementation of this method does nothing; it's just a convenient "hook" that you can override. This method is called by @@ -178,7 +180,6 @@ class Sprite(object): method by the same name in the Group class. """ - pass def kill(self): """remove the Sprite from all Groups @@ -191,8 +192,8 @@ class Sprite(object): adding it to Groups. """ - for c in self.__g: - c.remove_internal(self) + for group in self.__g: + group.remove_internal(self) self.__g.clear() def groups(self): @@ -212,10 +213,49 @@ class Sprite(object): Returns True when the Sprite belongs to one or more Groups. """ - return truth(self.__g) + return bool(self.__g) def __repr__(self): - return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g)) + return f"<{self.__class__.__name__} Sprite(in {len(self.__g)} groups)>" + + @property + def layer(self): + """ + Dynamic, read only property for protected _layer attribute. + This will get the _layer variable if it exists. + + If you try to get it before it is set it will raise an attribute error. + + Layer property can only be set before the sprite is added to a group, + after that it is read only and a sprite's layer in a group should be + set via the group's change_layer() method. + + :return: layer as an int, or raise AttributeError. + """ + return self._layer + + @layer.setter + def layer(self, value): + if not self.alive(): + self._layer = value + else: + raise AttributeError( + "Can't set layer directly after " + "adding to group. Use " + "group.change_layer(sprite, new_layer) " + "instead." + ) + + +class WeakSprite(Sprite): + """A subclass of Sprite that references its Groups weakly. This + means that any group this belongs to that is not referenced anywhere + else is garbage collected automatically. + """ + + def __init__(self, *groups): + super().__init__(*groups) + self.__dict__["_Sprite__g"] = WeakSet(self._Sprite__g) class DirtySprite(Sprite): @@ -251,13 +291,14 @@ class DirtySprite(Sprite): """ def __init__(self, *groups): - self.dirty = 1 - self.blendmode = 0 # pygame 1.8, referred to as special_flags in - # the documentation of Surface.blit + + # referred to as special_flags in the documentation of Surface.blit + self.blendmode = 0 self._visible = 1 - self._layer = getattr(self, '_layer', 0) # Default 0 unless - # initialized differently. + + # Default 0 unless initialized differently. + self._layer = getattr(self, "_layer", 0) self.source_rect = None Sprite.__init__(self, *groups) @@ -271,18 +312,54 @@ class DirtySprite(Sprite): """return the visible value of that sprite""" return self._visible - visible = property(lambda self: self._get_visible(), - lambda self, value: self._set_visible(value), - doc="you can make this sprite disappear without " - "removing it from the group,\n" - "assign 0 for invisible and 1 for visible") + @property + def visible(self): + """ + You can make this sprite disappear without removing it from the group + assign 0 for invisible and 1 for visible + """ + return self._get_visible() + + @visible.setter + def visible(self, value): + self._set_visible(value) + + @property + def layer(self): + """ + Layer property can only be set before the sprite is added to a group, + after that it is read only and a sprite's layer in a group should be + set via the group's change_layer() method. + + Overwrites dynamic property from sprite class for speed. + """ + return self._layer + + @layer.setter + def layer(self, value): + if not self.alive(): + self._layer = value + else: + raise AttributeError( + "Can't set layer directly after " + "adding to group. Use " + "group.change_layer(sprite, new_layer) " + "instead." + ) def __repr__(self): - return "<%s DirtySprite(in %d groups)>" % \ - (self.__class__.__name__, len(self.groups())) + return ( + f"<{self.__class__.__name__} DirtySprite(in {len(self.groups())} groups)>" + ) -class AbstractGroup(object): +class WeakDirtySprite(WeakSprite, DirtySprite): + """A subclass of WeakSprite and DirtySprite that combines the benefits + of both classes. + """ + + +class AbstractGroup: """base class for containers of sprites AbstractGroup does everything needed to behave as a normal group. You can @@ -304,7 +381,7 @@ class AbstractGroup(object): def sprites(self): """get a list of sprites in the group - Group.sprite(): return list + Group.sprites(): return list Returns an object that can be looped over with a 'for' loop. (For now, it is always a list, but this could change in a future version of @@ -314,16 +391,36 @@ class AbstractGroup(object): """ return list(self.spritedict) - def add_internal(self, sprite): - self.spritedict[sprite] = 0 + def add_internal( + self, + sprite, + layer=None, # noqa pylint: disable=unused-argument; supporting legacy derived classes that override in non-pythonic way + ): + """ + For adding a sprite to this group internally. + + :param sprite: The sprite we are adding. + :param layer: the layer to add to, if the group type supports layers + """ + self.spritedict[sprite] = None def remove_internal(self, sprite): - r = self.spritedict[sprite] - if r: - self.lostsprites.append(r) + """ + For removing a sprite from this group internally. + + :param sprite: The sprite we are removing. + """ + lost_rect = self.spritedict[sprite] + if lost_rect: + self.lostsprites.append(lost_rect) del self.spritedict[sprite] def has_internal(self, sprite): + """ + For checking if a sprite is in this group internally. + + :param sprite: The sprite we are checking. + """ return sprite in self.spritedict def copy(self): @@ -335,7 +432,9 @@ class AbstractGroup(object): and has the same sprites in it. """ - return self.__class__(self.sprites()) + return self.__class__( # noqa pylint: disable=too-many-function-args + self.sprites() # Needed because copy() won't work on AbstractGroup + ) def __iter__(self): return iter(self.sprites()) @@ -369,7 +468,7 @@ class AbstractGroup(object): # instance of the Sprite class or is not an instance of a # subclass of the Sprite class. Alternately, it could be an # old-style sprite group. - if hasattr(sprite, '_spritegroup'): + if hasattr(sprite, "_spritegroup"): for spr in sprite.sprites(): if not self.has_internal(spr): self.add_internal(spr) @@ -388,8 +487,8 @@ class AbstractGroup(object): """ # This function behaves essentially the same as Group.add. It first # tries to handle each argument as an instance of the Sprite class. If - # that failes, then it tries to handle the argument as an iterable - # object. If that failes, then it tries to handle the argument as an + # that fails, then it tries to handle the argument as an iterable + # object. If that fails, then it tries to handle the argument as an # old-style sprite group. Lastly, if that fails, it assumes that the # normal Sprite methods should be used. for sprite in sprites: @@ -401,7 +500,7 @@ class AbstractGroup(object): try: self.remove(*sprite) except (TypeError, AttributeError): - if hasattr(sprite, '_spritegroup'): + if hasattr(sprite, "_spritegroup"): for spr in sprite.sprites(): if self.has_internal(spr): self.remove_internal(spr) @@ -420,61 +519,70 @@ class AbstractGroup(object): 'in' operator, e.g. 'sprite in group', 'subgroup in group'. """ - return_value = False + if not sprites: + return False # return False if no sprites passed in for sprite in sprites: if isinstance(sprite, Sprite): # Check for Sprite instance's membership in this group - if self.has_internal(sprite): - return_value = True - else: + if not self.has_internal(sprite): return False else: try: - if self.has(*sprite): - return_value = True - else: + if not self.has(*sprite): return False except (TypeError, AttributeError): - if hasattr(sprite, '_spritegroup'): + if hasattr(sprite, "_spritegroup"): for spr in sprite.sprites(): - if self.has_internal(spr): - return_value = True - else: + if not self.has_internal(spr): return False else: - if self.has_internal(sprite): - return_value = True - else: + if not self.has_internal(sprite): return False - return return_value + return True - def update(self, *args): + def update(self, *args, **kwargs): """call the update method of every member sprite - Group.update(*args): return None + Group.update(*args, **kwargs): return None Calls the update method of every member sprite. All arguments that were passed to this method are passed to the Sprite update function. """ - for s in self.sprites(): - s.update(*args) + for sprite in self.sprites(): + sprite.update(*args, **kwargs) - def draw(self, surface): + def draw( + self, surface, bgsurf=None, special_flags=0 + ): # noqa pylint: disable=unused-argument; bgsurf arg used in LayeredDirty """draw all sprites onto the surface - Group.draw(surface): return None + Group.draw(surface, special_flags=0): return Rect_list Draws all of the member sprites onto the given surface. """ sprites = self.sprites() - surface_blit = surface.blit - for spr in sprites: - self.spritedict[spr] = surface_blit(spr.image, spr.rect) + if hasattr(surface, "blits"): + self.spritedict.update( + zip( + sprites, + surface.blits( + (spr.image, spr.rect, None, special_flags) for spr in sprites + ), + ) + ) + else: + for spr in sprites: + self.spritedict[spr] = surface.blit( + spr.image, spr.rect, None, special_flags + ) self.lostsprites = [] + dirty = self.lostsprites + + return dirty def clear(self, surface, bgd): """erase the previous position of all sprites @@ -488,18 +596,18 @@ class AbstractGroup(object): """ if callable(bgd): - for r in self.lostsprites: - bgd(surface, r) - for r in self.spritedict.values(): - if r: - bgd(surface, r) + for lost_clear_rect in self.lostsprites: + bgd(surface, lost_clear_rect) + for clear_rect in self.spritedict.values(): + if clear_rect: + bgd(surface, clear_rect) else: surface_blit = surface.blit - for r in self.lostsprites: - surface_blit(bgd, r, r) - for r in self.spritedict.values(): - if r: - surface_blit(bgd, r, r) + for lost_clear_rect in self.lostsprites: + surface_blit(bgd, lost_clear_rect, lost_clear_rect) + for clear_rect in self.spritedict.values(): + if clear_rect: + surface_blit(bgd, clear_rect, clear_rect) def empty(self): """remove all sprites @@ -509,12 +617,12 @@ class AbstractGroup(object): Removes all the sprites from the group. """ - for s in self.sprites(): - self.remove_internal(s) - s.remove_internal(self) + for sprite in self.sprites(): + self.remove_internal(sprite) + sprite.remove_internal(self) - def __nonzero__(self): - return truth(self.sprites()) + def __bool__(self): + return bool(self.sprites()) def __len__(self): """return number of sprites in group @@ -527,7 +635,8 @@ class AbstractGroup(object): return len(self.sprites()) def __repr__(self): - return "<%s(%d sprites)>" % (self.__class__.__name__, len(self)) + return f"<{self.__class__.__name__}({len(self)} sprites)>" + class Group(AbstractGroup): """container class for many Sprites @@ -548,13 +657,16 @@ class Group(AbstractGroup): iterated over in no particular order. """ + def __init__(self, *sprites): AbstractGroup.__init__(self) self.add(*sprites) + RenderPlain = Group RenderClear = Group + class RenderUpdates(Group): """Group class that tracks dirty updates @@ -564,30 +676,31 @@ class RenderUpdates(Group): method that tracks the changed areas of the screen. """ - def draw(self, surface): - spritedict = self.spritedict - surface_blit = surface.blit - dirty = self.lostsprites - self.lostsprites = [] - dirty_append = dirty.append - for s in self.sprites(): - r = spritedict[s] - newrect = surface_blit(s.image, s.rect) - if r: - if newrect.colliderect(r): - dirty_append(newrect.union(r)) - else: - dirty_append(newrect) - dirty_append(r) - else: - dirty_append(newrect) - spritedict[s] = newrect - return dirty + + def draw(self, surface, bgsurf=None, special_flags=0): + surface_blit = surface.blit + dirty = self.lostsprites + self.lostsprites = [] + dirty_append = dirty.append + for sprite in self.sprites(): + old_rect = self.spritedict[sprite] + new_rect = surface_blit(sprite.image, sprite.rect, None, special_flags) + if old_rect: + if new_rect.colliderect(old_rect): + dirty_append(new_rect.union(old_rect)) + else: + dirty_append(new_rect) + dirty_append(old_rect) + else: + dirty_append(new_rect) + self.spritedict[sprite] = new_rect + return dirty + class OrderedUpdates(RenderUpdates): """RenderUpdates class that draws Sprites in order of addition - pygame.sprite.OrderedUpdates(*spites): return OrderedUpdates + pygame.sprite.OrderedUpdates(*sprites): return OrderedUpdates This class derives from pygame.sprite.RenderUpdates(). It maintains the order in which the Sprites were added to the Group for rendering. @@ -595,14 +708,15 @@ class OrderedUpdates(RenderUpdates): slower than regular Groups. """ + def __init__(self, *sprites): self._spritelist = [] RenderUpdates.__init__(self, *sprites) def sprites(self): - return list(self._spritelist) + return self._spritelist.copy() - def add_internal(self, sprite): + def add_internal(self, sprite, layer=None): RenderUpdates.add_internal(self, sprite) self._spritelist.append(sprite) @@ -614,7 +728,7 @@ class OrderedUpdates(RenderUpdates): class LayeredUpdates(AbstractGroup): """LayeredUpdates Group handles layers, which are drawn like OrderedUpdates - pygame.sprite.LayeredUpdates(*spites, **kwargs): return LayeredUpdates + pygame.sprite.LayeredUpdates(*sprites, **kwargs): return LayeredUpdates This group is fully compatible with pygame.sprite.Sprite. New in pygame 1.8.0 @@ -639,7 +753,7 @@ class LayeredUpdates(AbstractGroup): self._spritelayers = {} self._spritelist = [] AbstractGroup.__init__(self) - self._default_layer = kwargs.get('default_layer', 0) + self._default_layer = kwargs.get("default_layer", 0) self.add(*sprites, **kwargs) @@ -653,13 +767,14 @@ class LayeredUpdates(AbstractGroup): if layer is None: try: - layer = sprite._layer + layer = sprite.layer except AttributeError: - layer = sprite._layer = self._default_layer - elif hasattr(sprite, '_layer'): - sprite._layer = layer + layer = self._default_layer + setattr(sprite, "_layer", layer) + elif hasattr(sprite, "_layer"): + setattr(sprite, "_layer", layer) - sprites = self._spritelist # speedup + sprites = self._spritelist # speedup sprites_layers = self._spritelayers sprites_layers[sprite] = layer @@ -694,10 +809,7 @@ class LayeredUpdates(AbstractGroup): if not sprites: return - if 'layer' in kwargs: - layer = kwargs['layer'] - else: - layer = None + layer = kwargs["layer"] if "layer" in kwargs else None for sprite in sprites: # It's possible that some sprite is also an iterator. # If this is the case, we should add the sprite itself, @@ -716,7 +828,7 @@ class LayeredUpdates(AbstractGroup): # instance of the Sprite class or is not an instance of a # subclass of the Sprite class. Alternately, it could be an # old-style sprite group. - if hasattr(sprite, '_spritegroup'): + if hasattr(sprite, "_spritegroup"): for spr in sprite.sprites(): if not self.has_internal(spr): self.add_internal(spr, layer) @@ -733,11 +845,11 @@ class LayeredUpdates(AbstractGroup): """ self._spritelist.remove(sprite) # these dirty rects are suboptimal for one frame - r = self.spritedict[sprite] - if r is not self._init_rect: - self.lostsprites.append(r) # dirty rect - if hasattr(sprite, 'rect'): - self.lostsprites.append(sprite.rect) # dirty rect + old_rect = self.spritedict[sprite] + if old_rect is not self._init_rect: + self.lostsprites.append(old_rect) # dirty rect + if hasattr(sprite, "rect"): + self.lostsprites.append(sprite.rect) # dirty rect del self.spritedict[sprite] del self._spritelayers[sprite] @@ -748,12 +860,12 @@ class LayeredUpdates(AbstractGroup): LayeredUpdates.sprites(): return sprites """ - return list(self._spritelist) + return self._spritelist.copy() - def draw(self, surface): + def draw(self, surface, bgsurf=None, special_flags=0): """draw all sprites in the right order onto the passed surface - LayeredUpdates.draw(surface): return Rect_list + LayeredUpdates.draw(surface, special_flags=0): return Rect_list """ spritedict = self.spritedict @@ -764,7 +876,7 @@ class LayeredUpdates(AbstractGroup): init_rect = self._init_rect for spr in self.sprites(): rec = spritedict[spr] - newrect = surface_blit(spr.image, spr.rect) + newrect = surface_blit(spr.image, spr.rect, None, special_flags) if rec is init_rect: dirty_append(newrect) else: @@ -785,10 +897,9 @@ class LayeredUpdates(AbstractGroup): """ _sprites = self._spritelist - rect = Rect(pos, (0, 0)) + rect = Rect(pos, (1, 1)) colliding_idx = rect.collidelistall(_sprites) - colliding = [_sprites[i] for i in colliding_idx] - return colliding + return [_sprites[i] for i in colliding_idx] def get_sprite(self, idx): """return the sprite at the index idx from the groups sprites @@ -810,7 +921,7 @@ class LayeredUpdates(AbstractGroup): self.remove(*sprites) return sprites - #---# layer methods + # layer methods def layers(self): """return a list of unique defined layers defined. @@ -828,8 +939,8 @@ class LayeredUpdates(AbstractGroup): checked. """ - sprites = self._spritelist # speedup - sprites_layers = self._spritelayers # speedup + sprites = self._spritelist # speedup + sprites_layers = self._spritelayers # speedup sprites.remove(sprite) sprites_layers.pop(sprite) @@ -849,8 +960,8 @@ class LayeredUpdates(AbstractGroup): while mid < leng and sprites_layers[sprites[mid]] <= new_layer: mid += 1 sprites.insert(mid, sprite) - if hasattr(sprite, 'layer'): - sprite.layer = new_layer + if hasattr(sprite, "_layer"): + setattr(sprite, "_layer", new_layer) # add layer info sprites_layers[sprite] = new_layer @@ -926,8 +1037,9 @@ class LayeredUpdates(AbstractGroup): for spr in self._spritelist: if sprite_layers[spr] == layer: sprites_append(spr) - elif sprite_layers[spr] > layer:# break after because no other will - # follow with same layer + elif sprite_layers[spr] > layer: + # break after because no other will + # follow with same layer break return sprites @@ -949,7 +1061,7 @@ class LayeredUpdates(AbstractGroup): class LayeredDirty(LayeredUpdates): """LayeredDirty Group is for DirtySprites; subclasses LayeredUpdates - pygame.sprite.LayeredDirty(*spites, **kwargs): return LayeredDirty + pygame.sprite.LayeredDirty(*sprites, **kwargs): return LayeredDirty This group requires pygame.sprite.DirtySprite or any sprite that has the following attributes: @@ -965,7 +1077,7 @@ class LayeredDirty(LayeredUpdates): _use_update: True/False (default is False) _default_layer: default layer where the sprites without a layer are added - _time_threshold: treshold time for switching between dirty rect mode + _time_threshold: threshold time for switching between dirty rect mode and fullscreen mode; defaults to updating at 80 frames per second, which is equal to 1000.0 / 80.0 @@ -976,13 +1088,13 @@ class LayeredDirty(LayeredUpdates): def __init__(self, *sprites, **kwargs): """initialize group. - pygame.sprite.LayeredDirty(*spites, **kwargs): return LayeredDirty + pygame.sprite.LayeredDirty(*sprites, **kwargs): return LayeredDirty You can specify some additional attributes through kwargs: _use_update: True/False (default is False) _default_layer: default layer where the sprites without a layer are added - _time_threshold: treshold time for switching between dirty rect + _time_threshold: threshold time for switching between dirty rect mode and fullscreen mode; defaults to updating at 80 frames per second, which is equal to 1000.0 / 80.0 @@ -992,13 +1104,14 @@ class LayeredDirty(LayeredUpdates): self._use_update = False - self._time_threshold = 1000.0 / 80.0 # 1000.0 / fps + self._time_threshold = 1000.0 / 80.0 # 1000.0 / fps self._bgd = None for key, val in kwargs.items(): - if key in ['_use_update', '_time_threshold', '_default_layer']: - if hasattr(self, key): - setattr(self, key, val) + if key in ["_use_update", "_time_threshold", "_default_layer"] and hasattr( + self, key + ): + setattr(self, key, val) def add_internal(self, sprite, layer=None): """Do not use this method directly. @@ -1007,160 +1120,187 @@ class LayeredDirty(LayeredUpdates): """ # check if all needed attributes are set - if not hasattr(sprite, 'dirty'): + if not hasattr(sprite, "dirty"): raise AttributeError() - if not hasattr(sprite, 'visible'): + if not hasattr(sprite, "visible"): raise AttributeError() - if not hasattr(sprite, 'blendmode'): + if not hasattr(sprite, "blendmode"): raise AttributeError() if not isinstance(sprite, DirtySprite): raise TypeError() - if sprite.dirty == 0: # set it dirty if it is not + if sprite.dirty == 0: # set it dirty if it is not sprite.dirty = 1 LayeredUpdates.add_internal(self, sprite, layer) - def draw(self, surface, bgd=None): + def draw(self, surface, bgsurf=None, special_flags=None): """draw all sprites in the right order onto the given surface - LayeredDirty.draw(surface, bgd=None): return Rect_list + LayeredDirty.draw(surface, bgsurf=None, special_flags=None): return Rect_list You can pass the background too. If a self.bgd is already set to some - value that is not None, then the bgd argument has no effect. + value that is not None, then the bgsurf argument has no effect. + Passing a value to special_flags will pass that value as the + special_flags argument to pass to all Surface.blit calls, overriding + the sprite.blendmode attribute """ - # speedups - _orig_clip = surface.get_clip() - _clip = self._clip - if _clip is None: - _clip = _orig_clip - - _surf = surface - _sprites = self._spritelist - _old_rect = self.spritedict - _update = self.lostsprites - _update_append = _update.append - _ret = None - _surf_blit = _surf.blit - _rect = Rect - if bgd is not None: - self._bgd = bgd - _bgd = self._bgd - init_rect = self._init_rect - - _surf.set_clip(_clip) + # functions and classes assigned locally to speed up loops + orig_clip = surface.get_clip() + latest_clip = self._clip + if latest_clip is None: + latest_clip = orig_clip + + local_sprites = self._spritelist + local_old_rect = self.spritedict + local_update = self.lostsprites + rect_type = Rect + + surf_blit_func = surface.blit + if bgsurf is not None: + self._bgd = bgsurf + local_bgd = self._bgd + + surface.set_clip(latest_clip) # ------- # 0. decide whether to render with update or flip start_time = get_ticks() - if self._use_update: # dirty rects mode - # 1. find dirty area on screen and put the rects into _update - # still not happy with that part - for spr in _sprites: - if 0 < spr.dirty: - # chose the right rect - if spr.source_rect: - _union_rect = _rect(spr.rect.topleft, - spr.source_rect.size) - else: - _union_rect = _rect(spr.rect) - - _union_rect_collidelist = _union_rect.collidelist - _union_rect_union_ip = _union_rect.union_ip - i = _union_rect_collidelist(_update) - while -1 < i: - _union_rect_union_ip(_update[i]) - del _update[i] - i = _union_rect_collidelist(_update) - _update_append(_union_rect.clip(_clip)) - - if _old_rect[spr] is not init_rect: - _union_rect = _rect(_old_rect[spr]) - _union_rect_collidelist = _union_rect.collidelist - _union_rect_union_ip = _union_rect.union_ip - i = _union_rect_collidelist(_update) - while -1 < i: - _union_rect_union_ip(_update[i]) - del _update[i] - i = _union_rect_collidelist(_update) - _update_append(_union_rect.clip(_clip)) + if self._use_update: # dirty rects mode + # 1. find dirty area on screen and put the rects into + # self.lostsprites still not happy with that part + self._find_dirty_area( + latest_clip, + local_old_rect, + rect_type, + local_sprites, + local_update, + local_update.append, + self._init_rect, + ) # can it be done better? because that is an O(n**2) algorithm in # worst case # clear using background - if _bgd is not None: - for rec in _update: - _surf_blit(_bgd, rec, rec) + if local_bgd is not None: + flags = 0 if special_flags is None else special_flags + for rec in local_update: + surf_blit_func(local_bgd, rec, rec, flags) # 2. draw - for spr in _sprites: - if 1 > spr.dirty: - if spr._visible: - # sprite not dirty; blit only the intersecting part - if spr.source_rect is not None: - # For possible future speed up, source_rect's data - # can be prefetched outside of this loop. - _spr_rect = _rect(spr.rect.topleft, - spr.source_rect.size) - rect_offset_x = spr.source_rect[0] - _spr_rect[0] - rect_offset_y = spr.source_rect[1] - _spr_rect[1] - else: - _spr_rect = spr.rect - rect_offset_x = -_spr_rect[0] - rect_offset_y = -_spr_rect[1] - - _spr_rect_clip = _spr_rect.clip - - for idx in _spr_rect.collidelistall(_update): - # clip - clip = _spr_rect_clip(_update[idx]) - _surf_blit(spr.image, - clip, - (clip[0] + rect_offset_x, - clip[1] + rect_offset_y, - clip[2], - clip[3]), - spr.blendmode) - else: # dirty sprite - if spr._visible: - _old_rect[spr] = _surf_blit(spr.image, - spr.rect, - spr.source_rect, - spr.blendmode) - if spr.dirty == 1: - spr.dirty = 0 - _ret = list(_update) - else: # flip, full screen mode - if _bgd is not None: - _surf_blit(_bgd, (0, 0)) - for spr in _sprites: - if spr._visible: - _old_rect[spr] = _surf_blit(spr.image, - spr.rect, - spr.source_rect, - spr.blendmode) - _ret = [_rect(_clip)] # return only the part of the screen changed - + self._draw_dirty_internal( + local_old_rect, + rect_type, + local_sprites, + surf_blit_func, + local_update, + special_flags, + ) + local_ret = list(local_update) + else: # flip, full screen mode + if local_bgd is not None: + flags = 0 if special_flags is None else special_flags + surf_blit_func(local_bgd, (0, 0), None, flags) + for spr in local_sprites: + if spr.visible: + flags = spr.blendmode if special_flags is None else special_flags + local_old_rect[spr] = surf_blit_func( + spr.image, spr.rect, spr.source_rect, flags + ) + # return only the part of the screen changed + local_ret = [rect_type(latest_clip)] # timing for switching modes # How may a good threshold be found? It depends on the hardware. end_time = get_ticks() - if end_time-start_time > self._time_threshold: + if end_time - start_time > self._time_threshold: self._use_update = False else: self._use_update = True -## # debug -## print " check: using dirty rects:", self._use_update - - # emtpy dirty rects list - _update[:] = [] + # empty dirty rects list + local_update[:] = [] # ------- # restore original clip - _surf.set_clip(_orig_clip) - return _ret + surface.set_clip(orig_clip) + return local_ret + + @staticmethod + def _draw_dirty_internal( + _old_rect, _rect, _sprites, _surf_blit, _update, _special_flags + ): + for spr in _sprites: + flags = spr.blendmode if _special_flags is None else _special_flags + if spr.dirty < 1 and spr.visible: + # sprite not dirty; blit only the intersecting part + if spr.source_rect is not None: + # For possible future speed up, source_rect's data + # can be pre-fetched outside of this loop. + _spr_rect = _rect(spr.rect.topleft, spr.source_rect.size) + rect_offset_x = spr.source_rect[0] - _spr_rect[0] + rect_offset_y = spr.source_rect[1] - _spr_rect[1] + else: + _spr_rect = spr.rect + rect_offset_x = -_spr_rect[0] + rect_offset_y = -_spr_rect[1] + + _spr_rect_clip = _spr_rect.clip + + for idx in _spr_rect.collidelistall(_update): + # clip + clip = _spr_rect_clip(_update[idx]) + _surf_blit( + spr.image, + clip, + ( + clip[0] + rect_offset_x, + clip[1] + rect_offset_y, + clip[2], + clip[3], + ), + flags, + ) + else: # dirty sprite + if spr.visible: + _old_rect[spr] = _surf_blit( + spr.image, spr.rect, spr.source_rect, flags + ) + if spr.dirty == 1: + spr.dirty = 0 + + @staticmethod + def _find_dirty_area( + _clip, _old_rect, _rect, _sprites, _update, _update_append, init_rect + ): + for spr in _sprites: + if spr.dirty > 0: + # chose the right rect + if spr.source_rect: + _union_rect = _rect(spr.rect.topleft, spr.source_rect.size) + else: + _union_rect = _rect(spr.rect) + + _union_rect_collidelist = _union_rect.collidelist + _union_rect_union_ip = _union_rect.union_ip + i = _union_rect_collidelist(_update) + while i > -1: + _union_rect_union_ip(_update[i]) + del _update[i] + i = _union_rect_collidelist(_update) + _update_append(_union_rect.clip(_clip)) + + if _old_rect[spr] is not init_rect: + _union_rect = _rect(_old_rect[spr]) + _union_rect_collidelist = _union_rect.collidelist + _union_rect_union_ip = _union_rect.union_ip + i = _union_rect_collidelist(_update) + while i > -1: + _union_rect_union_ip(_update[i]) + del _update[i] + i = _union_rect_collidelist(_update) + _update_append(_union_rect.clip(_clip)) def clear(self, surface, bgd): """use to set background @@ -1217,7 +1357,7 @@ class LayeredDirty(LayeredUpdates): sprite.dirty = 1 def set_timing_treshold(self, time_ms): - """set the treshold in milliseconds + """set the threshold in milliseconds set_timing_treshold(time_ms): return None @@ -1226,8 +1366,34 @@ class LayeredDirty(LayeredUpdates): method is taking so long to update the screen that the frame rate falls below 80 frames per second. + Raises TypeError if time_ms is not int or float. + + """ + warn( + "This function will be removed, use set_timing_threshold function instead", + DeprecationWarning, + ) + self.set_timing_threshold(time_ms) + + def set_timing_threshold(self, time_ms): + """set the threshold in milliseconds + + set_timing_threshold(time_ms): return None + + Defaults to 1000.0 / 80.0. This means that the screen will be painted + using the flip method rather than the update method if the update + method is taking so long to update the screen that the frame rate falls + below 80 frames per second. + + Raises TypeError if time_ms is not int or float. + """ - self._time_threshold = time_ms + if isinstance(time_ms, (int, float)): + self._time_threshold = time_ms + else: + raise TypeError( + f"Expected numeric value, got {time_ms.__class__.__name__} instead" + ) class GroupSingle(AbstractGroup): @@ -1254,16 +1420,15 @@ class GroupSingle(AbstractGroup): def sprites(self): if self.__sprite is not None: return [self.__sprite] - else: - return [] + return [] - def add_internal(self, sprite): + def add_internal(self, sprite, layer=None): if self.__sprite is not None: self.__sprite.remove_internal(self) self.remove_internal(self.__sprite) self.__sprite = sprite - def __nonzero__(self): + def __bool__(self): return self.__sprite is not None def _get_sprite(self): @@ -1274,10 +1439,18 @@ class GroupSingle(AbstractGroup): sprite.add_internal(self) return sprite - sprite = property(_get_sprite, - _set_sprite, - None, - "The sprite contained in this group") + @property + def sprite(self): + """ + Property for the single sprite contained in this group + + :return: The sprite. + """ + return self._get_sprite() + + @sprite.setter + def sprite(self, sprite_to_set): + self._set_sprite(sprite_to_set) def remove_internal(self, sprite): if sprite is self.__sprite: @@ -1309,7 +1482,8 @@ def collide_rect(left, right): """ return left.rect.colliderect(right.rect) -class collide_rect_ratio: + +class collide_rect_ratio: # noqa pylint: disable=invalid-name; this is a function-like class """A callable class that checks for collisions using scaled rects The class checks for collisions between two sprites using a scaled version @@ -1330,6 +1504,17 @@ class collide_rect_ratio: """ self.ratio = ratio + def __repr__(self): + """ + Turn the class into a string. + """ + # pylint: disable=consider-using-f-string + return "<{klass} @{id:x} {attrs}>".format( + klass=self.__class__.__name__, + id=id(self) & 0xFFFFFF, + attrs=" ".join(f"{k}={v!r}" for k, v in self.__dict__.items()), + ) + def __call__(self, left, right): """detect collision between two sprites using scaled rects @@ -1346,17 +1531,16 @@ class collide_rect_ratio: leftrect = left.rect width = leftrect.width height = leftrect.height - leftrect = leftrect.inflate(width * ratio - width, - height * ratio - height) + leftrect = leftrect.inflate(width * ratio - width, height * ratio - height) rightrect = right.rect width = rightrect.width height = rightrect.height - rightrect = rightrect.inflate(width * ratio - width, - height * ratio - height) + rightrect = rightrect.inflate(width * ratio - width, height * ratio - height) return leftrect.colliderect(rightrect) + def collide_circle(left, right): """detect collision between two sprites using circles @@ -1376,30 +1560,31 @@ def collide_circle(left, right): xdistance = left.rect.centerx - right.rect.centerx ydistance = left.rect.centery - right.rect.centery - distancesquared = xdistance ** 2 + ydistance ** 2 + distancesquared = xdistance**2 + ydistance**2 - if hasattr(left, 'radius'): + try: leftradius = left.radius - else: + except AttributeError: leftrect = left.rect # approximating the radius of a square by using half of the diagonal, # might give false positives (especially if its a long small rect) - leftradius = 0.5 * ((leftrect.width ** 2 + leftrect.height ** 2) ** 0.5) + leftradius = 0.5 * ((leftrect.width**2 + leftrect.height**2) ** 0.5) # store the radius on the sprite for next time - setattr(left, 'radius', leftradius) + left.radius = leftradius - if hasattr(right, 'radius'): + try: rightradius = right.radius - else: + except AttributeError: rightrect = right.rect # approximating the radius of a square by using half of the diagonal # might give false positives (especially if its a long small rect) - rightradius = 0.5 * ((rightrect.width ** 2 + rightrect.height ** 2) ** 0.5) + rightradius = 0.5 * ((rightrect.width**2 + rightrect.height**2) ** 0.5) # store the radius on the sprite for next time - setattr(right, 'radius', rightradius) + right.radius = rightradius return distancesquared <= (leftradius + rightradius) ** 2 -class collide_circle_ratio(object): + +class collide_circle_ratio: # noqa pylint: disable=invalid-name; this is a function-like class """detect collision between two sprites using scaled circles This callable class checks for collisions between two sprites using a @@ -1417,12 +1602,22 @@ class collide_circle_ratio(object): The given ratio is expected to be a floating point value used to scale the underlying sprite radius before checking for collisions. - When the ratio is ratio=1.0, then it behaves exactly like the + When the ratio is ratio=1.0, then it behaves exactly like the collide_circle method. """ self.ratio = ratio + def __repr__(self): + """ + Turn the class into a string. + """ + # pylint: disable=consider-using-f-string + return "<{klass} @{id:x} {attrs}>".format( + klass=self.__class__.__name__, + id=id(self) & 0xFFFFFF, + attrs=" ".join(f"{k}={v!r}" for k, v in self.__dict__.items()), + ) def __call__(self, left, right): """detect collision between two sprites using scaled circles @@ -1443,26 +1638,29 @@ class collide_circle_ratio(object): ratio = self.ratio xdistance = left.rect.centerx - right.rect.centerx ydistance = left.rect.centery - right.rect.centery - distancesquared = xdistance ** 2 + ydistance ** 2 + distancesquared = xdistance**2 + ydistance**2 - if hasattr(left, "radius"): - leftradius = left.radius * ratio - else: + try: + leftradius = left.radius + except AttributeError: leftrect = left.rect - leftradius = ratio * 0.5 * ((leftrect.width ** 2 + leftrect.height ** 2) ** 0.5) + leftradius = 0.5 * ((leftrect.width**2 + leftrect.height**2) ** 0.5) # store the radius on the sprite for next time - setattr(left, 'radius', leftradius) + left.radius = leftradius + leftradius *= ratio - if hasattr(right, "radius"): - rightradius = right.radius * ratio - else: + try: + rightradius = right.radius + except AttributeError: rightrect = right.rect - rightradius = ratio * 0.5 * ((rightrect.width ** 2 + rightrect.height ** 2) ** 0.5) + rightradius = 0.5 * ((rightrect.width**2 + rightrect.height**2) ** 0.5) # store the radius on the sprite for next time - setattr(right, 'radius', rightradius) + right.radius = rightradius + rightradius *= ratio return distancesquared <= (leftradius + rightradius) ** 2 + def collide_mask(left, right): """collision detection between two sprites, using masks. @@ -1489,6 +1687,7 @@ def collide_mask(left, right): rightmask = from_surface(right.image) return leftmask.overlap(rightmask, (xoffset, yoffset)) + def spritecollide(sprite, group, dokill, collided=None): """find Sprites in a Group that intersect another Sprite @@ -1509,30 +1708,36 @@ def spritecollide(sprite, group, dokill, collided=None): which will be used to calculate the collision. """ - if dokill: + # pull the default collision function in as a local variable outside + # the loop as this makes the loop run faster + default_sprite_collide_func = sprite.rect.colliderect + if dokill: crashed = [] append = crashed.append - if collided: - for s in group.sprites(): - if collided(sprite, s): - s.kill() - append(s) - else: - spritecollide = sprite.rect.colliderect - for s in group.sprites(): - if spritecollide(s.rect): - s.kill() - append(s) + for group_sprite in group.sprites(): + if collided is not None: + if collided(sprite, group_sprite): + group_sprite.kill() + append(group_sprite) + else: + if default_sprite_collide_func(group_sprite.rect): + group_sprite.kill() + append(group_sprite) return crashed - elif collided: - return [s for s in group if collided(sprite, s)] - else: - spritecollide = sprite.rect.colliderect - return [s for s in group if spritecollide(s.rect)] + if collided is not None: + return [ + group_sprite for group_sprite in group if collided(sprite, group_sprite) + ] + + return [ + group_sprite + for group_sprite in group + if default_sprite_collide_func(group_sprite.rect) + ] def groupcollide(groupa, groupb, dokilla, dokillb, collided=None): @@ -1554,20 +1759,23 @@ def groupcollide(groupa, groupb, dokilla, dokillb, collided=None): """ crashed = {} - SC = spritecollide + # pull the collision function in as a local variable outside + # the loop as this makes the loop run faster + sprite_collide_func = spritecollide if dokilla: - for s in groupa.sprites(): - c = SC(s, groupb, dokillb, collided) - if c: - crashed[s] = c - s.kill() + for group_a_sprite in groupa.sprites(): + collision = sprite_collide_func(group_a_sprite, groupb, dokillb, collided) + if collision: + crashed[group_a_sprite] = collision + group_a_sprite.kill() else: - for s in groupa: - c = SC(s, groupb, dokillb, collided) - if c: - crashed[s] = c + for group_a_sprite in groupa: + collision = sprite_collide_func(group_a_sprite, groupb, dokillb, collided) + if collision: + crashed[group_a_sprite] = collision return crashed + def spritecollideany(sprite, group, collided=None): """finds any sprites in a group that collide with the given sprite @@ -1588,14 +1796,17 @@ def spritecollideany(sprite, group, collided=None): """ - if collided: - for s in group: - if collided(sprite, s): - return s + # pull the default collision function in as a local variable outside + # the loop as this makes the loop run faster + default_sprite_collide_func = sprite.rect.colliderect + + if collided is not None: + for group_sprite in group: + if collided(sprite, group_sprite): + return group_sprite else: # Special case old behaviour for speed. - spritecollide = sprite.rect.colliderect - for s in group: - if spritecollide(s.rect): - return s + for group_sprite in group: + if default_sprite_collide_func(group_sprite.rect): + return group_sprite return None diff --git a/venv/Lib/site-packages/pygame/surface.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/surface.cp37-win_amd64.pyd deleted file mode 100644 index ec0b0869f5d8d1d45c77813989d1e73b37c7a027..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/surface.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/surfarray.py b/venv/Lib/site-packages/pygame/surfarray.py index 91446d77a8d90257a80f6fc64cde9d4f3cc0c79e..8243460292301bde607f6e229f854e1c6cecd499 100644 --- a/venv/Lib/site-packages/pygame/surfarray.py +++ b/venv/Lib/site-packages/pygame/surfarray.py @@ -20,9 +20,9 @@ """pygame module for accessing surface pixel data using array interfaces -Functions to convert pixel data between pygame Surfaces and arrays. This -module will only be functional when pygame can use the external Numpy or -Numeric packages. +Functions to convert between NumPy arrays and Surface objects. This module +will only be functional when pygame can use the external NumPy package. +If NumPy can't be imported, surfarray becomes a MissingModule object. Every pixel is stored as a single integer value to represent the red, green, and blue colors. The 8bit images use a value that looks into a @@ -35,37 +35,65 @@ as 2D arrays. This module can also separate the red, green, and blue color values into separate indices. These types of arrays are referred to as 3D arrays, and the last index is 0 for red, 1 for green, and 2 for blue. - -Supported array types are - - numpy - numeric (deprecated; will be removed in Pygame 1.9.3.) - -The default will be numpy, if installed. Otherwise, Numeric will be set -as default if installed, and a deprecation warning will be issued. If -neither numpy nor Numeric are installed, the module will raise an -ImportError. - -The array type to use can be changed at runtime using the use_arraytype() -method, which requires one of the above types as string. - -Note: numpy and Numeric are not completely compatible. Certain array -manipulations, which work for one type, might behave differently or even -completely break for the other. - -Additionally, in contrast to Numeric, numpy does use unsigned 16-bit -integers. Images with 16-bit data will be treated as unsigned -integers. Numeric instead uses signed integers for the representation, -which is important to keep in mind, if you use the module's functions -and wonder about the values. """ -# Try to import the necessary modules. -import pygame._numpysurfarray as numpysf - -from pygame.pixelcopy import array_to_surface, make_surface as pc_make_surface -def blit_array (surface, array): +from pygame.pixelcopy import ( + array_to_surface, + surface_to_array, + map_array as pix_map_array, + make_surface as pix_make_surface, +) +import numpy +from numpy import ( + array as numpy_array, + empty as numpy_empty, + uint32 as numpy_uint32, + ndarray as numpy_ndarray, +) + +import warnings # will be removed in the future + + +# float96 not available on all numpy versions. +numpy_floats = [] +for type_name in "float32 float64 float96".split(): + if hasattr(numpy, type_name): + numpy_floats.append(getattr(numpy, type_name)) +# Added below due to deprecation of numpy.float. See issue #2814 +numpy_floats.append(float) + +# Pixel sizes corresponding to NumPy supported integer sizes, and therefore +# permissible for 2D reference arrays. +_pixel2d_bitdepths = {8, 16, 32} + + +__all__ = [ + "array2d", + "array3d", + "array_alpha", + "array_blue", + "array_colorkey", + "array_green", + "array_red", + "array_to_surface", + "blit_array", + "get_arraytype", + "get_arraytypes", + "make_surface", + "map_array", + "pixels2d", + "pixels3d", + "pixels_alpha", + "pixels_blue", + "pixels_green", + "pixels_red", + "surface_to_array", + "use_arraytype", +] + + +def blit_array(surface, array): """pygame.surfarray.blit_array(Surface, array): return None Blit directly from a array values. @@ -78,12 +106,28 @@ def blit_array (surface, array): This function will temporarily lock the Surface as the new values are copied. """ - return numpysf.blit_array (surface, array) + if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: + array = array.round(0).astype(numpy_uint32) + return array_to_surface(surface, array) + + +def make_surface(array): + """pygame.surfarray.make_surface (array): return Surface + + Copy an array to a new surface. + + Create a new Surface that best resembles the data and format on the + array. The array can be 2D or 3D with any sized integer values. + """ + if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats: + array = array.round(0).astype(numpy_uint32) + return pix_make_surface(array) -def array2d (surface): - """pygame.surfarray.array2d (Surface): return array - Copy pixels into a 2d array. +def array2d(surface): + """pygame.surfarray.array2d(Surface): return array + + copy pixels into a 2d array Copy the pixels from a Surface into a 2D array. The bit depth of the surface will control the size of the integer values, and will work @@ -93,13 +137,22 @@ def array2d (surface): (see the Surface.lock - lock the Surface memory for pixel access method). """ - return numpysf.array2d (surface) + bpp = surface.get_bytesize() + try: + dtype = (numpy.uint8, numpy.uint16, numpy.int32, numpy.int32)[bpp - 1] + except IndexError: + raise ValueError(f"unsupported bit depth {bpp * 8} for 2D array") + size = surface.get_size() + array = numpy.empty(size, dtype) + surface_to_array(array, surface) + return array + + +def pixels2d(surface): + """pygame.surfarray.pixels2d(Surface): return array -def pixels2d (surface): - """pygame.surfarray.pixels2d (Surface): return array + reference pixels into a 2d array - Reference pixels into a 2d array. - Create a new 2D array that directly references the pixel values in a Surface. Any changes to the array will affect the pixels in the Surface. This is a fast operation since no data is copied. @@ -111,12 +164,20 @@ def pixels2d (surface): the array (see the Surface.lock - lock the Surface memory for pixel access method). """ - return numpysf.pixels2d (surface) + if surface.get_bitsize() not in _pixel2d_bitdepths: + raise ValueError("unsupported bit depth for 2D reference array") + try: + return numpy_array(surface.get_view("2"), copy=False) + except (ValueError, TypeError): + raise ValueError( + f"bit depth {surface.get_bitsize()} unsupported for 2D reference array" + ) -def array3d (surface): - """pygame.surfarray.array3d (Surface): return array - Copy pixels into a 3d array. +def array3d(surface): + """pygame.surfarray.array3d(Surface): return array + + copy pixels into a 3d array Copy the pixels from a Surface into a 3D array. The bit depth of the surface will control the size of the integer values, and will work @@ -126,12 +187,16 @@ def array3d (surface): (see the Surface.lock - lock the Surface memory for pixel access method). """ - return numpysf.array3d (surface) + width, height = surface.get_size() + array = numpy.empty((width, height, 3), numpy.uint8) + surface_to_array(array, surface) + return array + -def pixels3d (surface): - """pygame.surfarray.pixels3d (Surface): return array +def pixels3d(surface): + """pygame.surfarray.pixels3d(Surface): return array - Reference pixels into a 3d array. + reference pixels into a 3d array Create a new 3D array that directly references the pixel values in a Surface. Any changes to the array will affect the pixels in the @@ -144,12 +209,13 @@ def pixels3d (surface): the array (see the Surface.lock - lock the Surface memory for pixel access method). """ - return numpysf.pixels3d (surface) + return numpy_array(surface.get_view("3"), copy=False) -def array_alpha (surface): - """pygame.surfarray.array_alpha (Surface): return array - Copy pixel alphas into a 2d array. +def array_alpha(surface): + """pygame.surfarray.array_alpha(Surface): return array + + copy pixel alphas into a 2d array Copy the pixel alpha values (degree of transparency) from a Surface into a 2D array. This will work for any type of Surface @@ -160,12 +226,16 @@ def array_alpha (surface): (see the Surface.lock - lock the Surface memory for pixel access method). """ - return numpysf.array_alpha (surface) + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "A") + return array + -def pixels_alpha (surface): - """pygame.surfarray.pixels_alpha (Surface): return array +def pixels_alpha(surface): + """pygame.surfarray.pixels_alpha(Surface): return array - Reference pixel alphas into a 2d array. + reference pixel alphas into a 2d array Create a new 2D array that directly references the alpha values (degree of transparency) in a Surface. Any changes to the array will @@ -177,10 +247,11 @@ def pixels_alpha (surface): The Surface this array references will remain locked for the lifetime of the array. """ - return numpysf.pixels_alpha (surface) + return numpy.array(surface.get_view("A"), copy=False) -def pixels_red (surface): - """pygame.surfarray.pixels_red (Surface): return array + +def pixels_red(surface): + """pygame.surfarray.pixels_red(Surface): return array Reference pixel red into a 2d array. @@ -193,10 +264,29 @@ def pixels_red (surface): The Surface this array references will remain locked for the lifetime of the array. """ - return numpysf.pixels_red (surface) + return numpy.array(surface.get_view("R"), copy=False) + + +def array_red(surface): + """pygame.surfarray.array_red(Surface): return array + + copy pixel red into a 2d array + + Copy the pixel red values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "R") + return array -def pixels_green (surface): - """pygame.surfarray.pixels_green (Surface): return array + +def pixels_green(surface): + """pygame.surfarray.pixels_green(Surface): return array Reference pixel green into a 2d array. @@ -209,10 +299,29 @@ def pixels_green (surface): The Surface this array references will remain locked for the lifetime of the array. """ - return numpysf.pixels_green (surface) + return numpy.array(surface.get_view("G"), copy=False) + + +def array_green(surface): + """pygame.surfarray.array_green(Surface): return array + + copy pixel green into a 2d array + + Copy the pixel green values from a Surface into a 2D array. This will work + for any type of Surface format. -def pixels_blue (surface): - """pygame.surfarray.pixels_blue (Surface): return array + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "G") + return array + + +def pixels_blue(surface): + """pygame.surfarray.pixels_blue(Surface): return array Reference pixel blue into a 2d array. @@ -225,16 +334,35 @@ def pixels_blue (surface): The Surface this array references will remain locked for the lifetime of the array. """ - return numpysf.pixels_blue (surface) + return numpy.array(surface.get_view("B"), copy=False) + + +def array_blue(surface): + """pygame.surfarray.array_blue(Surface): return array + + copy pixel blue into a 2d array + + Copy the pixel blue values from a Surface into a 2D array. This will work + for any type of Surface format. + + This function will temporarily lock the Surface as pixels are copied + (see the Surface.lock - lock the Surface memory for pixel access + method). + """ + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "B") + return array -def array_colorkey (surface): - """pygame.surfarray.array_colorkey (Surface): return array - Copy the colorkey values into a 2d array. +def array_colorkey(surface): + """pygame.surfarray.array_colorkey(Surface): return array + + copy the colorkey values into a 2d array Create a new array with the colorkey transparency value from each pixel. If the pixel matches the colorkey it will be fully - tranparent; otherwise it will be fully opaque. + transparent; otherwise it will be fully opaque. This will work on any type of Surface format. If the image has no colorkey a solid opaque array will be returned. @@ -242,49 +370,78 @@ def array_colorkey (surface): This function will temporarily lock the Surface as pixels are copied. """ - return numpysf.array_colorkey (surface) + size = surface.get_size() + array = numpy.empty(size, numpy.uint8) + surface_to_array(array, surface, "C") + return array -def make_surface(array): - """pygame.surfarray.make_surface (array): return Surface - Copy an array to a new surface. +def map_array(surface, array): + """pygame.surfarray.map_array(Surface, array3d): return array2d - Create a new Surface that best resembles the data and format on the - array. The array can be 2D or 3D with any sized integer values. - """ - return numpysf.make_surface (array) - -def map_array (surface, array): - """pygame.surfarray.map_array (Surface, array3d): return array2d - - Map a 3D array into a 2D array. + map a 3d array into a 2d array Convert a 3D array into a 2D array. This will use the given Surface - format to control the conversion. Palette surface formats are not - supported. + format to control the conversion. + + Note: arrays do not need to be 3D, as long as the minor axis has + three elements giving the component colours, any array shape can be + used (for example, a single colour can be mapped, or an array of + colours). The array shape is limited to eleven dimensions maximum, + including the three element minor axis. """ - return numpysf.map_array (surface, array) + if array.ndim == 0: + raise ValueError("array must have at least 1 dimension") + shape = array.shape + if shape[-1] != 3: + raise ValueError("array must be a 3d array of 3-value color data") + target = numpy_empty(shape[:-1], numpy.int32) + pix_map_array(target, array, surface) + return target -def use_arraytype (arraytype): - """pygame.surfarray.use_arraytype (arraytype): return None + +def use_arraytype(arraytype): + """pygame.surfarray.use_arraytype(arraytype): return None DEPRECATED - only numpy arrays are now supported. """ - arraytype = arraytype.lower () + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) + arraytype = arraytype.lower() if arraytype != "numpy": raise ValueError("invalid array type") -def get_arraytype (): - """pygame.surfarray.get_arraytype (): return str + +def get_arraytype(): + """pygame.surfarray.get_arraytype(): return str DEPRECATED - only numpy arrays are now supported. """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) return "numpy" -def get_arraytypes (): - """pygame.surfarray.get_arraytypes (): return tuple + +def get_arraytypes(): + """pygame.surfarray.get_arraytypes(): return tuple DEPRECATED - only numpy arrays are now supported. """ + warnings.warn( + DeprecationWarning( + "only numpy arrays are now supported, " + "this function will be removed in a " + "future version of the module" + ) + ) return ("numpy",) - diff --git a/venv/Lib/site-packages/pygame/surflock.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/surflock.cp37-win_amd64.pyd deleted file mode 100644 index 7295389a229697dded2914a8a513c07b1174460a..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/surflock.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/sysfont.py b/venv/Lib/site-packages/pygame/sysfont.py index b3c74431fde1f9559319cf9d015b27ea7d9805e5..cc79a2e0b4845a243edc8082a55c3118e7a70251 100644 --- a/venv/Lib/site-packages/pygame/sysfont.py +++ b/venv/Lib/site-packages/pygame/sysfont.py @@ -1,4 +1,3 @@ -# coding: ascii # pygame - Python Game Library # Copyright (C) 2000-2003 Pete Shinners # @@ -22,38 +21,28 @@ import os import sys -from pygame.compat import xrange_, PY_MAJOR_VERSION +import warnings from os.path import basename, dirname, exists, join, splitext -import xml.etree.ElementTree as ET +from pygame.font import Font -OpenType_extensions = frozenset(('.ttf', '.ttc', '.otf')) +if sys.platform != "emscripten": + if os.name == "nt": + import winreg as _winreg + import subprocess + + +OpenType_extensions = frozenset((".ttf", ".ttc", ".otf")) Sysfonts = {} Sysalias = {} -# Python 3 compatibility -if PY_MAJOR_VERSION >= 3: - def toascii(raw): - """convert bytes to ASCII-only string""" - return raw.decode('ascii', 'ignore') - if os.name == 'nt': - import winreg as _winreg - else: - import subprocess -else: - def toascii(raw): - """return ASCII characters of a given unicode or 8-bit string""" - return raw.decode('ascii', 'ignore') - if os.name == 'nt': - import _winreg - else: - import subprocess +is_init = False def _simplename(name): """create simple version of the font name""" # return alphanumeric characters of a string (converted to lowercase) - return ''.join(c.lower() for c in name if c.isalnum()) + return "".join(c.lower() for c in name if c.isalnum()) def _addfont(name, bold, italic, font, fontdict): @@ -66,132 +55,149 @@ def _addfont(name, bold, italic, font, fontdict): def initsysfonts_win32(): """initialize fonts dictionary on Windows""" - fontdir = join(os.environ.get('WINDIR', 'C:\\Windows'), 'Fonts') - - TrueType_suffix = '(TrueType)' - mods = ('demibold', 'narrow', 'light', 'unicode', 'bt', 'mt') - + fontdir = join(os.environ.get("WINDIR", "C:\\Windows"), "Fonts") fonts = {} # add fonts entered in the registry + microsoft_font_dirs = [ + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts", + ] - # find valid registry keys containing font information. - # http://docs.python.org/lib/module-sys.html - # 0 (VER_PLATFORM_WIN32s) Win32s on Windows 3.1 - # 1 (VER_PLATFORM_WIN32_WINDOWS) Windows 95/98/ME - # 2 (VER_PLATFORM_WIN32_NT) Windows NT/2000/XP - # 3 (VER_PLATFORM_WIN32_CE) Windows CE - if sys.getwindowsversion()[0] == 1: - key_name = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts" - else: - key_name = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" - key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) - - for i in xrange_(_winreg.QueryInfoKey(key)[1]): - try: - # name is the font's name e.g. Times New Roman (TrueType) - # font is the font's filename e.g. times.ttf - name, font = _winreg.EnumValue(key, i)[0:2] - except EnvironmentError: - break - - # try to handle windows unicode strings for file names with - # international characters - if PY_MAJOR_VERSION < 3: - # here are two documents with some information about it: - # http://www.python.org/peps/pep-0277.html - # https://www.microsoft.com/technet/archive/interopmigration/linux/mvc/lintowin.mspx#ECAA + for domain in [_winreg.HKEY_LOCAL_MACHINE, _winreg.HKEY_CURRENT_USER]: + for font_dir in microsoft_font_dirs: try: - font = str(font) - except UnicodeEncodeError: - # MBCS is the windows encoding for unicode file names. + key = _winreg.OpenKey(domain, font_dir) + except FileNotFoundError: + continue + + for i in range(_winreg.QueryInfoKey(key)[1]): try: - font = font.encode('MBCS') - except: - # no success with str or MBCS encoding... skip this font. + # name is the font's name e.g. Times New Roman (TrueType) + # font is the font's filename e.g. times.ttf + name, font, _ = _winreg.EnumValue(key, i) + except OSError: + break + + if splitext(font)[1].lower() not in OpenType_extensions: continue + if not dirname(font): + font = join(fontdir, font) - if splitext(font)[1].lower() not in OpenType_extensions: - continue - if not dirname(font): - font = join(fontdir, font) + # Some are named A & B, both names should be processed separately + # Ex: the main Cambria file is marked as "Cambria & Cambria Math" + for name in name.split("&"): + _parse_font_entry_win(name, font, fonts) - if name.endswith(TrueType_suffix): - name = name.rstrip(TrueType_suffix).rstrip() - name = name.lower().split() + return fonts - bold = italic = 0 - for m in mods: - if m in name: - name.remove(m) - if 'bold' in name: - name.remove('bold') - bold = 1 - if 'italic' in name: - name.remove('italic') - italic = 1 - name = ''.join(name) - name = _simplename(name) +def _parse_font_entry_win(name, font, fonts): + """ + Parse out a simpler name and the font style from the initial file name. - _addfont(name, bold, italic, font, fonts) + :param name: The font name + :param font: The font file path + :param fonts: The pygame font dictionary + """ + true_type_suffix = "(TrueType)" + mods = ("demibold", "narrow", "light", "unicode", "bt", "mt") + if name.endswith(true_type_suffix): + name = name.rstrip(true_type_suffix).rstrip() + name = name.lower().split() + bold = italic = False + for mod in mods: + if mod in name: + name.remove(mod) + if "bold" in name: + name.remove("bold") + bold = True + if "italic" in name: + name.remove("italic") + italic = True + name = "".join(name) + name = _simplename(name) + + _addfont(name, bold, italic, font, fonts) + + +def _parse_font_entry_darwin(name, filepath, fonts): + """ + Parses a font entry for macOS - return fonts + :param name: The filepath without extensions or directories + :param filepath: The full path to the font + :param fonts: The pygame font dictionary to add the parsed font data to. + """ + name = _simplename(name) + + mods = ("regular",) + + for mod in mods: + if mod in name: + name = name.replace(mod, "") + + bold = italic = False + if "bold" in name: + name = name.replace("bold", "") + bold = True + if "italic" in name: + name = name.replace("italic", "") + italic = True + + _addfont(name, bold, italic, filepath, fonts) -def _add_font_paths(sub_elements, fonts): - """ Gets each element, checks its tag content, - if wanted fetches the next value in the iterable - """ - font_name = font_path = None - for tag in sub_elements: - if tag.text == "_name": - font_name = next(sub_elements).text - if splitext(font_name)[1] not in OpenType_extensions: - break - bold = "bold" in font_name - italic = "italic" in font_name - if tag.text == "path" and font_name is not None: - font_path = next(sub_elements).text - _addfont(_simplename(font_name),bold,italic,font_path,fonts) - break +def _font_finder_darwin(): + locations = [ + "/Library/Fonts", + "/Network/Library/Fonts", + "/System/Library/Fonts", + "/System/Library/Fonts/Supplemental", + ] + + username = os.getenv("USER") + if username: + locations.append(f"/Users/{username}/Library/Fonts") + + strange_root = "/System/Library/Assets/com_apple_MobileAsset_Font3" + if exists(strange_root): + strange_locations = os.listdir(strange_root) + for loc in strange_locations: + locations.append(f"{strange_root}/{loc}/AssetData") -def _system_profiler_darwin(): fonts = {} - flout, flerr = subprocess.Popen( - ' '.join(['system_profiler', '-xml','SPFontsDataType']), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=True - ).communicate() - for font_node in ET.fromstring(flout).iterfind('./array/dict/array/dict'): - _add_font_paths(font_node.iter("*"), fonts) + for location in locations: + if not exists(location): + continue - return fonts + files = os.listdir(location) + for file in files: + name, extension = splitext(file) + if extension in OpenType_extensions: + _parse_font_entry_darwin(name, join(location, file), fonts) + return fonts def initsysfonts_darwin(): - """ Read the fonts on MacOS, and OS X. - """ - # if the X11 binary exists... try and use that. - # Not likely to be there on pre 10.4.x ... or MacOS 10.10+ - if exists('/usr/X11/bin/fc-list'): - fonts = initsysfonts_unix('/usr/X11/bin/fc-list') - # This fc-list path will work with the X11 from the OS X 10.3 installation - # disc - elif exists('/usr/X11R6/bin/fc-list'): - fonts = initsysfonts_unix('/usr/X11R6/bin/fc-list') - elif exists('/usr/sbin/system_profiler'): - try: - fonts = _system_profiler_darwin() - except: - fonts = {} - else: - fonts = {} + """Read the fonts on MacOS, and OS X.""" + # fc-list is not likely to be there on pre 10.4.x, or MacOS 10.10+ + fonts = {} + + fclist_locations = [ + "/usr/X11/bin/fc-list", # apple x11 + "/usr/X11R6/bin/fc-list", # apple x11 + ] + for bin_location in fclist_locations: + if exists(bin_location): + fonts = initsysfonts_unix(bin_location) + break + + if len(fonts) == 0: + fonts = _font_finder_darwin() return fonts @@ -201,56 +207,127 @@ def initsysfonts_unix(path="fc-list"): """use the fc-list from fontconfig to get a list of fonts""" fonts = {} - try: - # note, we capture stderr so if fc-list isn't there to stop stderr - # printing. - flout, flerr = subprocess.Popen('%s : file family style' % path, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - close_fds=True).communicate() - except Exception: + if sys.platform == "emscripten": return fonts - entries = toascii(flout) try: - for line in entries.split('\n'): + proc = subprocess.run( + [path, ":", "file", "family", "style"], + stdout=subprocess.PIPE, # capture stdout + stderr=subprocess.PIPE, # capture stderr + check=True, # so that errors raise python exception which is handled below + timeout=1, # so that we don't hang the program waiting + ) + + except FileNotFoundError: + warnings.warn( + f"'{path}' is missing, system fonts cannot be loaded on your platform" + ) + + except subprocess.TimeoutExpired: + warnings.warn( + f"Process running '{path}' timed-out! System fonts cannot be loaded on " + "your platform" + ) + + except subprocess.CalledProcessError as e: + warnings.warn( + f"'{path}' failed with error code {e.returncode}! System fonts cannot be " + f"loaded on your platform. Error log is:\n{e.stderr}" + ) + else: + for entry in proc.stdout.decode("ascii", "ignore").splitlines(): try: - filename, family, style = line.split(':', 2) - if splitext(filename)[1].lower() in OpenType_extensions: - bold = 'Bold' in style - italic = 'Italic' in style - oblique = 'Oblique' in style - for name in family.strip().split(','): - if name: - break - else: - name = splitext(basename(filename))[0] - - _addfont( - _simplename(name), bold, italic or oblique, filename, fonts) - - except Exception: + _parse_font_entry_unix(entry, fonts) + except ValueError: # try the next one. pass - except Exception: - pass - return fonts +def _parse_font_entry_unix(entry, fonts): + """ + Parses an entry in the unix font data to add to the pygame font + dictionary. + + :param entry: A entry from the unix font list. + :param fonts: The pygame font dictionary to add the parsed font data to. + + """ + filename, family, style = entry.split(":", 2) + if splitext(filename)[1].lower() in OpenType_extensions: + bold = "Bold" in style + italic = "Italic" in style + oblique = "Oblique" in style + for name in family.strip().split(","): + if name: + break + else: + name = splitext(basename(filename))[0] + + _addfont(_simplename(name), bold, italic or oblique, filename, fonts) + + def create_aliases(): - """map common fonts that are absent from the system to similar fonts that are installed in the system""" + """Map common fonts that are absent from the system to similar fonts + that are installed in the system + """ alias_groups = ( - ('monospace', 'misc-fixed', 'courier', 'couriernew', 'console', - 'fixed', 'mono', 'freemono', 'bitstreamverasansmono', - 'verasansmono', 'monotype', 'lucidaconsole'), - ('sans', 'arial', 'helvetica', 'swiss', 'freesans', - 'bitstreamverasans', 'verasans', 'verdana', 'tahoma'), - ('serif', 'times', 'freeserif', 'bitstreamveraserif', 'roman', - 'timesroman', 'timesnewroman', 'dutch', 'veraserif', - 'georgia'), - ('wingdings', 'wingbats'), + ( + "monospace", + "misc-fixed", + "courier", + "couriernew", + "console", + "fixed", + "mono", + "freemono", + "bitstreamverasansmono", + "verasansmono", + "monotype", + "lucidaconsole", + "consolas", + "dejavusansmono", + "liberationmono", + ), + ( + "sans", + "arial", + "helvetica", + "swiss", + "freesans", + "bitstreamverasans", + "verasans", + "verdana", + "tahoma", + "calibri", + "gillsans", + "segoeui", + "trebuchetms", + "ubuntu", + "dejavusans", + "liberationsans", + ), + ( + "serif", + "times", + "freeserif", + "bitstreamveraserif", + "roman", + "timesroman", + "timesnewroman", + "dutch", + "veraserif", + "georgia", + "cambria", + "constantia", + "dejavuserif", + "liberationserif", + ), + ("wingdings", "wingbats"), + ("comicsansms", "comicsans"), ) for alias_set in alias_groups: for name in alias_set: @@ -264,76 +341,99 @@ def create_aliases(): Sysalias[name] = found -# initialize it all, called once def initsysfonts(): - if sys.platform == 'win32': + """ + Initialise the sysfont module, called once. Locates the installed fonts + and creates some aliases for common font categories. + + Has different initialisation functions for different platforms. + """ + global is_init + if is_init: + # no need to re-init + return + + if sys.platform == "win32": fonts = initsysfonts_win32() - elif sys.platform == 'darwin': + elif sys.platform == "darwin": fonts = initsysfonts_darwin() else: fonts = initsysfonts_unix() + Sysfonts.update(fonts) create_aliases() - if not Sysfonts: # dummy so we don't try to reinit - Sysfonts[None] = None + is_init = True -# pygame.font specific declarations def font_constructor(fontpath, size, bold, italic): - import pygame.font + """ + pygame.font specific declarations - font = pygame.font.Font(fontpath, size) + :param fontpath: path to a font. + :param size: size of a font. + :param bold: bold style, True or False. + :param italic: italic style, True or False. + + :return: A font.Font object. + """ + + font = Font(fontpath, size) if bold: - font.set_bold(1) + font.set_bold(True) if italic: - font.set_italic(1) + font.set_italic(True) return font # the exported functions + def SysFont(name, size, bold=False, italic=False, constructor=None): """pygame.font.SysFont(name, size, bold=False, italic=False, constructor=None) -> Font - create a pygame Font from system font resources - - This will search the system fonts for the given font - name. You can also enable bold or italic styles, and - the appropriate system font will be selected if available. - - This will always return a valid Font object, and will - fallback on the builtin pygame font if the given font - is not found. - - Name can also be a comma separated list of names, in - which case set of names will be searched in order. Pygame - uses a small set of common font aliases, if the specific - font you ask for is not available, a reasonable alternative - may be used. - - if optional contructor is provided, it must be a function with - signature constructor(fontpath, size, bold, italic) which returns - a Font instance. If None, a pygame.font.Font object is created. + Create a pygame Font from system font resources. + + This will search the system fonts for the given font + name. You can also enable bold or italic styles, and + the appropriate system font will be selected if available. + + This will always return a valid Font object, and will + fallback on the builtin pygame font if the given font + is not found. + + Name can also be an iterable of font names, a string of + comma-separated font names, or a bytes of comma-separated + font names, in which case the set of names will be searched + in order. Pygame uses a small set of common font aliases. If the + specific font you ask for is not available, a reasonable + alternative may be used. + + If optional constructor is provided, it must be a function with + signature constructor(fontpath, size, bold, italic) which returns + a Font instance. If None, a pygame.font.Font object is created. """ if constructor is None: constructor = font_constructor - if not Sysfonts: - initsysfonts() + initsysfonts() gotbold = gotitalic = False fontname = None if name: - allnames = name - for name in allnames.split(','): - name = _simplename(name) - styles = Sysfonts.get(name) + if isinstance(name, (str, bytes)): + name = name.split(b"," if isinstance(name, bytes) else ",") + for single_name in name: + if isinstance(single_name, bytes): + single_name = single_name.decode() + + single_name = _simplename(single_name) + styles = Sysfonts.get(single_name) if not styles: - styles = Sysalias.get(name) + styles = Sysalias.get(single_name) if styles: plainname = styles.get((False, False)) fontname = styles.get((bold, italic)) - if not fontname and not plainname: + if not (fontname or plainname): # Neither requested style, nor plain font exists, so # return a font with the name requested, but an # arbitrary style. @@ -364,39 +464,43 @@ def SysFont(name, size, bold=False, italic=False, constructor=None): def get_fonts(): """pygame.font.get_fonts() -> list - get a list of system font names + get a list of system font names - Returns the list of all found system fonts. Note that - the names of the fonts will be all lowercase with spaces - removed. This is how pygame internally stores the font - names for matching. + Returns the list of all found system fonts. Note that + the names of the fonts will be all lowercase with spaces + removed. This is how pygame internally stores the font + names for matching. """ - if not Sysfonts: - initsysfonts() + initsysfonts() return list(Sysfonts) -def match_font(name, bold=0, italic=0): +def match_font(name, bold=False, italic=False): """pygame.font.match_font(name, bold=0, italic=0) -> name - find the filename for the named system font + find the filename for the named system font - This performs the same font search as the SysFont() - function, only it returns the path to the TTF file - that would be loaded. The font name can be a comma - separated list of font names to try. + This performs the same font search as the SysFont() + function, only it returns the path to the TTF file + that would be loaded. The font name can also be an + iterable of font names or a string/bytes of comma-separated + font names to try. - If no match is found, None is returned. + If no match is found, None is returned. """ - if not Sysfonts: - initsysfonts() + initsysfonts() fontname = None - allnames = name - for name in allnames.split(','): - name = _simplename(name) - styles = Sysfonts.get(name) + if isinstance(name, (str, bytes)): + name = name.split(b"," if isinstance(name, bytes) else ",") + + for single_name in name: + if isinstance(single_name, bytes): + single_name = single_name.decode() + + single_name = _simplename(single_name) + styles = Sysfonts.get(single_name) if not styles: - styles = Sysalias.get(name) + styles = Sysalias.get(single_name) if styles: while not fontname: fontname = styles.get((bold, italic)) @@ -406,6 +510,8 @@ def match_font(name, bold=0, italic=0): bold = 0 elif not fontname: fontname = list(styles.values())[0] + if fontname: break + return fontname diff --git a/venv/Lib/site-packages/pygame/tests/__init__.py b/venv/Lib/site-packages/pygame/tests/__init__.py index 48cfdcead7be1dafbbcbac688621444493bf6544..dd26586975afb03c9ad6b693dca86b0af8d439b6 100644 --- a/venv/Lib/site-packages/pygame/tests/__init__.py +++ b/venv/Lib/site-packages/pygame/tests/__init__.py @@ -20,15 +20,15 @@ directly. """ -if __name__ == 'pygame.tests': +if __name__ == "pygame.tests": from pygame.tests.test_utils.run_tests import run -elif __name__ == '__main__': +elif __name__ == "__main__": import os import sys + pkg_dir = os.path.split(os.path.abspath(__file__))[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) diff --git a/venv/Lib/site-packages/pygame/tests/__main__.py b/venv/Lib/site-packages/pygame/tests/__main__.py index 51b2e1328c804ccc89bc915e190d41dfc6e39913..9b4ceddea280b6673e2a1d13c84dd67201985845 100644 --- a/venv/Lib/site-packages/pygame/tests/__main__.py +++ b/venv/Lib/site-packages/pygame/tests/__main__.py @@ -14,16 +14,16 @@ run_tests.py in the main distribution directory is an alternative to test.go import sys -if __name__ == '__main__': +if __name__ == "__main__": import os + pkg_dir = os.path.split(os.path.abspath(__file__))[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") if is_pygame_pkg: from pygame.tests.test_utils.run_tests import run_and_exit @@ -37,97 +37,107 @@ if is_pygame_pkg: else: test_pkg_name = "test" program_name = sys.argv[0] -if program_name == '-c': - program_name = 'python -c "import %s.go"' % test_pkg_name +if program_name == "-c": + program_name = f'python -c "import {test_pkg_name}.go"' ########################################################################### # Set additional command line options # # Defined in test_runner.py as it shares options, added to here -opt_parser.set_usage(""" +opt_parser.set_usage( + f""" -Runs all or some of the %(pkg)s.xxxx_test tests. +Runs all or some of the {test_pkg_name}.xxxx_test tests. -$ %(exec)s sprite threads -sd +$ {program_name} sprite threads -sd Runs the sprite and threads module tests isolated in subprocesses, dumping all failing tests info in the form of a dict. -""" % {'pkg': test_pkg_name, 'exec': program_name}) - -opt_parser.add_option ( - "-d", "--dump", action = 'store_true', - help = "dump results as dict ready to eval" ) +""" +) -opt_parser.add_option ( - "-F", "--file", - help = "dump results to a file" ) +opt_parser.add_option( + "-d", "--dump", action="store_true", help="dump results as dict ready to eval" +) -opt_parser.add_option ( - "-m", "--multi_thread", metavar = 'THREADS', type = 'int', - help = "run subprocessed tests in x THREADS" ) +opt_parser.add_option("-F", "--file", help="dump results to a file") -opt_parser.add_option ( - "-t", "--time_out", metavar = 'SECONDS', type = 'int', - help = "kill stalled subprocessed tests after SECONDS" ) +opt_parser.add_option( + "-m", + "--multi_thread", + metavar="THREADS", + type="int", + help="run subprocessed tests in x THREADS", +) -opt_parser.add_option ( - "-f", "--fake", metavar = "DIR", - help = "run fake tests in run_tests__tests/$DIR" ) +opt_parser.add_option( + "-t", + "--time_out", + metavar="SECONDS", + type="int", + help="kill stalled subprocessed tests after SECONDS", +) -opt_parser.add_option ( - "-p", "--python", metavar = "PYTHON", - help = "path to python excutable to run subproccesed tests\n" - "default (sys.executable): %s" % sys.executable) +opt_parser.add_option( + "-f", "--fake", metavar="DIR", help="run fake tests in run_tests__tests/$DIR" +) -opt_parser.add_option ( - "-I", "--interactive", action = 'store_true', - help = "include tests requiring user input") +opt_parser.add_option( + "-p", + "--python", + metavar="PYTHON", + help="path to python executable to run subproccesed tests\n" + "default (sys.executable): %s" % sys.executable, +) opt_parser.add_option( - "-S", "--seed", type = 'int', - help = "Randomisation seed" + "-I", + "--interactive", + action="store_true", + help="include tests requiring user input", ) +opt_parser.add_option("-S", "--seed", type="int", help="Randomisation seed") + ########################################################################### -# Set run() keyword arguements according to command line arguemnts. +# Set run() keyword arguments according to command line arguments. # args will be the test module list, passed as positional argumemts. options, args = opt_parser.parse_args() - kwds = {} if options.incomplete: - kwds['incomplete'] = True + kwds["incomplete"] = True if options.usesubprocess: - kwds['usesubprocess'] = True + kwds["usesubprocess"] = True else: - kwds['usesubprocess'] = False + kwds["usesubprocess"] = False if options.dump: - kwds['dump'] = True + kwds["dump"] = True if options.file: - kwds['file'] = options.file + kwds["file"] = options.file if options.exclude: - kwds['exclude'] = options.exclude + kwds["exclude"] = options.exclude if options.unbuffered: - kwds['unbuffered'] = True + kwds["unbuffered"] = True if options.randomize: - kwds['randomize'] = True + kwds["randomize"] = True if options.seed is not None: - kwds['seed'] = options.seed + kwds["seed"] = options.seed if options.multi_thread is not None: - kwds['multi_thread'] = options.multi_thread + kwds["multi_thread"] = options.multi_thread if options.time_out is not None: - kwds['time_out'] = options.time_out + kwds["time_out"] = options.time_out if options.fake: - kwds['fake'] = options.fake + kwds["fake"] = options.fake if options.python: - kwds['python'] = options.python + kwds["python"] = options.python if options.interactive: - kwds['interactive'] = True + kwds["interactive"] = True +kwds["verbosity"] = options.verbosity if options.verbosity is not None else 1 + ########################################################################### # Run the test suite. run_and_exit(*args, **kwds) - - diff --git a/venv/Lib/site-packages/pygame/tests/base_test.py b/venv/Lib/site-packages/pygame/tests/base_test.py index 86c9ccb8e9a3179210080201827dad7c3a7752e3..b11d2d680c192da0e1104d83ba0d49e8d0f3d29a 100644 --- a/venv/Lib/site-packages/pygame/tests/base_test.py +++ b/venv/Lib/site-packages/pygame/tests/base_test.py @@ -1,10 +1,9 @@ -# -*- coding: utf8 -*- - import sys import unittest import platform -IS_PYPY = 'PyPy' == platform.python_implementation() + +IS_PYPY = "PyPy" == platform.python_implementation() try: from pygame.tests.test_utils import arrinter @@ -13,34 +12,19 @@ except NameError: import pygame -init_called = quit_called = 0 -def __PYGAMEinit__(): #called automatically by pygame.init() - global init_called - init_called = init_called + 1 - pygame.register_quit(pygame_quit) -def pygame_quit(): - global quit_called - quit_called = quit_called + 1 +quit_count = 0 -quit_hook_ran = 0 def quit_hook(): - global quit_hook_ran - quit_hook_ran = 1 + global quit_count + quit_count += 1 class BaseModuleTest(unittest.TestCase): - def tearDown(self): # Clean up after each test method. pygame.quit() - def testAutoInit(self): - pygame.init() - pygame.quit() - self.assertEqual(init_called, 1) - self.assertEqual(quit_called, 1) - def test_get_sdl_byteorder(self): """Ensure the SDL byte order is valid""" byte_order = pygame.get_sdl_byteorder() @@ -52,7 +36,7 @@ class BaseModuleTest(unittest.TestCase): """Ensure the SDL version is valid""" self.assertEqual(len(pygame.get_sdl_version()), 3) - class ExporterBase(object): + class ExporterBase: def __init__(self, shape, typechar, itemsize): import ctypes @@ -71,49 +55,52 @@ class BaseModuleTest(unittest.TestCase): self.strides = tuple(strides) self.data = ctypes.addressof(self.parent), False if self.itemsize == 1: - byteorder = '|' - elif sys.byteorder == 'big': - byteorder = '>' + byteorder = "|" + elif sys.byteorder == "big": + byteorder = ">" else: - byteorder = '<' + byteorder = "<" self.typestr = byteorder + typechar + str(self.itemsize) def assertSame(self, proxy, obj): self.assertEqual(proxy.length, obj.size) iface = proxy.__array_interface__ - self.assertEqual(iface['typestr'], obj.typestr) - self.assertEqual(iface['shape'], obj.shape) - self.assertEqual(iface['strides'], obj.strides) - self.assertEqual(iface['data'], obj.data) + self.assertEqual(iface["typestr"], obj.typestr) + self.assertEqual(iface["shape"], obj.shape) + self.assertEqual(iface["strides"], obj.strides) + self.assertEqual(iface["data"], obj.data) def test_PgObject_GetBuffer_array_interface(self): from pygame.bufferproxy import BufferProxy class Exporter(self.ExporterBase): def get__array_interface__(self): - return {'version': 3, - 'typestr': self.typestr, - 'shape': self.shape, - 'strides': self.strides, - 'data': self.data} + return { + "version": 3, + "typestr": self.typestr, + "shape": self.shape, + "strides": self.strides, + "data": self.data, + } + __array_interface__ = property(get__array_interface__) # Should be ignored by PgObject_GetBuffer __array_struct__ = property(lambda self: None) _shape = [2, 3, 5, 7, 11] # Some prime numbers for ndim in range(1, len(_shape)): - o = Exporter(_shape[0:ndim], 'i', 2) + o = Exporter(_shape[0:ndim], "i", 2) v = BufferProxy(o) self.assertSame(v, o) ndim = 2 shape = _shape[0:ndim] - for typechar in ('i', 'u'): + for typechar in ("i", "u"): for itemsize in (1, 2, 4, 8): o = Exporter(shape, typechar, itemsize) v = BufferProxy(o) self.assertSame(v, o) for itemsize in (4, 8): - o = Exporter(shape, 'f', itemsize) + o = Exporter(shape, "f", itemsize) v = BufferProxy(o) self.assertSame(v, o) @@ -135,6 +122,7 @@ class BaseModuleTest(unittest.TestCase): class WRDict(dict): """Weak referenceable dict""" + pass class Exporter2(Exporter): @@ -142,16 +130,19 @@ class BaseModuleTest(unittest.TestCase): self.d = WRDict(Exporter.get__array_interface__(self)) self.dict_ref = weakref.ref(self.d) return self.d + __array_interface__ = property(get__array_interface__2) + def free_dict(self): self.d = None + def is_dict_alive(self): try: return self.dict_ref() is not None except AttributeError: raise NoDictError("__array_interface__ is unread") - o = Exporter2((2, 4), 'u', 4) + o = Exporter2((2, 4), "u", 4) v = BufferProxy(o) self.assertRaises(NoDictError, o.is_dict_alive) length = v.length @@ -165,29 +156,30 @@ class BaseModuleTest(unittest.TestCase): class Exporter(self.ExporterBase): def __init__(self, shape, typechar, itemsize): - super(Exporter, self).__init__(shape, typechar, itemsize) + super().__init__(shape, typechar, itemsize) self.view = BufferProxy(self.__dict__) def get__array_struct__(self): return self.view.__array_struct__ + __array_struct__ = property(get__array_struct__) # Should not cause PgObject_GetBuffer to fail __array_interface__ = property(lambda self: None) _shape = [2, 3, 5, 7, 11] # Some prime numbers for ndim in range(1, len(_shape)): - o = Exporter(_shape[0:ndim], 'i', 2) + o = Exporter(_shape[0:ndim], "i", 2) v = BufferProxy(o) self.assertSame(v, o) ndim = 2 shape = _shape[0:ndim] - for typechar in ('i', 'u'): + for typechar in ("i", "u"): for itemsize in (1, 2, 4, 8): o = Exporter(shape, typechar, itemsize) v = BufferProxy(o) self.assertSame(v, o) for itemsize in (4, 8): - o = Exporter(shape, 'f', itemsize) + o = Exporter(shape, "f", itemsize) v = BufferProxy(o) self.assertSame(v, o) @@ -217,26 +209,48 @@ class BaseModuleTest(unittest.TestCase): self.assertEqual(imp.strides, exp.strides) self.assertTrue(imp.suboffsets is None) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + @unittest.skipIf(IS_PYPY, "pypy no likey") def test_newbuf(self): from pygame.bufferproxy import BufferProxy Exporter = self.buftools.Exporter _shape = [2, 3, 5, 7, 11] # Some prime numbers for ndim in range(1, len(_shape)): - o = Exporter(_shape[0:ndim], '=h') + o = Exporter(_shape[0:ndim], "=h") v = BufferProxy(o) self.NEWBUF_assertSame(v, o) ndim = 2 shape = _shape[0:ndim] - for format in ['b', 'B', '=h', '=H', '=i', '=I', '=q', '=Q', 'f', 'd', - '1h', '=1h', 'x', '1x', '2x', '3x', '4x', '5x', '6x', - '7x', '8x', '9x']: + for format in [ + "b", + "B", + "=h", + "=H", + "=i", + "=I", + "=q", + "=Q", + "f", + "d", + "1h", + "=1h", + "x", + "1x", + "2x", + "3x", + "4x", + "5x", + "6x", + "7x", + "8x", + "9x", + ]: o = Exporter(shape, format) v = BufferProxy(o) self.NEWBUF_assertSame(v, o) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_bad_format(self): from pygame.bufferproxy import BufferProxy from pygame.newbuffer import BufferMixin @@ -247,23 +261,37 @@ class BaseModuleTest(unittest.TestCase): Importer = buftools.Importer PyBUF_FORMAT = buftools.PyBUF_FORMAT - for format in ['', '=', '1', ' ', '2h', '=2h', - '0x', '11x', '=!', 'h ', ' h', 'hh', '?']: + for format in [ + "", + "=", + "1", + " ", + "2h", + "=2h", + "0x", + "11x", + "=!", + "h ", + " h", + "hh", + "?", + ]: exp = Exporter((1,), format, itemsize=2) b = BufferProxy(exp) self.assertRaises(ValueError, Importer, b, PyBUF_FORMAT) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + @unittest.skipIf(IS_PYPY, "fails on pypy") def test_PgDict_AsBuffer_PyBUF_flags(self): from pygame.bufferproxy import BufferProxy is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN - fsys, frev = ('<', '>') if is_lil_endian else ('>', '<') + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") buftools = self.buftools Importer = buftools.Importer - a = BufferProxy({'typestr': '|u4', - 'shape': (10, 2), - 'data': (9, False)}) # 9? No data accesses. + a = BufferProxy( + {"typestr": "|u4", "shape": (10, 2), "data": (9, False)} + ) # 9? No data accesses. b = Importer(a, buftools.PyBUF_SIMPLE) self.assertEqual(b.ndim, 0) self.assertTrue(b.format is None) @@ -294,10 +322,14 @@ class BaseModuleTest(unittest.TestCase): self.assertTrue(b.suboffsets is None) self.assertFalse(b.readonly) self.assertEqual(b.buf, 9) - a = BufferProxy({'typestr': fsys + 'i2', - 'shape': (5, 10), - 'strides': (24, 2), - 'data': (42, False)}) # 42? No data accesses. + a = BufferProxy( + { + "typestr": fsys + "i2", + "shape": (5, 10), + "strides": (24, 2), + "data": (42, False), + } + ) # 42? No data accesses. b = Importer(a, buftools.PyBUF_STRIDES) self.assertEqual(b.ndim, 2) self.assertTrue(b.format is None) @@ -310,7 +342,7 @@ class BaseModuleTest(unittest.TestCase): self.assertEqual(b.buf, 42) b = Importer(a, buftools.PyBUF_FULL_RO) self.assertEqual(b.ndim, 2) - self.assertEqual(b.format, '=h') + self.assertEqual(b.format, "=h") self.assertEqual(b.len, 100) self.assertEqual(b.itemsize, 2) self.assertEqual(b.shape, (5, 10)) @@ -320,29 +352,27 @@ class BaseModuleTest(unittest.TestCase): self.assertEqual(b.buf, 42) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) - a = BufferProxy({'typestr': frev + 'i2', - 'shape': (3, 5, 10), - 'strides': (120, 24, 2), - 'data': (1000000, True)}) # 1000000? No data accesses. + a = BufferProxy( + { + "typestr": frev + "i2", + "shape": (3, 5, 10), + "strides": (120, 24, 2), + "data": (1000000, True), + } + ) # 1000000? No data accesses. b = Importer(a, buftools.PyBUF_FULL_RO) self.assertEqual(b.ndim, 3) - self.assertEqual(b.format, frev + 'h') + self.assertEqual(b.format, frev + "h") self.assertEqual(b.len, 300) self.assertEqual(b.itemsize, 2) self.assertEqual(b.shape, (3, 5, 10)) @@ -352,17 +382,18 @@ class BaseModuleTest(unittest.TestCase): self.assertEqual(b.buf, 1000000) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL) - @unittest.skipIf(IS_PYPY or (not pygame.HAVE_NEWBUF), 'newbuf with ctypes') + @unittest.skipIf(IS_PYPY or (not pygame.HAVE_NEWBUF), "newbuf with ctypes") def test_PgObject_AsBuffer_PyBUF_flags(self): from pygame.bufferproxy import BufferProxy import ctypes is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN - fsys, frev = ('<', '>') if is_lil_endian else ('>', '<') + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") buftools = self.buftools Importer = buftools.Importer - e = arrinter.Exporter((10, 2), typekind='f', - itemsize=ctypes.sizeof(ctypes.c_double)) + e = arrinter.Exporter( + (10, 2), typekind="f", itemsize=ctypes.sizeof(ctypes.c_double) + ) a = BufferProxy(e) b = Importer(a, buftools.PyBUF_SIMPLE) self.assertEqual(b.ndim, 0) @@ -394,8 +425,7 @@ class BaseModuleTest(unittest.TestCase): self.assertTrue(b.suboffsets is None) self.assertFalse(b.readonly) self.assertEqual(b.buf, e.data) - e = arrinter.Exporter((5, 10), typekind='i', itemsize=2, - strides=(24, 2)) + e = arrinter.Exporter((5, 10), typekind="i", itemsize=2, strides=(24, 2)) a = BufferProxy(e) b = Importer(a, buftools.PyBUF_STRIDES) self.assertEqual(b.ndim, e.nd) @@ -409,7 +439,7 @@ class BaseModuleTest(unittest.TestCase): self.assertEqual(b.buf, e.data) b = Importer(a, buftools.PyBUF_FULL_RO) self.assertEqual(b.ndim, e.nd) - self.assertEqual(b.format, '=h') + self.assertEqual(b.format, "=h") self.assertEqual(b.len, e.len) self.assertEqual(b.itemsize, e.itemsize) self.assertEqual(b.shape, e.shape) @@ -418,34 +448,30 @@ class BaseModuleTest(unittest.TestCase): self.assertFalse(b.readonly) self.assertEqual(b.buf, e.data) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_WRITABLE) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG) - e = arrinter.Exporter((3, 5, 10), typekind='i', itemsize=2, - strides=(120, 24, 2), - flags=arrinter.PAI_ALIGNED) + e = arrinter.Exporter( + (3, 5, 10), + typekind="i", + itemsize=2, + strides=(120, 24, 2), + flags=arrinter.PAI_ALIGNED, + ) a = BufferProxy(e) b = Importer(a, buftools.PyBUF_FULL_RO) self.assertEqual(b.ndim, e.nd) - self.assertEqual(b.format, frev + 'h') + self.assertEqual(b.format, frev + "h") self.assertEqual(b.len, e.len) self.assertEqual(b.itemsize, e.itemsize) self.assertEqual(b.shape, e.shape) @@ -460,24 +486,22 @@ class BaseModuleTest(unittest.TestCase): from pygame.bufferproxy import BufferProxy bp = BufferProxy(1) - self.assertRaises(ValueError, getattr, bp, 'length') + self.assertRaises(ValueError, getattr, bp, "length") def not_init_assertions(self): self.assertFalse(pygame.get_init(), "pygame shouldn't be initialized") - self.assertFalse(pygame.display.get_init(), - "display shouldn't be initialized") + self.assertFalse(pygame.display.get_init(), "display shouldn't be initialized") - if 'pygame.mixer' in sys.modules: - self.assertFalse(pygame.mixer.get_init(), - "mixer shouldn't be initialized") + if "pygame.mixer" in sys.modules: + self.assertFalse(pygame.mixer.get_init(), "mixer shouldn't be initialized") - if 'pygame.font' in sys.modules: - self.assertFalse(pygame.font.get_init(), - "init shouldn't be initialized") + if "pygame.font" in sys.modules: + self.assertFalse(pygame.font.get_init(), "init shouldn't be initialized") ## !!! TODO : Remove when scrap works for OS X import platform - if platform.system().startswith('Darwin'): + + if platform.system().startswith("Darwin"): return try: @@ -493,17 +517,17 @@ class BaseModuleTest(unittest.TestCase): self.assertTrue(pygame.get_init()) self.assertTrue(pygame.display.get_init()) - if 'pygame.mixer' in sys.modules: + if "pygame.mixer" in sys.modules: self.assertTrue(pygame.mixer.get_init()) - if 'pygame.font' in sys.modules: + if "pygame.font" in sys.modules: self.assertTrue(pygame.font.get_init()) def test_quit__and_init(self): # __doc__ (as of 2008-06-25) for pygame.base.quit: - # pygame.quit(): return None - # uninitialize all pygame modules + # pygame.quit(): return None + # uninitialize all pygame modules # Make sure everything is not init self.not_init_assertions() @@ -522,25 +546,24 @@ class BaseModuleTest(unittest.TestCase): def test_register_quit(self): """Ensure that a registered function is called on quit()""" - self.assertFalse(quit_hook_ran) + self.assertEqual(quit_count, 0) pygame.init() pygame.register_quit(quit_hook) pygame.quit() - self.assertTrue(quit_hook_ran) + self.assertEqual(quit_count, 1) def test_get_error(self): - # __doc__ (as of 2008-08-02) for pygame.base.get_error: - # pygame.get_error(): return errorstr - # get the current error message - # - # SDL maintains an internal error message. This message will usually - # be given to you when pygame.error is raised. You will rarely need to - # call this function. - # + # pygame.get_error(): return errorstr + # get the current error message + # + # SDL maintains an internal error message. This message will usually + # be given to you when pygame.error is raised. You will rarely need to + # call this function. + # # The first error could be all sorts of nonsense or empty. e = pygame.get_error() @@ -549,10 +572,7 @@ class BaseModuleTest(unittest.TestCase): pygame.set_error("") self.assertEqual(pygame.get_error(), "") - - def test_set_error(self): - # The first error could be all sorts of nonsense or empty. e = pygame.get_error() pygame.set_error("hi") @@ -561,53 +581,25 @@ class BaseModuleTest(unittest.TestCase): self.assertEqual(pygame.get_error(), "") def test_unicode_error(self): - if sys.version_info.major > 2: - pygame.set_error(u'你好') - self.assertEqual(u'你好', pygame.get_error()) - else: - # no unicode objects for now - pygame.set_error(u'你好') - encstr = u'你好'.encode('utf8') - self.assertEqual(encstr, pygame.get_error()) + pygame.set_error("你好") + self.assertEqual("你好", pygame.get_error()) def test_init(self): - - # __doc__ (as of 2008-08-02) for pygame.base.init: - - # pygame.init(): return (numpass, numfail) - # initialize all imported pygame modules - # - # Initialize all imported Pygame modules. No exceptions will be raised - # if a module fails, but the total number if successful and failed - # inits will be returned as a tuple. You can always initialize - # individual modules manually, but pygame.init is a convenient way to - # get everything started. The init() functions for individual modules - # will raise exceptions when they fail. - # - # You may want to initalise the different modules seperately to speed - # up your program or to not use things your game does not. - # - # It is safe to call this init() more than once: repeated calls will - # have no effect. This is true even if you have pygame.quit() all the - # modules. - # - - - - # Make sure everything is not init + """Ensures init() works properly.""" + # Make sure nothing initialized. self.not_init_assertions() - # Initiate it - pygame.init() + # display and joystick must init, at minimum + expected_min_passes = 2 - # Check - self.init_assertions() + # All modules should pass. + expected_fails = 0 - # Quit - pygame.quit() + passes, fails = pygame.init() - # All modules have quit - self.not_init_assertions() + self.init_assertions() + self.assertGreaterEqual(passes, expected_min_passes) + self.assertEqual(fails, expected_fails) def test_get_init(self): # Test if get_init() gets the init state. @@ -626,13 +618,6 @@ class BaseModuleTest(unittest.TestCase): self.assertFalse(pygame.get_init()) - def todo_test_segfault(self): - - # __doc__ (as of 2008-08-02) for pygame.base.segfault: - - # crash - - self.fail() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/blit_test.py b/venv/Lib/site-packages/pygame/tests/blit_test.py index f551a6844c5e3ab7ecef2b714552a88792e15ea9..407945db7c12e850d69b92d583a27c004658efbb 100644 --- a/venv/Lib/site-packages/pygame/tests/blit_test.py +++ b/venv/Lib/site-packages/pygame/tests/blit_test.py @@ -3,81 +3,75 @@ import unittest import pygame from pygame.locals import * -class BlitTest( unittest.TestCase ): - def test_SRCALPHA( self ): - """ SRCALPHA tests. - """ - #blend(s, 0, d) = d - s = pygame.Surface((1,1), SRCALPHA, 32) - s.fill((255, 255,255, 0)) - - d = pygame.Surface((1,1), SRCALPHA, 32) - d.fill((0, 0,255, 255)) - - s.blit(d, (0,0)) - self.assertEqual(s.get_at((0,0)), d.get_at((0,0)) ) - - #blend(s, 255, d) = s - s = pygame.Surface((1,1), SRCALPHA, 32) - s.fill((123, 0, 0, 255)) - s1 = pygame.Surface((1,1), SRCALPHA, 32) - s1.fill((123, 0, 0, 255)) - d = pygame.Surface((1,1), SRCALPHA, 32) - d.fill((10, 0,0, 0)) - s.blit(d, (0,0)) - self.assertEqual(s.get_at((0,0)), s1.get_at((0,0)) ) - #TODO: these should be true too. - #blend(0, sA, 0) = 0 - #blend(255, sA, 255) = 255 - #blend(s, sA, d) <= 255 +class BlitTest(unittest.TestCase): + def test_SRCALPHA(self): + """SRCALPHA tests.""" + # blend(s, 0, d) = d + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((255, 255, 255, 0)) - def test_BLEND( self ): - """ BLEND_ tests. - """ + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((0, 0, 255, 255)) - #test that it doesn't overflow, and that it is saturated. - s = pygame.Surface((1,1), SRCALPHA, 32) - s.fill((255, 255,255, 0)) + s.blit(d, (0, 0)) + self.assertEqual(s.get_at((0, 0)), d.get_at((0, 0))) - d = pygame.Surface((1,1), SRCALPHA, 32) - d.fill((0, 0,255, 255)) + # blend(s, 255, d) = s + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((123, 0, 0, 255)) + s1 = pygame.Surface((1, 1), SRCALPHA, 32) + s1.fill((123, 0, 0, 255)) + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((10, 0, 0, 0)) + s.blit(d, (0, 0)) + self.assertEqual(s.get_at((0, 0)), s1.get_at((0, 0))) - s.blit(d, (0,0), None, BLEND_ADD) + # TODO: these should be true too. + # blend(0, sA, 0) = 0 + # blend(255, sA, 255) = 255 + # blend(s, sA, d) <= 255 - #print "d %s" % (d.get_at((0,0)),) - #print s.get_at((0,0)) - #self.assertEqual(s.get_at((0,0))[2], 255 ) - #self.assertEqual(s.get_at((0,0))[3], 0 ) + def test_BLEND(self): + """BLEND_ tests.""" + # test that it doesn't overflow, and that it is saturated. + s = pygame.Surface((1, 1), SRCALPHA, 32) + s.fill((255, 255, 255, 0)) + d = pygame.Surface((1, 1), SRCALPHA, 32) + d.fill((0, 0, 255, 255)) - s.blit(d, (0,0), None, BLEND_RGBA_ADD) - #print s.get_at((0,0)) - self.assertEqual(s.get_at((0,0))[3], 255 ) + s.blit(d, (0, 0), None, BLEND_ADD) + # print("d %s" % (d.get_at((0,0)),)) + # print(s.get_at((0,0))) + # self.assertEqual(s.get_at((0,0))[2], 255 ) + # self.assertEqual(s.get_at((0,0))[3], 0 ) + + s.blit(d, (0, 0), None, BLEND_RGBA_ADD) + # print(s.get_at((0,0))) + self.assertEqual(s.get_at((0, 0))[3], 255) # test adding works. - s.fill((20, 255,255, 0)) - d.fill((10, 0,255, 255)) - s.blit(d, (0,0), None, BLEND_ADD) - self.assertEqual(s.get_at((0,0))[2], 255 ) + s.fill((20, 255, 255, 0)) + d.fill((10, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_ADD) + self.assertEqual(s.get_at((0, 0))[2], 255) # test subbing works. - s.fill((20, 255,255, 0)) - d.fill((10, 0,255, 255)) - s.blit(d, (0,0), None, BLEND_SUB) - self.assertEqual(s.get_at((0,0))[0], 10 ) + s.fill((20, 255, 255, 0)) + d.fill((10, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_SUB) + self.assertEqual(s.get_at((0, 0))[0], 10) # no overflow in sub blend. - s.fill((20, 255,255, 0)) - d.fill((30, 0,255, 255)) - s.blit(d, (0,0), None, BLEND_SUB) - self.assertEqual(s.get_at((0,0))[0], 0 ) - + s.fill((20, 255, 255, 0)) + d.fill((30, 0, 255, 255)) + s.blit(d, (0, 0), None, BLEND_SUB) + self.assertEqual(s.get_at((0, 0))[0], 0) def make_blit_list(self, num_surfs): - blit_list = [] for i in range(num_surfs): dest = (i * 10, 0) @@ -88,7 +82,6 @@ class BlitTest( unittest.TestCase ): return blit_list def test_blits(self): - NUM_SURFS = 255 PRINT_TIMING = 0 dst = pygame.Surface((NUM_SURFS * 10, 10), SRCALPHA, 32) @@ -100,19 +93,19 @@ class BlitTest( unittest.TestCase ): dst.blit(surface, dest) from time import time + t0 = time() results = blits(blit_list) t1 = time() if PRINT_TIMING: - print("python blits: %s" % (t1-t0)) + print(f"python blits: {t1 - t0}") dst.fill((230, 230, 230)) t0 = time() results = dst.blits(blit_list) t1 = time() if PRINT_TIMING: - print("Surface.blits :%s" % (t1-t0)) - + print(f"Surface.blits :{t1 - t0}") # check if we blit all the different colors in the correct spots. for i in range(NUM_SURFS): @@ -123,19 +116,17 @@ class BlitTest( unittest.TestCase ): self.assertEqual(len(results), NUM_SURFS) t0 = time() - results = dst.blits(blit_list, doreturn = 0) + results = dst.blits(blit_list, doreturn=0) t1 = time() if PRINT_TIMING: - print("Surface.blits doreturn=0: %s" % (t1-t0)) + print(f"Surface.blits doreturn=0: {t1 - t0}") self.assertEqual(results, None) - t0 = time() results = dst.blits(((surf, dest) for surf, dest in blit_list)) t1 = time() if PRINT_TIMING: - print("Surface.blits generator: %s" % (t1-t0)) - + print(f"Surface.blits generator: {t1 - t0}") def test_blits_not_sequence(self): dst = pygame.Surface((100, 10), SRCALPHA, 32) @@ -143,7 +134,9 @@ class BlitTest( unittest.TestCase ): def test_blits_wrong_length(self): dst = pygame.Surface((100, 10), SRCALPHA, 32) - self.assertRaises(ValueError, dst.blits, [pygame.Surface((10, 10), SRCALPHA, 32)]) + self.assertRaises( + ValueError, dst.blits, [pygame.Surface((10, 10), SRCALPHA, 32)] + ) def test_blits_bad_surf_args(self): dst = pygame.Surface((100, 10), SRCALPHA, 32) @@ -151,9 +144,10 @@ class BlitTest( unittest.TestCase ): def test_blits_bad_dest(self): dst = pygame.Surface((100, 10), SRCALPHA, 32) - self.assertRaises(TypeError, dst.blits, [(pygame.Surface((10, 10), SRCALPHA, 32), None)]) - + self.assertRaises( + TypeError, dst.blits, [(pygame.Surface((10, 10), SRCALPHA, 32), None)] + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/bufferproxy_test.py b/venv/Lib/site-packages/pygame/tests/bufferproxy_test.py index 7e1bcca3a278f5e1444e2fa404fa491bc81b844b..1282e35cbc1d1885b73ecadd44a6decf95e97e92 100644 --- a/venv/Lib/site-packages/pygame/tests/bufferproxy_test.py +++ b/venv/Lib/site-packages/pygame/tests/bufferproxy_test.py @@ -1,14 +1,12 @@ -import sys import re import weakref import gc import ctypes import unittest - import pygame from pygame.bufferproxy import BufferProxy -from pygame.compat import as_bytes + try: BufferError @@ -17,14 +15,15 @@ except NameError: class BufferProxyTest(unittest.TestCase): - view_keywords = {'shape': (5, 4, 3), - 'typestr': '|u1', - 'data': (0, True), - 'strides': (4, 20, 1)} + view_keywords = { + "shape": (5, 4, 3), + "typestr": "|u1", + "data": (0, True), + "strides": (4, 20, 1), + } def test_module_name(self): - self.assertEqual(pygame.bufferproxy.__name__, - "pygame.bufferproxy") + self.assertEqual(pygame.bufferproxy.__name__, "pygame.bufferproxy") def test_class_name(self): self.assertEqual(BufferProxy.__name__, "BufferProxy") @@ -34,27 +33,27 @@ class BufferProxyTest(unittest.TestCase): v = BufferProxy(kwds) d = pygame.get_array_interface(v) self.assertEqual(len(d), 5) - self.assertEqual(d['version'], 3) - self.assertEqual(d['shape'], kwds['shape']) - self.assertEqual(d['typestr'], kwds['typestr']) - self.assertEqual(d['data'], kwds['data']) - self.assertEqual(d['strides'], kwds['strides']) + self.assertEqual(d["version"], 3) + self.assertEqual(d["shape"], kwds["shape"]) + self.assertEqual(d["typestr"], kwds["typestr"]) + self.assertEqual(d["data"], kwds["data"]) + self.assertEqual(d["strides"], kwds["strides"]) def test___array_interface___property(self): kwds = self.view_keywords v = BufferProxy(kwds) d = v.__array_interface__ self.assertEqual(len(d), 5) - self.assertEqual(d['version'], 3) - self.assertEqual(d['shape'], kwds['shape']) - self.assertEqual(d['typestr'], kwds['typestr']) - self.assertEqual(d['data'], kwds['data']) - self.assertEqual(d['strides'], kwds['strides']) + self.assertEqual(d["version"], 3) + self.assertEqual(d["shape"], kwds["shape"]) + self.assertEqual(d["typestr"], kwds["typestr"]) + self.assertEqual(d["data"], kwds["data"]) + self.assertEqual(d["strides"], kwds["strides"]) def test_parent_property(self): kwds = dict(self.view_keywords) p = [] - kwds['parent'] = p + kwds["parent"] = p v = BufferProxy(kwds) self.assertIs(v.parent, p) @@ -71,11 +70,11 @@ class BufferProxyTest(unittest.TestCase): kwds = dict(self.view_keywords) p = [] - kwds['parent'] = p + kwds["parent"] = p # For array interface success = [] - kwds['before'] = callback + kwds["before"] = callback v = BufferProxy(kwds) self.assertEqual(len(success), 0) d = v.__array_interface__ @@ -89,7 +88,7 @@ class BufferProxyTest(unittest.TestCase): # For array struct success = [] - kwds['before'] = callback + kwds["before"] = callback v = BufferProxy(kwds) self.assertEqual(len(success), 0) c = v.__array_struct__ @@ -102,9 +101,9 @@ class BufferProxyTest(unittest.TestCase): self.assertEqual(len(success), 1) # Callback raises an exception - kwds['before'] = raise_exception + kwds["before"] = raise_exception v = BufferProxy(kwds) - self.assertRaises(MyException, lambda : v.__array_struct__) + self.assertRaises(MyException, lambda: v.__array_struct__) def test_after(self): def callback(parent): @@ -112,11 +111,11 @@ class BufferProxyTest(unittest.TestCase): kwds = dict(self.view_keywords) p = [] - kwds['parent'] = p + kwds["parent"] = p # For array interface success = [] - kwds['after'] = callback + kwds["after"] = callback v = BufferProxy(kwds) self.assertEqual(len(success), 0) d = v.__array_interface__ @@ -130,7 +129,7 @@ class BufferProxyTest(unittest.TestCase): # For array struct success = [] - kwds['after'] = callback + kwds["after"] = callback v = BufferProxy(kwds) self.assertEqual(len(success), 0) c = v.__array_struct__ @@ -144,11 +143,11 @@ class BufferProxyTest(unittest.TestCase): def test_attribute(self): v = BufferProxy(self.view_keywords) - self.assertRaises(AttributeError, getattr, v, 'undefined') - v.undefined = 12; + self.assertRaises(AttributeError, getattr, v, "undefined") + v.undefined = 12 self.assertEqual(v.undefined, 12) del v.undefined - self.assertRaises(AttributeError, getattr, v, 'undefined') + self.assertRaises(AttributeError, getattr, v, "undefined") def test_weakref(self): v = BufferProxy(self.view_keywords) @@ -163,12 +162,16 @@ class BufferProxyTest(unittest.TestCase): def test_gc(self): """refcount agnostic check that contained objects are freed""" + def before_callback(parent): return r[0] + def after_callback(parent): return r[1] - class Obj(object): + + class Obj: pass + p = Obj() a = Obj() r = [Obj(), Obj()] @@ -179,9 +182,9 @@ class BufferProxyTest(unittest.TestCase): weak_before = weakref.ref(before_callback) weak_after = weakref.ref(after_callback) kwds = dict(self.view_keywords) - kwds['parent'] = p - kwds['before'] = before_callback - kwds['after'] = after_callback + kwds["parent"] = p + kwds["before"] = before_callback + kwds["after"] = after_callback v = BufferProxy(kwds) v.some_attribute = a weak_v = weakref.ref(v) @@ -207,7 +210,7 @@ class BufferProxyTest(unittest.TestCase): # Cycle removal kwds = dict(self.view_keywords) - kwds['parent'] = [] + kwds["parent"] = [] v = BufferProxy(kwds) v.some_attribute = v tracked = True @@ -217,7 +220,7 @@ class BufferProxyTest(unittest.TestCase): else: tracked = False self.assertTrue(tracked) - kwds['parent'].append(v) + kwds["parent"].append(v) kwds = None gc.collect() n1 = len(gc.garbage) @@ -242,50 +245,52 @@ class BufferProxyTest(unittest.TestCase): def test_subclassing(self): class MyBufferProxy(BufferProxy): def __repr__(self): - return "*%s*" % (BufferProxy.__repr__(self),) + return f"*{BufferProxy.__repr__(self)}*" + kwds = dict(self.view_keywords) - kwds['parent'] = 0 + kwds["parent"] = 0 v = MyBufferProxy(kwds) self.assertEqual(v.parent, 0) r = repr(v) - self.assertEqual(r[:2], '*<') - self.assertEqual(r[-2:], '>*') + self.assertEqual(r[:2], "*<") + self.assertEqual(r[-2:], ">*") - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def NEWBUF_test_newbuf(self): from ctypes import string_at from pygame.tests.test_utils import buftools + Exporter = buftools.Exporter Importer = buftools.Importer - exp = Exporter((10,), 'B', readonly=True) + exp = Exporter((10,), "B", readonly=True) b = BufferProxy(exp) self.assertEqual(b.length, exp.len) self.assertEqual(b.raw, string_at(exp.buf, exp.len)) d = b.__array_interface__ try: - self.assertEqual(d['typestr'], '|u1') - self.assertEqual(d['shape'], exp.shape) - self.assertEqual(d['strides'], exp.strides) - self.assertEqual(d['data'], (exp.buf, True)) + self.assertEqual(d["typestr"], "|u1") + self.assertEqual(d["shape"], exp.shape) + self.assertEqual(d["strides"], exp.strides) + self.assertEqual(d["data"], (exp.buf, True)) finally: d = None - exp = Exporter((3,), '=h') + exp = Exporter((3,), "=h") b = BufferProxy(exp) self.assertEqual(b.length, exp.len) self.assertEqual(b.raw, string_at(exp.buf, exp.len)) d = b.__array_interface__ try: lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN - f = '{}i{}'.format('<' if lil_endian else '>', exp.itemsize) - self.assertEqual(d['typestr'], f) - self.assertEqual(d['shape'], exp.shape) - self.assertEqual(d['strides'], exp.strides) - self.assertEqual(d['data'], (exp.buf, False)) + f = f"{'<' if lil_endian else '>'}i{exp.itemsize}" + self.assertEqual(d["typestr"], f) + self.assertEqual(d["shape"], exp.shape) + self.assertEqual(d["strides"], exp.strides) + self.assertEqual(d["data"], (exp.buf, False)) finally: d = None - exp = Exporter((10, 2), '=i') + exp = Exporter((10, 2), "=i") b = BufferProxy(exp) imp = Importer(b, buftools.PyBUF_RECORDS) self.assertTrue(imp.obj is b) @@ -299,10 +304,12 @@ class BufferProxyTest(unittest.TestCase): self.assertEqual(imp.strides, exp.strides) self.assertTrue(imp.suboffsets is None) - d = {'typestr': '|u1', - 'shape': (10,), - 'strides': (1,), - 'data': (9, True)} # 9? Will not reading the data anyway. + d = { + "typestr": "|u1", + "shape": (10,), + "strides": (1,), + "data": (9, True), + } # 9? Will not reading the data anyway. b = BufferProxy(d) imp = Importer(b, buftools.PyBUF_SIMPLE) self.assertTrue(imp.obj is b) @@ -321,23 +328,28 @@ class BufferProxyTest(unittest.TestCase): except AttributeError: pass else: + def test_oldbuf_arg(self): self.OLDBUF_test_oldbuf_arg() def OLDBUF_test_oldbuf_arg(self): - from pygame.bufferproxy import (get_segcount, get_read_buffer, - get_write_buffer) + from pygame.bufferproxy import get_segcount, get_read_buffer, get_write_buffer - content = as_bytes('\x01\x00\x00\x02') * 12 + content = b"\x01\x00\x00\x02" * 12 memory = ctypes.create_string_buffer(content) memaddr = ctypes.addressof(memory) + def raise_exception(o): raise ValueError("An exception") - bf = BufferProxy({'shape': (len(content),), - 'typestr': '|u1', - 'data': (memaddr, False), - 'strides': (1,)}) + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, False), + "strides": (1,), + } + ) seglen, segaddr = get_read_buffer(bf, 0) self.assertEqual(segaddr, 0) self.assertEqual(seglen, 0) @@ -354,10 +366,14 @@ class BufferProxyTest(unittest.TestCase): self.assertEqual(segaddr, memaddr) self.assertEqual(seglen, len(content)) - bf = BufferProxy({'shape': (len(content),), - 'typestr': '|u1', - 'data': (memaddr, True), - 'strides': (1,)}) + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, True), + "strides": (1,), + } + ) segcount, buflen = get_segcount(bf) self.assertEqual(segcount, 1) self.assertEqual(buflen, len(content)) @@ -366,19 +382,27 @@ class BufferProxyTest(unittest.TestCase): self.assertEqual(seglen, len(content)) self.assertRaises(ValueError, get_write_buffer, bf, 0) - bf = BufferProxy({'shape': (len(content),), - 'typestr': '|u1', - 'data': (memaddr, True), - 'strides': (1,), - 'before': raise_exception}) + bf = BufferProxy( + { + "shape": (len(content),), + "typestr": "|u1", + "data": (memaddr, True), + "strides": (1,), + "before": raise_exception, + } + ) segcount, buflen = get_segcount(bf) self.assertEqual(segcount, 0) self.assertEqual(buflen, 0) - bf = BufferProxy({'shape': (3, 4), - 'typestr': '|u4', - 'data': (memaddr, True), - 'strides': (12, 4)}) + bf = BufferProxy( + { + "shape": (3, 4), + "typestr": "|u4", + "data": (memaddr, True), + "strides": (12, 4), + } + ) segcount, buflen = get_segcount(bf) self.assertEqual(segcount, 3 * 4) self.assertEqual(buflen, 3 * 4 * 4) @@ -389,69 +413,62 @@ class BufferProxyTest(unittest.TestCase): class BufferProxyLegacyTest(unittest.TestCase): - content = as_bytes('\x01\x00\x00\x02') * 12 + content = b"\x01\x00\x00\x02" * 12 buffer = ctypes.create_string_buffer(content) data = (ctypes.addressof(buffer), True) def test_length(self): - # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.length: - # The size of the buffer data in bytes. - bf = BufferProxy({'shape': (3, 4), - 'typestr': '|u4', - 'data': self.data, - 'strides': (12, 4)}) + # The size of the buffer data in bytes. + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (12, 4)} + ) self.assertEqual(bf.length, len(self.content)) - bf = BufferProxy({'shape': (3, 3), - 'typestr': '|u4', - 'data': self.data, - 'strides': (12, 4)}) - self.assertEqual(bf.length, 3*3*4) + bf = BufferProxy( + {"shape": (3, 3), "typestr": "|u4", "data": self.data, "strides": (12, 4)} + ) + self.assertEqual(bf.length, 3 * 3 * 4) def test_raw(self): - # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.raw: - # The raw buffer data as string. The string may contain NUL bytes. + # The raw buffer data as string. The string may contain NUL bytes. - bf = BufferProxy({'shape': (len(self.content),), - 'typestr': '|u1', - 'data': self.data}) + bf = BufferProxy( + {"shape": (len(self.content),), "typestr": "|u1", "data": self.data} + ) self.assertEqual(bf.raw, self.content) - bf = BufferProxy({'shape': (3, 4), - 'typestr': '|u4', - 'data': self.data, - 'strides': (4, 12)}) + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (4, 12)} + ) self.assertEqual(bf.raw, self.content) - bf = BufferProxy({'shape': (3, 4), - 'typestr': '|u1', - 'data': self.data, - 'strides': (16, 4)}) - self.assertRaises(ValueError, getattr, bf, 'raw') + bf = BufferProxy( + {"shape": (3, 4), "typestr": "|u1", "data": self.data, "strides": (16, 4)} + ) + self.assertRaises(ValueError, getattr, bf, "raw") def test_write(self): - # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.write: - # B.write (bufferproxy, buffer, offset) -> None - # - # Writes raw data to the bufferproxy. - # - # Writes the raw data from buffer to the BufferProxy object, starting - # at the specified offset within the BufferProxy. - # If the length of the passed buffer exceeds the length of the - # BufferProxy (reduced by the offset), an IndexError will be raised. + # B.write (bufferproxy, buffer, offset) -> None + # + # Writes raw data to the bufferproxy. + # + # Writes the raw data from buffer to the BufferProxy object, starting + # at the specified offset within the BufferProxy. + # If the length of the passed buffer exceeds the length of the + # BufferProxy (reduced by the offset), an IndexError will be raised. from ctypes import c_byte, sizeof, addressof, string_at, memset - nullbyte = '\x00'.encode('latin_1') + nullbyte = b"\x00" Buf = c_byte * 10 data_buf = Buf(*range(1, 3 * sizeof(Buf) + 1, 3)) data = string_at(data_buf, sizeof(data_buf)) buf = Buf() - bp = BufferProxy({'typestr': '|u1', - 'shape': (sizeof(buf),), - 'data': (addressof(buf), False)}) + bp = BufferProxy( + {"typestr": "|u1", "shape": (sizeof(buf),), "data": (addressof(buf), False)} + ) try: self.assertEqual(bp.raw, nullbyte * sizeof(Buf)) bp.write(data) @@ -469,15 +486,19 @@ class BufferProxyLegacyTest(unittest.TestCase): self.assertRaises(IndexError, bp.write, data[:5], -1) self.assertRaises(IndexError, bp.write, data[:5], bp.length) self.assertRaises(TypeError, bp.write, 12) - bp = BufferProxy({'typestr': '|u1', - 'shape': (sizeof(buf),), - 'data': (addressof(buf), True)}) - self.assertRaises(pygame.BufferError, bp.write, '123'.encode('latin_1')) + bp = BufferProxy( + { + "typestr": "|u1", + "shape": (sizeof(buf),), + "data": (addressof(buf), True), + } + ) + self.assertRaises(pygame.BufferError, bp.write, b"123") finally: # Make sure bp is garbage collected before buf bp = None gc.collect() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/camera_test.py b/venv/Lib/site-packages/pygame/tests/camera_test.py index 8dfb45a0416efd1e28de90416f0ef61e32099b20..79cf0f928b340f3c7d3c57e4a1367664bae11f20 100644 --- a/venv/Lib/site-packages/pygame/tests/camera_test.py +++ b/venv/Lib/site-packages/pygame/tests/camera_test.py @@ -1,8 +1,4 @@ import unittest -import math - -import pygame -from pygame.compat import long_ class CameraModuleTest(unittest.TestCase): diff --git a/venv/Lib/site-packages/pygame/tests/cdrom_tags.py b/venv/Lib/site-packages/pygame/tests/cdrom_tags.py deleted file mode 100644 index 6ec1b195f19f75c3e39dd655c091883c9775b14d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/cdrom_tags.py +++ /dev/null @@ -1 +0,0 @@ -__tags__ = ['interactive', 'SDL2_ignore'] diff --git a/venv/Lib/site-packages/pygame/tests/cdrom_test.py b/venv/Lib/site-packages/pygame/tests/cdrom_test.py deleted file mode 100644 index af0426be1efeea36756805490ec0e5eee19b67a2..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/cdrom_test.py +++ /dev/null @@ -1,318 +0,0 @@ -import unittest -from pygame.tests.test_utils import question, prompt - -import pygame - - -pygame.cdrom.init() -# The number of CD drives available for testing. -CD_DRIVE_COUNT = pygame.cdrom.get_count() -pygame.cdrom.quit() - - -class CDROMModuleTest(unittest.TestCase): - def setUp(self): - pygame.cdrom.init() - - def tearDown(self): - pygame.cdrom.quit() - - def todo_test_CD(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD: - - # pygame.cdrom.CD(id): return CD - # class to manage a cdrom drive - # - # You can create a CD object for each cdrom on the system. Use - # pygame.cdrom.get_count() to determine how many drives actually - # exist. The id argument is an integer of the drive, starting at zero. - # - # The CD object is not initialized, you can only call CD.get_id() and - # CD.get_name() on an uninitialized drive. - # - # It is safe to create multiple CD objects for the same drive, they - # will all cooperate normally. - # - - self.fail() - - def test_get_count(self): - """Ensure the correct number of CD drives can be detected.""" - count = pygame.cdrom.get_count() - response = question('Is the correct number of CD drives on this ' - 'system [{}]?'.format(count)) - - self.assertTrue(response) - - def test_get_init(self): - """Ensure the initialization state can be retrieved.""" - self.assertTrue(pygame.cdrom.get_init()) - - def test_init(self): - """Ensure module still initialized after multiple init() calls.""" - pygame.cdrom.init() - pygame.cdrom.init() - - self.assertTrue(pygame.cdrom.get_init()) - - def test_quit(self): - """Ensure module not initialized after quit() called.""" - pygame.cdrom.quit() - - self.assertFalse(pygame.cdrom.get_init()) - - def test_quit__multiple(self): - """Ensure module still not initialized after multiple quit() calls.""" - pygame.cdrom.quit() - pygame.cdrom.quit() - - self.assertFalse(pygame.cdrom.get_init()) - - -@unittest.skipIf(0 == CD_DRIVE_COUNT, "No CD drives detected") -class CDTypeTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - pygame.cdrom.init() - - cls._cd_id = 0 # Only testing drive 0 for now. Expand in the future. - cls._cd = pygame.cdrom.CD(cls._cd_id) - - @classmethod - def tearDownClass(cls): - pygame.cdrom.quit() - - def setUp(self): - self._cd.init() - - def tearDown(self): - self._cd.quit() - - def test_eject(self): - """Ensure CD drive opens/ejects.""" - self._cd.eject() - response = question('Did the CD eject?') - - self.assertTrue(response) - - prompt("Please close the CD drive") - - def test_get_name(self): - """Ensure correct name for CD drive.""" - cd_name = self._cd.get_name() - response = question('Is the correct name for the CD drive [{}]?' - ''.format(cd_name)) - - self.assertTrue(response) - - def todo_test_get_all(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_all: - - # CD.get_all(): return [(audio, start, end, lenth), ...] - # get all track information - # - # Return a list with information for every track on the cdrom. The - # information consists of a tuple with four values. The audio value is - # True if the track contains audio data. The start, end, and length - # values are floating point numbers in seconds. Start and end - # represent absolute times on the entire disc. - # - - self.fail() - - def todo_test_get_busy(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_busy: - - # CD.get_busy(): return bool - # true if the drive is playing audio - # - # Returns True if the drive busy playing back audio. - - self.fail() - - def todo_test_get_current(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_current: - - # CD.get_current(): return track, seconds - # the current audio playback position - # - # Returns both the current track and time of that track. This method - # works when the drive is either playing or paused. - # - # Note, track 0 is the first track on the CD. Track numbers start at zero. - - self.fail() - - def test_get_empty(self): - """Ensure correct name for CD drive.""" - prompt("Please ensure the CD drive is closed") - is_empty = self._cd.get_empty() - response = question('Is the CD drive empty?') - - self.assertEqual(is_empty, response) - - def test_get_id(self): - """Ensure the drive id/index is correct.""" - cd_id = self._cd.get_id() - - self.assertEqual(self._cd_id, cd_id) - - def test_get_init(self): - """Ensure the initialization state can be retrieved.""" - self.assertTrue(self._cd.get_init()) - - def todo_test_get_numtracks(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_numtracks: - - # CD.get_numtracks(): return count - # the number of tracks on the cdrom - # - # Return the number of tracks on the cdrom in the drive. This will - # return zero of the drive is empty or has no tracks. - # - - self.fail() - - def todo_test_get_paused(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_paused: - - # CD.get_paused(): return bool - # true if the drive is paused - # - # Returns True if the drive is currently paused. - - self.fail() - - def todo_test_get_track_audio(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_track_audio: - - # CD.get_track_audio(track): return bool - # true if the cdrom track has audio data - # - # Determine if a track on a cdrom contains audio data. You can also - # call CD.num_tracks() and CD.get_all() to determine more information - # about the cdrom. - # - # Note, track 0 is the first track on the CD. Track numbers start at zero. - - self.fail() - - def todo_test_get_track_length(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_track_length: - - # CD.get_track_length(track): return seconds - # length of a cdrom track - # - # Return a floating point value in seconds of the length of the cdrom track. - # Note, track 0 is the first track on the CD. Track numbers start at zero. - - self.fail() - - def todo_test_get_track_start(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.get_track_start: - - # CD.get_track_start(track): return seconds - # start time of a cdrom track - # - # Return the absolute time in seconds where at start of the cdrom track. - # Note, track 0 is the first track on the CD. Track numbers start at zero. - - self.fail() - - def test_init(self): - """Ensure CD drive still initialized after multiple init() calls.""" - self._cd.init() - self._cd.init() - - self.assertTrue(self._cd.get_init()) - - def todo_test_pause(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.pause: - - # CD.pause(): return None - # temporarily stop audio playback - # - # Temporarily stop audio playback on the CD. The playback can be - # resumed at the same point with the CD.resume() method. If the CD is - # not playing this method does nothing. - # - # Note, track 0 is the first track on the CD. Track numbers start at zero. - - self.fail() - - def todo_test_play(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.play: - - # CD.init(): return None - # initialize a cdrom drive for use - # - # Playback audio from an audio cdrom in the drive. Besides the track - # number argument, you can also pass a starting and ending time for - # playback. The start and end time are in seconds, and can limit the - # section of an audio track played. - # - # If you pass a start time but no end, the audio will play to the end - # of the track. If you pass a start time and 'None' for the end time, - # the audio will play to the end of the entire disc. - # - # See the CD.get_numtracks() and CD.get_track_audio() to find tracks to playback. - # Note, track 0 is the first track on the CD. Track numbers start at zero. - - self.fail() - - def test_quit(self): - """Ensure CD drive not initialized after quit() called.""" - self._cd.quit() - - self.assertFalse(self._cd.get_init()) - - def test_quit__multiple(self): - """Ensure CD drive still not initialized after multiple quit() calls. - """ - self._cd.quit() - self._cd.quit() - - self.assertFalse(self._cd.get_init()) - - def todo_test_resume(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.resume: - - # CD.resume(): return None - # unpause audio playback - # - # Unpause a paused CD. If the CD is not paused or already playing, - # this method does nothing. - # - - self.fail() - - def todo_test_stop(self): - - # __doc__ (as of 2008-08-02) for pygame.cdrom.CD.stop: - - # CD.stop(): return None - # stop audio playback - # - # Stops playback of audio from the cdrom. This will also lose the - # current playback position. This method does nothing if the drive - # isn't already playing audio. - # - - self.fail() - -################################################################################ - -if __name__ == '__main__': - unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/color_test.py b/venv/Lib/site-packages/pygame/tests/color_test.py index 992d9d48ee353c800b3d35da5ed66486d2dce15a..918c6982518be22ed3a5df0af091fb5646a6f291 100644 --- a/venv/Lib/site-packages/pygame/tests/color_test.py +++ b/venv/Lib/site-packages/pygame/tests/color_test.py @@ -1,23 +1,24 @@ -import unittest import math import operator import platform +import unittest +from collections.abc import Collection, Sequence import pygame -from pygame.compat import long_ - +from pygame.colordict import THECOLORS -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() ################################### CONSTANTS ################################## rgba_vals = [0, 1, 62, 63, 126, 127, 255] -rgba_combinations = [ - (r,g,b,a) for r in rgba_vals - for g in rgba_vals - for b in rgba_vals - for a in rgba_vals - ] +rgba_combinations = [ + (r, g, b, a) + for r in rgba_vals + for g in rgba_vals + for b in rgba_vals + for a in rgba_vals +] ################################################################################ @@ -39,6 +40,7 @@ def gamma_correct(rgba_0_255, gamma): # correct_gamma() -- test against statically defined verified correct values # coerce () -- ?? + def _assignr(x, y): x.r = y @@ -59,7 +61,7 @@ def _assign_item(x, p, y): x[p] = y -class ColorTypeTest (unittest.TestCase): +class ColorTypeTest(unittest.TestCase): def test_new(self): c = pygame.Color.__new__(pygame.Color) self.assertEqual(c, pygame.Color(0, 0, 0, 255)) @@ -78,46 +80,46 @@ class ColorTypeTest (unittest.TestCase): # This was a problem with the way 2 digit hex numbers were # calculated. The test_hex_digits test is related to the fix. Color = pygame.color.Color - self.assertRaises(ValueError, lambda: Color('# f000000')) - self.assertRaises(ValueError, lambda: Color('#f 000000')) - self.assertRaises(ValueError, lambda: Color('#-f000000')) + self.assertRaises(ValueError, lambda: Color("# f000000")) + self.assertRaises(ValueError, lambda: Color("#f 000000")) + self.assertRaises(ValueError, lambda: Color("#-f000000")) def test_hex_digits(self): # This is an implementation specific test. # Two digit hex numbers are calculated using table lookups # for the upper and lower digits. Color = pygame.color.Color - self.assertEqual(Color('#00000000').r, 0x00) - self.assertEqual(Color('#10000000').r, 0x10) - self.assertEqual(Color('#20000000').r, 0x20) - self.assertEqual(Color('#30000000').r, 0x30) - self.assertEqual(Color('#40000000').r, 0x40) - self.assertEqual(Color('#50000000').r, 0x50) - self.assertEqual(Color('#60000000').r, 0x60) - self.assertEqual(Color('#70000000').r, 0x70) - self.assertEqual(Color('#80000000').r, 0x80) - self.assertEqual(Color('#90000000').r, 0x90) - self.assertEqual(Color('#A0000000').r, 0xA0) - self.assertEqual(Color('#B0000000').r, 0xB0) - self.assertEqual(Color('#C0000000').r, 0xC0) - self.assertEqual(Color('#D0000000').r, 0xD0) - self.assertEqual(Color('#E0000000').r, 0xE0) - self.assertEqual(Color('#F0000000').r, 0xF0) - self.assertEqual(Color('#01000000').r, 0x01) - self.assertEqual(Color('#02000000').r, 0x02) - self.assertEqual(Color('#03000000').r, 0x03) - self.assertEqual(Color('#04000000').r, 0x04) - self.assertEqual(Color('#05000000').r, 0x05) - self.assertEqual(Color('#06000000').r, 0x06) - self.assertEqual(Color('#07000000').r, 0x07) - self.assertEqual(Color('#08000000').r, 0x08) - self.assertEqual(Color('#09000000').r, 0x09) - self.assertEqual(Color('#0A000000').r, 0x0A) - self.assertEqual(Color('#0B000000').r, 0x0B) - self.assertEqual(Color('#0C000000').r, 0x0C) - self.assertEqual(Color('#0D000000').r, 0x0D) - self.assertEqual(Color('#0E000000').r, 0x0E) - self.assertEqual(Color('#0F000000').r, 0x0F) + self.assertEqual(Color("#00000000").r, 0x00) + self.assertEqual(Color("#10000000").r, 0x10) + self.assertEqual(Color("#20000000").r, 0x20) + self.assertEqual(Color("#30000000").r, 0x30) + self.assertEqual(Color("#40000000").r, 0x40) + self.assertEqual(Color("#50000000").r, 0x50) + self.assertEqual(Color("#60000000").r, 0x60) + self.assertEqual(Color("#70000000").r, 0x70) + self.assertEqual(Color("#80000000").r, 0x80) + self.assertEqual(Color("#90000000").r, 0x90) + self.assertEqual(Color("#A0000000").r, 0xA0) + self.assertEqual(Color("#B0000000").r, 0xB0) + self.assertEqual(Color("#C0000000").r, 0xC0) + self.assertEqual(Color("#D0000000").r, 0xD0) + self.assertEqual(Color("#E0000000").r, 0xE0) + self.assertEqual(Color("#F0000000").r, 0xF0) + self.assertEqual(Color("#01000000").r, 0x01) + self.assertEqual(Color("#02000000").r, 0x02) + self.assertEqual(Color("#03000000").r, 0x03) + self.assertEqual(Color("#04000000").r, 0x04) + self.assertEqual(Color("#05000000").r, 0x05) + self.assertEqual(Color("#06000000").r, 0x06) + self.assertEqual(Color("#07000000").r, 0x07) + self.assertEqual(Color("#08000000").r, 0x08) + self.assertEqual(Color("#09000000").r, 0x09) + self.assertEqual(Color("#0A000000").r, 0x0A) + self.assertEqual(Color("#0B000000").r, 0x0B) + self.assertEqual(Color("#0C000000").r, 0x0C) + self.assertEqual(Color("#0D000000").r, 0x0D) + self.assertEqual(Color("#0E000000").r, 0x0E) + self.assertEqual(Color("#0F000000").r, 0x0F) def test_comparison(self): Color = pygame.color.Color @@ -176,6 +178,7 @@ class ColorTypeTest (unittest.TestCase): class TupleSubclass(tuple): pass + self.assertTrue(Color(255, 0, 0, 0) == TupleSubclass((255, 0, 0, 0))) self.assertTrue(TupleSubclass((255, 0, 0, 0)) == Color(255, 0, 0, 0)) self.assertFalse(Color(255, 0, 0, 0) != TupleSubclass((255, 0, 0, 0))) @@ -188,20 +191,20 @@ class ColorTypeTest (unittest.TestCase): self.assertFalse("#ff000000" == Color(255, 0, 0, 0)) self.assertTrue("#ff000000" != Color(255, 0, 0, 0)) - self.assertFalse(Color(255, 0, 0, 0) == 0xff000000) - self.assertTrue(Color(255, 0, 0, 0) != 0xff000000) + self.assertFalse(Color(255, 0, 0, 0) == 0xFF000000) + self.assertTrue(Color(255, 0, 0, 0) != 0xFF000000) - self.assertFalse(0xff000000 == Color(255, 0, 0, 0)) - self.assertTrue(0xff000000 != Color(255, 0, 0, 0)) + self.assertFalse(0xFF000000 == Color(255, 0, 0, 0)) + self.assertTrue(0xFF000000 != Color(255, 0, 0, 0)) self.assertFalse(Color(255, 0, 0, 0) == [255, 0, 0, 0]) self.assertTrue(Color(255, 0, 0, 0) != [255, 0, 0, 0]) - self.assertFalse([255, 0, 0, 0] == Color(255, 0, 0 ,0)) + self.assertFalse([255, 0, 0, 0] == Color(255, 0, 0, 0)) self.assertTrue([255, 0, 0, 0] != Color(255, 0, 0, 0)) # Comparison is not implemented for invalid color values. - class Test(object): + class Test: def __eq__(self, other): return -1 @@ -216,8 +219,8 @@ class ColorTypeTest (unittest.TestCase): return -2 t = Test() - t_tuple = TestTuple(('a', 0, 0, 0)) - black = Color('black') + t_tuple = TestTuple(("a", 0, 0, 0)) + black = Color("black") self.assertEqual(black == t, -1) self.assertEqual(t == black, -1) self.assertEqual(black != t, -2) @@ -228,49 +231,52 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(t_tuple != black, -2) def test_ignore_whitespace(self): - self.assertEqual(pygame.color.Color('red'), pygame.color.Color(' r e d ')) + self.assertEqual(pygame.color.Color("red"), pygame.color.Color(" r e d ")) def test_slice(self): - #"""|tags: python3_ignore|""" + # """|tags: python3_ignore|""" # slicing a color gives you back a tuple. # do all sorts of slice combinations. - c = pygame.Color(1,2,3,4) + c = pygame.Color(1, 2, 3, 4) - self.assertEqual((1,2,3,4), c[:]) - self.assertEqual((1,2,3), c[:-1]) + self.assertEqual((1, 2, 3, 4), c[:]) + self.assertEqual((1, 2, 3), c[:-1]) self.assertEqual((), c[:-5]) - self.assertEqual((1,2,3,4), c[:4]) - self.assertEqual((1,2,3,4), c[:5]) - self.assertEqual((1,2), c[:2]) + self.assertEqual((1, 2, 3, 4), c[:4]) + self.assertEqual((1, 2, 3, 4), c[:5]) + self.assertEqual((1, 2), c[:2]) self.assertEqual((1,), c[:1]) self.assertEqual((), c[:0]) - self.assertEqual((2,), c[1:-2]) self.assertEqual((3, 4), c[-2:]) self.assertEqual((4,), c[-1:]) - # NOTE: assigning to a slice is currently unsupported. - def test_unpack(self): # should be able to unpack to r,g,b,a and r,g,b - c = pygame.Color(1,2,3,4) - r,g,b,a = c - self.assertEqual((1,2,3,4), (r,g,b,a)) - self.assertEqual(c, (r,g,b,a)) + c = pygame.Color(1, 2, 3, 4) + r, g, b, a = c + self.assertEqual((1, 2, 3, 4), (r, g, b, a)) + self.assertEqual(c, (r, g, b, a)) c.set_length(3) - r,g,b = c - self.assertEqual((1,2,3), (r,g,b)) + r, g, b = c + self.assertEqual((1, 2, 3), (r, g, b)) + + # Checking if DeprecationWarning is triggered + # when function is called + for i in range(1, 5): + with self.assertWarns(DeprecationWarning): + c.set_length(i) def test_length(self): # should be able to unpack to r,g,b,a and r,g,b - c = pygame.Color(1,2,3,4) + c = pygame.Color(1, 2, 3, 4) self.assertEqual(len(c), 4) c.set_length(3) @@ -280,7 +286,7 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(c.a, 4) # however you can't get the alpha in this way: - self.assertRaises(IndexError, lambda x:c[x], 4) + self.assertRaises(IndexError, lambda x: c[x], 4) c.set_length(4) self.assertEqual(len(c), 4) @@ -289,35 +295,173 @@ class ColorTypeTest (unittest.TestCase): self.assertRaises(ValueError, c.set_length, 5) self.assertRaises(ValueError, c.set_length, -1) self.assertRaises(ValueError, c.set_length, 0) - self.assertRaises(ValueError, c.set_length, pow(2, long_(33))) + self.assertRaises(ValueError, c.set_length, pow(2, 33)) def test_case_insensitivity_of_string_args(self): - self.assertEqual(pygame.color.Color('red'), pygame.color.Color('Red')) + self.assertEqual(pygame.color.Color("red"), pygame.color.Color("Red")) def test_color(self): - c = pygame.Color(10, 20, 30, 40) - self.assertEqual(c.r, 10) - self.assertEqual(c.g, 20) - self.assertEqual(c.b, 30) - self.assertEqual(c.a, 40) - - c = pygame.Color("indianred3") - self.assertEqual(c.r, 205) - self.assertEqual(c.g, 85) - self.assertEqual(c.b, 85) - self.assertEqual(c.a, 255) + """Ensures Color objects can be created.""" + color = pygame.Color(0, 0, 0, 0) + + self.assertIsInstance(color, pygame.Color) + + def test_color__rgba_int_args(self): + """Ensures Color objects can be created using ints.""" + color = pygame.Color(10, 20, 30, 40) - c = pygame.Color(0xAABBCCDD) - self.assertEqual(c.r, 0xAA) - self.assertEqual(c.g, 0xBB) - self.assertEqual(c.b, 0xCC) - self.assertEqual(c.a, 0xDD) + self.assertEqual(color.r, 10) + self.assertEqual(color.g, 20) + self.assertEqual(color.b, 30) + self.assertEqual(color.a, 40) + def test_color__rgba_int_args_without_alpha(self): + """Ensures Color objects can be created without providing alpha.""" + color = pygame.Color(10, 20, 30) + + self.assertEqual(color.r, 10) + self.assertEqual(color.g, 20) + self.assertEqual(color.b, 30) + self.assertEqual(color.a, 255) + + def test_color__rgba_int_args_invalid_value(self): + """Ensures invalid values are detected when creating Color objects.""" self.assertRaises(ValueError, pygame.Color, 257, 10, 105, 44) self.assertRaises(ValueError, pygame.Color, 10, 257, 105, 44) self.assertRaises(ValueError, pygame.Color, 10, 105, 257, 44) self.assertRaises(ValueError, pygame.Color, 10, 105, 44, 257) + def test_color__rgba_int_args_invalid_value_without_alpha(self): + """Ensures invalid values are detected when creating Color objects + without providing an alpha. + """ + self.assertRaises(ValueError, pygame.Color, 256, 10, 105) + self.assertRaises(ValueError, pygame.Color, 10, 256, 105) + self.assertRaises(ValueError, pygame.Color, 10, 105, 256) + + def test_color__color_object_arg(self): + """Ensures Color objects can be created using Color objects.""" + color_args = (10, 20, 30, 40) + color_obj = pygame.Color(*color_args) + + new_color_obj = pygame.Color(color_obj) + + self.assertIsInstance(new_color_obj, pygame.Color) + self.assertEqual(new_color_obj, color_obj) + self.assertEqual(new_color_obj.r, color_args[0]) + self.assertEqual(new_color_obj.g, color_args[1]) + self.assertEqual(new_color_obj.b, color_args[2]) + self.assertEqual(new_color_obj.a, color_args[3]) + + def test_color__name_str_arg(self): + """Ensures Color objects can be created using str names.""" + for name in ("aquamarine3", "AQUAMARINE3", "AqUAmArIne3"): + color = pygame.Color(name) + + self.assertEqual(color.r, 102) + self.assertEqual(color.g, 205) + self.assertEqual(color.b, 170) + self.assertEqual(color.a, 255) + + def test_color__name_str_arg_from_colordict(self): + """Ensures Color objects can be created using str names + from the THECOLORS dict.""" + for name, values in THECOLORS.items(): + color = pygame.Color(name) + + self.assertEqual(color.r, values[0]) + self.assertEqual(color.g, values[1]) + self.assertEqual(color.b, values[2]) + self.assertEqual(color.a, values[3]) + + def test_color__html_str_arg(self): + """Ensures Color objects can be created using html strings.""" + # See test_webstyle() for related tests. + color = pygame.Color("#a1B2c3D4") + + self.assertEqual(color.r, 0xA1) + self.assertEqual(color.g, 0xB2) + self.assertEqual(color.b, 0xC3) + self.assertEqual(color.a, 0xD4) + + def test_color__hex_str_arg(self): + """Ensures Color objects can be created using hex strings.""" + # See test_webstyle() for related tests. + color = pygame.Color("0x1a2B3c4D") + + self.assertEqual(color.r, 0x1A) + self.assertEqual(color.g, 0x2B) + self.assertEqual(color.b, 0x3C) + self.assertEqual(color.a, 0x4D) + + def test_color__int_arg(self): + """Ensures Color objects can be created using one int value.""" + for value in (0x0, 0xFFFFFFFF, 0xAABBCCDD): + color = pygame.Color(value) + + self.assertEqual(color.r, (value >> 24) & 0xFF) + self.assertEqual(color.g, (value >> 16) & 0xFF) + self.assertEqual(color.b, (value >> 8) & 0xFF) + self.assertEqual(color.a, value & 0xFF) + + def test_color__int_arg_invalid(self): + """Ensures invalid int values are detected when creating Color objects.""" + with self.assertRaises(ValueError): + color = pygame.Color(0x1FFFFFFFF) + + def test_color__sequence_arg(self): + """Ensures Color objects can be created using tuples/lists.""" + color_values = (33, 44, 55, 66) + for seq_type in (tuple, list): + color = pygame.Color(seq_type(color_values)) + + self.assertEqual(color.r, color_values[0]) + self.assertEqual(color.g, color_values[1]) + self.assertEqual(color.b, color_values[2]) + self.assertEqual(color.a, color_values[3]) + + def test_color__sequence_arg_without_alpha(self): + """Ensures Color objects can be created using tuples/lists + without providing an alpha value. + """ + color_values = (33, 44, 55) + for seq_type in (tuple, list): + color = pygame.Color(seq_type(color_values)) + + self.assertEqual(color.r, color_values[0]) + self.assertEqual(color.g, color_values[1]) + self.assertEqual(color.b, color_values[2]) + self.assertEqual(color.a, 255) + + def test_color__sequence_arg_invalid_value(self): + """Ensures invalid sequences are detected when creating Color objects.""" + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((256, 90, 80, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 256, 80, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 256, 70))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 256))) + + def test_color__sequence_arg_invalid_value_without_alpha(self): + """Ensures invalid sequences are detected when creating Color objects + without providing an alpha. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((256, 90, 80))) + self.assertRaises(ValueError, cls, seq_type((100, 256, 80))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 256))) + + def test_color__sequence_arg_invalid_format(self): + """Ensures invalid sequences are detected when creating Color objects + with the wrong number of values. + """ + cls = pygame.Color + for seq_type in (tuple, list): + self.assertRaises(ValueError, cls, seq_type((100,))) + self.assertRaises(ValueError, cls, seq_type((100, 90))) + self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 70, 60))) + def test_rgba(self): c = pygame.Color(0) self.assertEqual(c.r, 0) @@ -561,7 +705,6 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(c.a, 146) self.assertEqual(hex(c), hex(0x33727592)) - def test_webstyle(self): c = pygame.Color("#CC00CC11") self.assertEqual(c.r, 204) @@ -621,7 +764,7 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(c.g, 0) self.assertEqual(c.b, 204) self.assertEqual(c.a, 0) - self.assertEqual(long_ (c), long_ (0xCC00CC00)) + self.assertEqual(int(c), int(0xCC00CC00)) # This will be an int c = pygame.Color(0x33727592) @@ -629,7 +772,7 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(c.g, 114) self.assertEqual(c.b, 117) self.assertEqual(c.a, 146) - self.assertEqual(long_ (c), long_ (0x33727592)) + self.assertEqual(int(c), int(0x33727592)) def test_normalize(self): c = pygame.Color(204, 38, 194, 55) @@ -673,11 +816,11 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(c[3], 213) # Now try some 'invalid' ones - self.assertRaises(ValueError, _assign_item, c, 0, 95.485) + self.assertRaises(TypeError, _assign_item, c, 0, 95.485) self.assertEqual(c[0], 33) self.assertRaises(ValueError, _assign_item, c, 1, -83) self.assertEqual(c[1], 48) - self.assertRaises(ValueError, _assign_item, c, 2, "Hello") + self.assertRaises(TypeError, _assign_item, c, 2, "Hello") self.assertEqual(c[2], 173) def test_Color_type_works_for_Surface_get_and_set_colorkey(self): @@ -693,7 +836,7 @@ class ColorTypeTest (unittest.TestCase): self.assertTrue(get_b == c.b) self.assertTrue(get_a == c.a) -########## HSLA, HSVA, CMY, I1I2I3 ALL ELEMENTS WITHIN SPECIFIED RANGE ######### + ########## HSLA, HSVA, CMY, I1I2I3 ALL ELEMENTS WITHIN SPECIFIED RANGE ######### def test_hsla__all_elements_within_limits(self): for c in rgba_combos_Color_generator(): @@ -721,21 +864,21 @@ class ColorTypeTest (unittest.TestCase): def test_i1i2i3__all_elements_within_limits(self): for c in rgba_combos_Color_generator(): i1, i2, i3 = c.i1i2i3 - self.assertTrue( 0 <= i1 <= 1) + self.assertTrue(0 <= i1 <= 1) self.assertTrue(-0.5 <= i2 <= 0.5) self.assertTrue(-0.5 <= i3 <= 0.5) def test_issue_269(self): """PyColor OverflowError on HSVA with hue value of 360 - >>> c = pygame.Color(0) - >>> c.hsva = (360,0,0,0) - Traceback (most recent call last): - File "", line 1, in - OverflowError: this is not allowed to happen ever - >>> pygame.ver - '1.9.1release' - >>> + >>> c = pygame.Color(0) + >>> c.hsva = (360,0,0,0) + Traceback (most recent call last): + File "", line 1, in + OverflowError: this is not allowed to happen ever + >>> pygame.ver + '1.9.1release' + >>> """ @@ -746,7 +889,7 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(c.hsva, (0, 100, 100, 100)) self.assertEqual(c, (255, 0, 0, 255)) -####################### COLORSPACE PROPERTY SANITY TESTS ####################### + ####################### COLORSPACE PROPERTY SANITY TESTS ####################### def colorspaces_converted_should_not_raise(self, prop): fails = 0 @@ -759,7 +902,7 @@ class ColorTypeTest (unittest.TestCase): try: setattr(other, prop, getattr(c, prop)) - #eg other.hsla = c.hsla + # eg other.hsla = c.hsla except ValueError: fails += 1 @@ -768,18 +911,18 @@ class ColorTypeTest (unittest.TestCase): self.assertTrue((fails, x) == (0, x)) def test_hsla__sanity_testing_converted_should_not_raise(self): - self.colorspaces_converted_should_not_raise('hsla') + self.colorspaces_converted_should_not_raise("hsla") def test_hsva__sanity_testing_converted_should_not_raise(self): - self.colorspaces_converted_should_not_raise('hsva') + self.colorspaces_converted_should_not_raise("hsva") def test_cmy__sanity_testing_converted_should_not_raise(self): - self.colorspaces_converted_should_not_raise('cmy') + self.colorspaces_converted_should_not_raise("cmy") def test_i1i2i3__sanity_testing_converted_should_not_raise(self): - self.colorspaces_converted_should_not_raise('i1i2i3') + self.colorspaces_converted_should_not_raise("i1i2i3") -################################################################################ + ################################################################################ def colorspaces_converted_should_equate_bar_rounding(self, prop): for c in rgba_combos_Color_generator(): @@ -787,7 +930,7 @@ class ColorTypeTest (unittest.TestCase): try: setattr(other, prop, getattr(c, prop)) - #eg other.hsla = c.hsla + # eg other.hsla = c.hsla self.assertTrue(abs(other.r - c.r) <= 1) self.assertTrue(abs(other.b - c.b) <= 1) @@ -797,21 +940,21 @@ class ColorTypeTest (unittest.TestCase): self.assertTrue(abs(other.a - c.a) <= 1) except ValueError: - pass # other tests will notify, this tests equation + pass # other tests will notify, this tests equation def test_hsla__sanity_testing_converted_should_equate_bar_rounding(self): - self.colorspaces_converted_should_equate_bar_rounding('hsla') + self.colorspaces_converted_should_equate_bar_rounding("hsla") def test_hsva__sanity_testing_converted_should_equate_bar_rounding(self): - self.colorspaces_converted_should_equate_bar_rounding('hsva') + self.colorspaces_converted_should_equate_bar_rounding("hsva") def test_cmy__sanity_testing_converted_should_equate_bar_rounding(self): - self.colorspaces_converted_should_equate_bar_rounding('cmy') + self.colorspaces_converted_should_equate_bar_rounding("cmy") def test_i1i2i3__sanity_testing_converted_should_equate_bar_rounding(self): - self.colorspaces_converted_should_equate_bar_rounding('i1i2i3') + self.colorspaces_converted_should_equate_bar_rounding("i1i2i3") -################################################################################ + ################################################################################ def test_correct_gamma__verified_against_python_implementation(self): "|tags:slow|" @@ -823,8 +966,7 @@ class ColorTypeTest (unittest.TestCase): for i, c in enumerate(rgba_combos_Color_generator()): gamma = gammas[i % gammas_len] - corrected = pygame.Color(*[gamma_correct(x, gamma) - for x in tuple(c)]) + corrected = pygame.Color(*[gamma_correct(x, gamma) for x in tuple(c)]) lib_corrected = c.correct_gamma(gamma) self.assertTrue(corrected.r == lib_corrected.r) @@ -837,31 +979,30 @@ class ColorTypeTest (unittest.TestCase): def test_pickle(self): import pickle - c1 = pygame.Color(1,2,3,4) - #c2 = pygame.Color(255,254,253,252) + + c1 = pygame.Color(1, 2, 3, 4) + # c2 = pygame.Color(255,254,253,252) pickle_string = pickle.dumps(c1) c1_frompickle = pickle.loads(pickle_string) - self.assertEqual(c1,c1_frompickle) + self.assertEqual(c1, c1_frompickle) -################################################################################ -# only available if ctypes module is also available + ################################################################################ + # only available if ctypes module is also available - @unittest.skipIf(IS_PYPY, 'PyPy has no ctypes') + @unittest.skipIf(IS_PYPY, "PyPy has no ctypes") def test_arraystruct(self): - import pygame.tests.test_utils.arrinter as ai import ctypes as ct c_byte_p = ct.POINTER(ct.c_byte) c = pygame.Color(5, 7, 13, 23) - flags = (ai.PAI_CONTIGUOUS | ai.PAI_FORTRAN | - ai.PAI_ALIGNED | ai.PAI_NOTSWAPPED) + flags = ai.PAI_CONTIGUOUS | ai.PAI_FORTRAN | ai.PAI_ALIGNED | ai.PAI_NOTSWAPPED for i in range(1, 5): c.set_length(i) inter = ai.ArrayInterface(c) self.assertEqual(inter.two, 2) self.assertEqual(inter.nd, 1) - self.assertEqual(inter.typekind, 'u') + self.assertEqual(inter.typekind, "u") self.assertEqual(inter.itemsize, 1) self.assertEqual(inter.flags, flags) self.assertEqual(inter.shape[0], i) @@ -870,27 +1011,28 @@ class ColorTypeTest (unittest.TestCase): for j in range(i): self.assertEqual(data[j], c[j]) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf(self): from pygame.tests.test_utils import buftools from ctypes import cast, POINTER, c_uint8 class ColorImporter(buftools.Importer): def __init__(self, color, flags): - super(ColorImporter, self).__init__(color, flags) + super().__init__(color, flags) self.items = cast(self.buf, POINTER(c_uint8)) def __getitem__(self, index): if 0 <= index < 4: return self.items[index] - raise IndexError("valid index values are between 0 and 3: " - "got {}".format(index)) + raise IndexError(f"valid index values are between 0 and 3: got {index}") + def __setitem__(self, index, value): if 0 <= index < 4: self.items[index] = value else: - raise IndexError("valid index values are between 0 and 3: " - "got {}".format(index)) + raise IndexError( + f"valid index values are between 0 and 3: got {index}" + ) c = pygame.Color(50, 100, 150, 200) imp = ColorImporter(c, buftools.PyBUF_SIMPLE) @@ -917,7 +1059,7 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(imp.ndim, 0) self.assertEqual(imp.itemsize, 1) self.assertEqual(imp.len, 4) - self.assertEqual(imp.format, 'B') + self.assertEqual(imp.format, "B") self.assertEqual(imp.ndim, 0) self.assertEqual(imp.itemsize, 1) self.assertEqual(imp.len, 4) @@ -945,15 +1087,201 @@ class ColorTypeTest (unittest.TestCase): self.assertEqual(imp.ndim, 1) self.assertEqual(imp.len, i) self.assertEqual(imp.shape, (i,)) - self.assertRaises(BufferError, ColorImporter, - c, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, ColorImporter, c, buftools.PyBUF_WRITABLE) + def test_color_iter(self): + c = pygame.Color(50, 100, 150, 200) -class SubclassTest(unittest.TestCase): + # call __iter__ explicitly to test that it is defined + color_iterator = c.__iter__() + for i, val in enumerate(color_iterator): + self.assertEqual(c[i], val) + + def test_color_contains(self): + c = pygame.Color(50, 60, 70) + + # call __contains__ explicitly to test that it is defined + self.assertTrue(c.__contains__(50)) + self.assertTrue(60 in c) + self.assertTrue(70 in c) + self.assertFalse(100 in c) + self.assertFalse(c.__contains__(10)) + self.assertRaises(TypeError, lambda: "string" in c) + self.assertRaises(TypeError, lambda: 3.14159 in c) + + def test_grayscale(self): + Color = pygame.color.Color + + color = Color(255, 0, 0, 255) + self.assertEqual(color.grayscale(), Color(76, 76, 76, 255)) + color = Color(3, 5, 7, 255) + self.assertEqual(color.grayscale(), Color(4, 4, 4, 255)) + color = Color(3, 5, 70, 255) + self.assertEqual(color.grayscale(), Color(11, 11, 11, 255)) + color = Color(3, 50, 70, 255) + self.assertEqual(color.grayscale(), Color(38, 38, 38, 255)) + color = Color(30, 50, 70, 255) + self.assertEqual(color.grayscale(), Color(46, 46, 46, 255)) + + color = Color(255, 0, 0, 144) + self.assertEqual(color.grayscale(), Color(76, 76, 76, 144)) + color = Color(3, 5, 7, 144) + self.assertEqual(color.grayscale(), Color(4, 4, 4, 144)) + color = Color(3, 5, 70, 144) + self.assertEqual(color.grayscale(), Color(11, 11, 11, 144)) + color = Color(3, 50, 70, 144) + self.assertEqual(color.grayscale(), Color(38, 38, 38, 144)) + color = Color(30, 50, 70, 144) + self.assertEqual(color.grayscale(), Color(46, 46, 46, 144)) + + def test_lerp(self): + # setup + Color = pygame.color.Color + + color0 = Color(0, 0, 0, 0) + color128 = Color(128, 128, 128, 128) + color255 = Color(255, 255, 255, 255) + color100 = Color(100, 100, 100, 100) + + # type checking + self.assertTrue(isinstance(color0.lerp(color128, 0.5), Color)) + + # common value testing + self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64)) + self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64)) + self.assertEqual(color128.lerp(color255, 0.5), Color(192, 192, 192, 192)) + self.assertEqual(color0.lerp(color255, 0.5), Color(128, 128, 128, 128)) + + # testing extremes + self.assertEqual(color0.lerp(color100, 0), color0) + self.assertEqual(color0.lerp(color100, 0.01), Color(1, 1, 1, 1)) + self.assertEqual(color0.lerp(color100, 0.99), Color(99, 99, 99, 99)) + self.assertEqual(color0.lerp(color100, 1), color100) + + # kwarg testing + self.assertEqual(color0.lerp(color=color100, amount=0.5), Color(50, 50, 50, 50)) + self.assertEqual(color0.lerp(amount=0.5, color=color100), Color(50, 50, 50, 50)) + + # invalid input testing + self.assertRaises(ValueError, lambda: color0.lerp(color128, 2.5)) + self.assertRaises(ValueError, lambda: color0.lerp(color128, -0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((256, 0, 0, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 256, 0, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 256, 0), 0.5)) + self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 0, 256), 0.5)) + self.assertRaises(TypeError, lambda: color0.lerp(0.2, 0.5)) + + def test_premul_alpha(self): + # setup + Color = pygame.color.Color + + color0 = Color(0, 0, 0, 0) + alpha0 = Color(255, 255, 255, 0) + alpha49 = Color(255, 0, 0, 49) + alpha67 = Color(0, 255, 0, 67) + alpha73 = Color(0, 0, 255, 73) + alpha128 = Color(255, 255, 255, 128) + alpha199 = Color(255, 255, 255, 199) + alpha255 = Color(128, 128, 128, 255) + + # type checking + self.assertTrue(isinstance(color0.premul_alpha(), Color)) + + # hand crafted value testing + self.assertEqual(alpha0.premul_alpha(), Color(0, 0, 0, 0)) + self.assertEqual(alpha49.premul_alpha(), Color(49, 0, 0, 49)) + self.assertEqual(alpha67.premul_alpha(), Color(0, 67, 0, 67)) + self.assertEqual(alpha73.premul_alpha(), Color(0, 0, 73, 73)) + self.assertEqual(alpha128.premul_alpha(), Color(128, 128, 128, 128)) + self.assertEqual(alpha199.premul_alpha(), Color(199, 199, 199, 199)) + self.assertEqual(alpha255.premul_alpha(), Color(128, 128, 128, 255)) + + # full range of alpha auto sub-testing + test_colors = [ + (200, 30, 74), + (76, 83, 24), + (184, 21, 6), + (74, 4, 74), + (76, 83, 24), + (184, 21, 234), + (160, 30, 74), + (96, 147, 204), + (198, 201, 60), + (132, 89, 74), + (245, 9, 224), + (184, 112, 6), + ] + + for r, g, b in test_colors: + for a in range(255): + with self.subTest(r=r, g=g, b=b, a=a): + alpha = a / 255.0 + self.assertEqual( + Color(r, g, b, a).premul_alpha(), + Color( + ((r + 1) * a) >> 8, + ((g + 1) * a) >> 8, + ((b + 1) * a) >> 8, + a, + ), + ) + + def test_update(self): + c = pygame.color.Color(0, 0, 0) + c.update(1, 2, 3, 4) + + self.assertEqual(c.r, 1) + self.assertEqual(c.g, 2) + self.assertEqual(c.b, 3) + self.assertEqual(c.a, 4) + + c = pygame.color.Color(0, 0, 0) + c.update([1, 2, 3, 4]) + + self.assertEqual(c.r, 1) + self.assertEqual(c.g, 2) + self.assertEqual(c.b, 3) + self.assertEqual(c.a, 4) + + c = pygame.color.Color(0, 0, 0) + c2 = pygame.color.Color(1, 2, 3, 4) + c.update(c2) + + self.assertEqual(c.r, 1) + self.assertEqual(c.g, 2) + self.assertEqual(c.b, 3) + self.assertEqual(c.a, 4) + + c = pygame.color.Color(1, 1, 1) + c.update("black") + + self.assertEqual(c.r, 0) + self.assertEqual(c.g, 0) + self.assertEqual(c.b, 0) + self.assertEqual(c.a, 255) + + c = pygame.color.Color(0, 0, 0, 120) + c.set_length(3) + c.update(1, 2, 3) + self.assertEqual(len(c), 3) + c.set_length(4) + self.assertEqual(c[3], 120) + + c.set_length(3) + c.update(1, 2, 3, 4) + self.assertEqual(len(c), 4) + + def test_collection_abc(self): + c = pygame.Color(64, 70, 75, 255) + self.assertTrue(isinstance(c, Collection)) + self.assertFalse(isinstance(c, Sequence)) + + +class SubclassTest(unittest.TestCase): class MyColor(pygame.Color): - def __init__ (self, *args, **kwds): - super(SubclassTest.MyColor, self).__init__ (*args, **kwds) + def __init__(self, *args, **kwds): + super(SubclassTest.MyColor, self).__init__(*args, **kwds) self.an_attribute = True def test_add(self): @@ -962,7 +1290,7 @@ class SubclassTest(unittest.TestCase): c2 = pygame.Color(64, 64, 64, 255) mc2 = mc1 + c2 self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") c3 = c2 + mc1 self.assertTrue(type(c3) is pygame.Color) @@ -972,7 +1300,7 @@ class SubclassTest(unittest.TestCase): c2 = pygame.Color(64, 64, 64, 255) mc2 = mc1 - c2 self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") c3 = c2 - mc1 self.assertTrue(type(c3) is pygame.Color) @@ -982,7 +1310,7 @@ class SubclassTest(unittest.TestCase): c2 = pygame.Color(64, 64, 64, 255) mc2 = mc1 * c2 self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") c3 = c2 * mc1 self.assertTrue(type(c3) is pygame.Color) @@ -992,7 +1320,7 @@ class SubclassTest(unittest.TestCase): c2 = pygame.Color(64, 64, 64, 255) mc2 = mc1 // c2 self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") c3 = c2 // mc1 self.assertTrue(type(c3) is pygame.Color) @@ -1002,7 +1330,7 @@ class SubclassTest(unittest.TestCase): c2 = pygame.Color(64, 64, 64, 255) mc2 = mc1 % c2 self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") c3 = c2 % mc1 self.assertTrue(type(c3) is pygame.Color) @@ -1011,17 +1339,22 @@ class SubclassTest(unittest.TestCase): self.assertTrue(mc1.an_attribute) mc2 = ~mc1 self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") def test_correct_gamma(self): mc1 = self.MyColor(64, 70, 75, 255) self.assertTrue(mc1.an_attribute) - mc2 = mc1.correct_gamma(.03) + mc2 = mc1.correct_gamma(0.03) self.assertTrue(isinstance(mc2, self.MyColor)) - self.assertRaises(AttributeError, getattr, mc2, 'an_attribute') + self.assertRaises(AttributeError, getattr, mc2, "an_attribute") + + def test_collection_abc(self): + mc1 = self.MyColor(64, 70, 75, 255) + self.assertTrue(isinstance(mc1, Collection)) + self.assertFalse(isinstance(mc1, Sequence)) ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/compat_test.py b/venv/Lib/site-packages/pygame/tests/compat_test.py deleted file mode 100644 index d16f3f0e0124bc2bdfe004be9936d05af698c3b1..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/compat_test.py +++ /dev/null @@ -1,87 +0,0 @@ -import sys - -import unittest -from pygame import compat -encode_file_path = sys.modules['pygame.rwobject'].encode_file_path - -class CompatModuleTest(unittest.TestCase): - def test_as_unicode(self): - r = r'Bo\u00F6tes' - ords = [ord('B'), ord('o'), 0xF6, ord('t'), ord('e'), ord('s')] - self.assertEqual(len(r), 11) - u = compat.as_unicode(r) - self.assertIsInstance(u, compat.unicode_) - self.assertEqual([ord(c) for c in u], ords) - - def test_as_bytes(self): - ords = [0, 1, 0x7F, 0x80, 0xC3, 0x20, 0xC3, 0xB6, 0xFF] - s = ''.join([chr(i) for i in ords]) - self.assertEqual(len(s), len(ords)) - b = compat.as_bytes(s) - self.assertIsInstance(b, compat.bytes_) - self.assertEqual([compat.ord_(i) for i in b], ords) - - def test_ord_(self): - self.assertIsInstance(compat.ord_(compat.bytes_(1)[0]), int) - - def test_bytes_(self): - self.assertFalse(compat.bytes_ is compat.unicode_) - self.assertTrue(hasattr(compat.bytes_, 'capitalize')) - self.assertFalse(hasattr(compat.bytes_, 'isdecimal')) - - def test_unicode_(self): - self.assertTrue(hasattr(compat.unicode_(), 'isdecimal')) - - def test_long_(self): - self.assertIsInstance(int('99999999999999999999'), compat.long_) - - def test_geterror(self): - msg = 'Success' - try: - raise TypeError(msg) - except TypeError: - e = compat.geterror() - self.assertIsInstance(e, TypeError) - self.assertEqual(str(e), msg) - - def test_xrange_(self): - self.assertFalse(isinstance(compat.xrange_(2), list)) - - def test_unichr_(self): - ordval = 86 - c = compat.unichr_(ordval) - self.assertIsInstance(c, compat.unicode_) - self.assertEqual(ord(c), ordval) - - def test_get_BytesIO(self): - BytesIO = compat.get_BytesIO() - b1 = compat.as_bytes("\x00\xffabc") - b2 = BytesIO(b1).read() - self.assertIsInstance(b2, compat.bytes_) - self.assertEqual(b2, b1) - - def test_get_StringIO(self): - StringIO = compat.get_StringIO() - b1 = "abcde" - b2 = StringIO(b1).read() - self.assertIsInstance(b2, str) - self.assertEqual(b2, b1) - - def test_raw_input_(self): - StringIO = compat.get_StringIO() - msg = 'success' - tmp = sys.stdin - sys.stdin = StringIO(msg + '\n') - try: - s = compat.raw_input_() - self.assertEqual(s, msg) - finally: - sys.stdin = tmp - - def test_filesystem_encode(self): - upath = compat.as_unicode(r"ab\u212Acd") - self.assertEqual(compat.filesystem_encode(upath), - encode_file_path(upath)) - -if __name__ == '__main__': - unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/constants_test.py b/venv/Lib/site-packages/pygame/tests/constants_test.py index 6ac574871696b10e34ca31427486b27a68e92a19..a028f982b762bd3ff0baddfaf56d2defc4cb0f0d 100644 --- a/venv/Lib/site-packages/pygame/tests/constants_test.py +++ b/venv/Lib/site-packages/pygame/tests/constants_test.py @@ -2,50 +2,425 @@ import unittest import pygame.constants -class KmodTests(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.constants = [ - 'KMOD_NONE', - 'KMOD_LSHIFT', - 'KMOD_RSHIFT', - 'KMOD_LCTRL', - 'KMOD_RCTRL', - 'KMOD_LALT', - 'KMOD_RALT', - 'KMOD_LMETA', - 'KMOD_RMETA', - 'KMOD_NUM', - 'KMOD_CAPS', - 'KMOD_MODE', - 'KMOD_CTRL', - 'KMOD_SHIFT', - 'KMOD_ALT', - 'KMOD_META', - ] - if pygame.get_sdl_version()[0] >= 2: - cls.constants.extend([ - 'KMOD_LGUI', - 'KMOD_RGUI', - 'KMOD_GUI', - ]) - - def test_kmod_existence(self): - for k in self.constants: - self.assertTrue(hasattr(pygame.constants, k), 'missing constant {}'.format(k)) - - def test_kmod_types(self): - for k in self.constants: - self.assertEqual(type(getattr(pygame.constants, k)), int) - -class KeyConstantTests(unittest.TestCase): - def test_letters(self): - for c in range(ord('a'), ord('z') + 1): - c = chr(c) - self.assertTrue(hasattr(pygame.constants, 'K_%s' % c), - 'missing constant: K_%s' % c) +# K_* and KSCAN_* common names. +K_AND_KSCAN_COMMON_NAMES = ( + "UNKNOWN", + "BACKSPACE", + "TAB", + "CLEAR", + "RETURN", + "PAUSE", + "ESCAPE", + "SPACE", + "COMMA", + "MINUS", + "PERIOD", + "SLASH", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "SEMICOLON", + "EQUALS", + "LEFTBRACKET", + "BACKSLASH", + "RIGHTBRACKET", + "DELETE", + "KP0", + "KP1", + "KP2", + "KP3", + "KP4", + "KP5", + "KP6", + "KP7", + "KP8", + "KP9", + "KP_PERIOD", + "KP_DIVIDE", + "KP_MULTIPLY", + "KP_MINUS", + "KP_PLUS", + "KP_ENTER", + "KP_EQUALS", + "UP", + "DOWN", + "RIGHT", + "LEFT", + "INSERT", + "HOME", + "END", + "PAGEUP", + "PAGEDOWN", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "NUMLOCK", + "CAPSLOCK", + "SCROLLOCK", + "RSHIFT", + "LSHIFT", + "RCTRL", + "LCTRL", + "RALT", + "LALT", + "RMETA", + "LMETA", + "LSUPER", + "RSUPER", + "MODE", + "HELP", + "PRINT", + "SYSREQ", + "BREAK", + "MENU", + "POWER", + "EURO", + "KP_0", + "KP_1", + "KP_2", + "KP_3", + "KP_4", + "KP_5", + "KP_6", + "KP_7", + "KP_8", + "KP_9", + "NUMLOCKCLEAR", + "SCROLLLOCK", + "RGUI", + "LGUI", + "PRINTSCREEN", + "CURRENCYUNIT", + "CURRENCYSUBUNIT", +) + +# Constants that have the same value. +K_AND_KSCAN_COMMON_OVERLAPS = ( + ("KP0", "KP_0"), + ("KP1", "KP_1"), + ("KP2", "KP_2"), + ("KP3", "KP_3"), + ("KP4", "KP_4"), + ("KP5", "KP_5"), + ("KP6", "KP_6"), + ("KP7", "KP_7"), + ("KP8", "KP_8"), + ("KP9", "KP_9"), + ("NUMLOCK", "NUMLOCKCLEAR"), + ("SCROLLOCK", "SCROLLLOCK"), + ("LSUPER", "LMETA", "LGUI"), + ("RSUPER", "RMETA", "RGUI"), + ("PRINT", "PRINTSCREEN"), + ("BREAK", "PAUSE"), + ("EURO", "CURRENCYUNIT"), +) + + +def create_overlap_set(constant_names): + """Helper function to find overlapping constant values/names. + + Returns a set of fronzensets: + set(frozenset(names of overlapping constants), ...) + """ + # Create an overlap dict. + overlap_dict = {} + + for name in constant_names: + value = getattr(pygame.constants, name) + overlap_dict.setdefault(value, set()).add(name) + + # Get all entries with more than 1 value. + overlaps = set() + + for overlap_names in overlap_dict.values(): + if len(overlap_names) > 1: + overlaps.add(frozenset(overlap_names)) + + return overlaps + + +class KConstantsTests(unittest.TestCase): + """Test K_* (key) constants.""" + + # K_* specific names. + K_SPECIFIC_NAMES = ( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "QUOTE", + "BACKQUOTE", + "EXCLAIM", + "QUOTEDBL", + "HASH", + "DOLLAR", + "AMPERSAND", + "LEFTPAREN", + "RIGHTPAREN", + "ASTERISK", + "PLUS", + "COLON", + "LESS", + "GREATER", + "QUESTION", + "AT", + "CARET", + "UNDERSCORE", + "PERCENT", + ) + + # Create a sequence of all the K_* constant names. + K_NAMES = tuple("K_" + n for n in K_AND_KSCAN_COMMON_NAMES + K_SPECIFIC_NAMES) + + def test_k__existence(self): + """Ensures K constants exist.""" + for name in self.K_NAMES: + self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}") + + def test_k__type(self): + """Ensures K constants are the correct type.""" + for name in self.K_NAMES: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_k__value_overlap(self): + """Ensures no unexpected K constant values overlap.""" + EXPECTED_OVERLAPS = { + frozenset("K_" + n for n in item) for item in K_AND_KSCAN_COMMON_OVERLAPS + } + + overlaps = create_overlap_set(self.K_NAMES) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + +class KscanConstantsTests(unittest.TestCase): + """Test KSCAN_* (scancode) constants.""" + + # KSCAN_* specific names. + KSCAN_SPECIFIC_NAMES = ( + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "APOSTROPHE", + "GRAVE", + "INTERNATIONAL1", + "INTERNATIONAL2", + "INTERNATIONAL3", + "INTERNATIONAL4", + "INTERNATIONAL5", + "INTERNATIONAL6", + "INTERNATIONAL7", + "INTERNATIONAL8", + "INTERNATIONAL9", + "LANG1", + "LANG2", + "LANG3", + "LANG4", + "LANG5", + "LANG6", + "LANG7", + "LANG8", + "LANG9", + "NONUSBACKSLASH", + "NONUSHASH", + ) + + # Create a sequence of all the KSCAN_* constant names. + KSCAN_NAMES = tuple( + "KSCAN_" + n for n in K_AND_KSCAN_COMMON_NAMES + KSCAN_SPECIFIC_NAMES + ) + + def test_kscan__existence(self): + """Ensures KSCAN constants exist.""" + for name in self.KSCAN_NAMES: + self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}") + + def test_kscan__type(self): + """Ensures KSCAN constants are the correct type.""" + for name in self.KSCAN_NAMES: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_kscan__value_overlap(self): + """Ensures no unexpected KSCAN constant values overlap.""" + EXPECTED_OVERLAPS = { + frozenset("KSCAN_" + n for n in item) + for item in K_AND_KSCAN_COMMON_OVERLAPS + } + + overlaps = create_overlap_set(self.KSCAN_NAMES) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + +class KmodConstantsTests(unittest.TestCase): + """Test KMOD_* (key modifier) constants.""" + + # KMOD_* constant names. + KMOD_CONSTANTS = ( + "KMOD_NONE", + "KMOD_LSHIFT", + "KMOD_RSHIFT", + "KMOD_SHIFT", + "KMOD_LCTRL", + "KMOD_RCTRL", + "KMOD_CTRL", + "KMOD_LALT", + "KMOD_RALT", + "KMOD_ALT", + "KMOD_LMETA", + "KMOD_RMETA", + "KMOD_META", + "KMOD_NUM", + "KMOD_CAPS", + "KMOD_MODE", + "KMOD_LGUI", + "KMOD_RGUI", + "KMOD_GUI", + ) + + def test_kmod__existence(self): + """Ensures KMOD constants exist.""" + for name in self.KMOD_CONSTANTS: + self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}") + + def test_kmod__type(self): + """Ensures KMOD constants are the correct type.""" + for name in self.KMOD_CONSTANTS: + value = getattr(pygame.constants, name) + + self.assertIs(type(value), int) + + def test_kmod__value_overlap(self): + """Ensures no unexpected KMOD constant values overlap.""" + # KMODs that have the same values. + EXPECTED_OVERLAPS = { + frozenset(["KMOD_LGUI", "KMOD_LMETA"]), + frozenset(["KMOD_RGUI", "KMOD_RMETA"]), + frozenset(["KMOD_GUI", "KMOD_META"]), + } + + overlaps = create_overlap_set(self.KMOD_CONSTANTS) + + self.assertSetEqual(overlaps, EXPECTED_OVERLAPS) + + def test_kmod__no_bitwise_overlap(self): + """Ensures certain KMOD constants have no overlapping bits.""" + NO_BITWISE_OVERLAP = ( + "KMOD_NONE", + "KMOD_LSHIFT", + "KMOD_RSHIFT", + "KMOD_LCTRL", + "KMOD_RCTRL", + "KMOD_LALT", + "KMOD_RALT", + "KMOD_LMETA", + "KMOD_RMETA", + "KMOD_NUM", + "KMOD_CAPS", + "KMOD_MODE", + ) + + kmods = 0 + + for name in NO_BITWISE_OVERLAP: + value = getattr(pygame.constants, name) + + self.assertFalse(kmods & value) + + kmods |= value + + def test_kmod__bitwise_overlap(self): + """Ensures certain KMOD constants have overlapping bits.""" + # KMODS that are comprised of other KMODs. + KMOD_COMPRISED_DICT = { + "KMOD_SHIFT": ("KMOD_LSHIFT", "KMOD_RSHIFT"), + "KMOD_CTRL": ("KMOD_LCTRL", "KMOD_RCTRL"), + "KMOD_ALT": ("KMOD_LALT", "KMOD_RALT"), + "KMOD_META": ("KMOD_LMETA", "KMOD_RMETA"), + "KMOD_GUI": ("KMOD_LGUI", "KMOD_RGUI"), + } + + for base_name, seq_names in KMOD_COMPRISED_DICT.items(): + expected_value = 0 # Reset. + + for name in seq_names: + expected_value |= getattr(pygame.constants, name) + + value = getattr(pygame.constants, base_name) + + self.assertEqual(value, expected_value) + ################################################################################ -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/cursors_test.py b/venv/Lib/site-packages/pygame/tests/cursors_test.py index 1e465e81a5ac1038e3fbe1e2e21b209ab772cde5..8132c513cbcf0c49b1b56e04ae490dd941763109 100644 --- a/venv/Lib/site-packages/pygame/tests/cursors_test.py +++ b/venv/Lib/site-packages/pygame/tests/cursors_test.py @@ -4,60 +4,287 @@ import pygame class CursorsModuleTest(unittest.TestCase): - def todo_test_compile(self): - + def test_compile(self): # __doc__ (as of 2008-06-25) for pygame.cursors.compile: - # pygame.cursors.compile(strings, black, white,xor) -> data, mask - # compile cursor strings into cursor data - # - # This takes a set of strings with equal length and computes - # the binary data for that cursor. The string widths must be - # divisible by 8. - # - # The black and white arguments are single letter strings that - # tells which characters will represent black pixels, and which - # characters represent white pixels. All other characters are - # considered clear. - # - # This returns a tuple containing the cursor data and cursor mask - # data. Both these arguments are used when setting a cursor with - # pygame.mouse.set_cursor(). - - self.fail() + # pygame.cursors.compile(strings, black, white,xor) -> data, mask + # compile cursor strings into cursor data + # + # This takes a set of strings with equal length and computes + # the binary data for that cursor. The string widths must be + # divisible by 8. + # + # The black and white arguments are single letter strings that + # tells which characters will represent black pixels, and which + # characters represent white pixels. All other characters are + # considered clear. + # + # This returns a tuple containing the cursor data and cursor mask + # data. Both these arguments are used when setting a cursor with + # pygame.mouse.set_cursor(). + + # Various types of input strings + test_cursor1 = ("X.X.XXXX", "XXXXXX..", " XXXX ") + + test_cursor2 = ( + "X.X.XXXX", + "XXXXXX..", + "XXXXXX ", + "XXXXXX..", + "XXXXXX..", + "XXXXXX", + "XXXXXX..", + "XXXXXX..", + ) + test_cursor3 = (".XX.", " ", ".. ", "X.. X") + + # Test such that total number of strings is not divisible by 8 + with self.assertRaises(ValueError): + pygame.cursors.compile(test_cursor1) + + # Test such that size of individual string is not divisible by 8 + with self.assertRaises(ValueError): + pygame.cursors.compile(test_cursor2) + + # Test such that neither size of individual string nor total number of strings is divisible by 8 + with self.assertRaises(ValueError): + pygame.cursors.compile(test_cursor3) + + # Test that checks whether the byte data from compile function is equal to actual byte data + actual_byte_data = ( + 192, + 0, + 0, + 224, + 0, + 0, + 240, + 0, + 0, + 216, + 0, + 0, + 204, + 0, + 0, + 198, + 0, + 0, + 195, + 0, + 0, + 193, + 128, + 0, + 192, + 192, + 0, + 192, + 96, + 0, + 192, + 48, + 0, + 192, + 56, + 0, + 192, + 248, + 0, + 220, + 192, + 0, + 246, + 96, + 0, + 198, + 96, + 0, + 6, + 96, + 0, + 3, + 48, + 0, + 3, + 48, + 0, + 1, + 224, + 0, + 1, + 128, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ), ( + 192, + 0, + 0, + 224, + 0, + 0, + 240, + 0, + 0, + 248, + 0, + 0, + 252, + 0, + 0, + 254, + 0, + 0, + 255, + 0, + 0, + 255, + 128, + 0, + 255, + 192, + 0, + 255, + 224, + 0, + 255, + 240, + 0, + 255, + 248, + 0, + 255, + 248, + 0, + 255, + 192, + 0, + 247, + 224, + 0, + 199, + 224, + 0, + 7, + 224, + 0, + 3, + 240, + 0, + 3, + 240, + 0, + 1, + 224, + 0, + 1, + 128, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ) + + cursor = pygame.cursors.compile(pygame.cursors.thickarrow_strings) + self.assertEqual(cursor, actual_byte_data) + + # Test such that cursor byte data obtained from compile function is valid in pygame.mouse.set_cursor() + pygame.display.init() + try: + pygame.mouse.set_cursor((24, 24), (0, 0), *cursor) + except pygame.error as e: + if "not currently supported" in str(e): + unittest.skip("skipping test as set_cursor() is not supported") + finally: + pygame.display.quit() + + ################################################################################ def test_load_xbm(self): # __doc__ (as of 2008-06-25) for pygame.cursors.load_xbm: - # pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args - # reads a pair of XBM files into set_cursor arguments - # - # Arguments can either be filenames or filelike objects - # with the readlines method. Not largely tested, but - # should work with typical XBM files. + # pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args + # reads a pair of XBM files into set_cursor arguments + # + # Arguments can either be filenames or filelike objects + # with the readlines method. Not largely tested, but + # should work with typical XBM files. # Test that load_xbm will take filenames as arguments cursorfile = fixture_path(r"xbm_cursors/white_sizing.xbm") - maskfile = fixture_path(r"xbm_cursors/white_sizing_mask.xbm") + maskfile = fixture_path(r"xbm_cursors/white_sizing_mask.xbm") cursor = pygame.cursors.load_xbm(cursorfile, maskfile) # Test that load_xbm will take file objects as arguments with open(cursorfile) as cursor_f, open(maskfile) as mask_f: cursor = pygame.cursors.load_xbm(cursor_f, mask_f) + # Can it load using pathlib.Path? + import pathlib + + cursor = pygame.cursors.load_xbm( + pathlib.Path(cursorfile), pathlib.Path(maskfile) + ) + # Is it in a format that mouse.set_cursor won't blow up on? pygame.display.init() try: pygame.mouse.set_cursor(*cursor) except pygame.error as e: - if 'not currently supported' in str(e): - unittest.skip('skipping test as set_cursor() is not supported') + if "not currently supported" in str(e): + unittest.skip("skipping test as set_cursor() is not supported") finally: pygame.display.quit() + def test_Cursor(self): + """Ensure that the cursor object parses information properly""" + + c1 = pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_CROSSHAIR) + + self.assertEqual(c1.data, (pygame.SYSTEM_CURSOR_CROSSHAIR,)) + self.assertEqual(c1.type, "system") + + c2 = pygame.cursors.Cursor(c1) + + self.assertEqual(c1, c2) + + with self.assertRaises(TypeError): + pygame.cursors.Cursor(-34002) + with self.assertRaises(TypeError): + pygame.cursors.Cursor("a", "b", "c", "d") + with self.assertRaises(TypeError): + pygame.cursors.Cursor((2,)) + + c3 = pygame.cursors.Cursor((0, 0), pygame.Surface((20, 20))) + + self.assertEqual(c3.data[0], (0, 0)) + self.assertEqual(c3.data[1].get_size(), (20, 20)) + self.assertEqual(c3.type, "color") + + xormask, andmask = pygame.cursors.compile(pygame.cursors.thickarrow_strings) + c4 = pygame.cursors.Cursor((24, 24), (0, 0), xormask, andmask) + + self.assertEqual(c4.data, ((24, 24), (0, 0), xormask, andmask)) + self.assertEqual(c4.type, "bitmap") + + ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() ################################################################################ diff --git a/venv/Lib/site-packages/pygame/tests/display_test.py b/venv/Lib/site-packages/pygame/tests/display_test.py index 7ec8bba047e1cb990caf8cf51c0b53fabb47ba0f..35851fa78d6c8085e1d2c97496b82e3377b7bbe9 100644 --- a/venv/Lib/site-packages/pygame/tests/display_test.py +++ b/venv/Lib/site-packages/pygame/tests/display_test.py @@ -1,293 +1,703 @@ -# -*- coding: utf-8 -*- - import unittest +import os +import sys +import time + import pygame, pygame.transform -from pygame.compat import unicode_ + +from pygame.tests.test_utils import question from pygame import display + class DisplayModuleTest(unittest.TestCase): default_caption = "pygame window" def setUp(self): display.init() + def tearDown(self): display.quit() - def test_update(self): - """ see if pygame.display.update takes rects with negative values. - "|Tags:display|" - """ - - #pygame.init() - screen = pygame.display.set_mode((100, 100)) - screen.fill((55, 55, 55)) - - r1 = pygame.Rect(0, 0, 100, 100) - pygame.display.update(r1) - - r2 = pygame.Rect(-10, 0, 100, 100) - pygame.display.update(r2) - - r3 = pygame.Rect(-10, 0, -100, -100) - pygame.display.update(r3) - - # NOTE: if I don't call pygame.quit there is a segfault. hrmm. - #pygame.quit() - # I think it's because unittest runs stuff in threads - # here's a stack trace... - - # NOTE to author of above: - # unittest doesn't run tests in threads - # segfault was probably caused by another tests need - # for a "clean slate" - - """ - #0 0x08103b7c in PyFrame_New () - #1 0x080bd666 in PyEval_EvalCodeEx () - #2 0x08105202 in PyFunction_SetClosure () - #3 0x080595ae in PyObject_Call () - #4 0x080b649f in PyEval_CallObjectWithKeywords () - #5 0x08059585 in PyObject_CallObject () - #6 0xb7f7aa2d in initbase () from /usr/lib/python2.4/site-packages/pygame/base.so - #7 0x080e09bd in Py_Finalize () - #8 0x08055597 in Py_Main () - #9 0xb7e04eb0 in __libc_start_main () from /lib/tls/libc.so.6 - #10 0x08054e31 in _start () - - """ - def test_Info(self): inf = pygame.display.Info() self.assertNotEqual(inf.current_h, -1) self.assertNotEqual(inf.current_w, -1) - #probably have an older SDL than 1.2.10 if -1. + # probably have an older SDL than 1.2.10 if -1. - screen = pygame.display.set_mode((128,128)) + screen = pygame.display.set_mode((128, 128)) inf = pygame.display.Info() self.assertEqual(inf.current_h, 128) self.assertEqual(inf.current_w, 128) - def todo_test_flip(self): + def test_flip(self): + screen = pygame.display.set_mode((100, 100)) - # __doc__ (as of 2008-08-02) for pygame.display.flip: + # test without a change + self.assertIsNone(pygame.display.flip()) - # pygame.display.flip(): return None - # update the full display Surface to the screen - # - # This will update the contents of the entire display. If your display - # mode is using the flags pygame.HWSURFACE and pygame.DOUBLEBUF, this - # will wait for a vertical retrace and swap the surfaces. If you are - # using a different type of display mode, it will simply update the - # entire contents of the surface. - # - # When using an pygame.OPENGL display mode this will perform a gl buffer swap. + # test with a change + pygame.Surface.fill(screen, (66, 66, 53)) + self.assertIsNone(pygame.display.flip()) - self.fail() + # test without display init + pygame.display.quit() + with self.assertRaises(pygame.error): + (pygame.display.flip()) - def todo_test_get_active(self): + # test without window + del screen + with self.assertRaises(pygame.error): + (pygame.display.flip()) - # __doc__ (as of 2008-08-02) for pygame.display.get_active: + def test_get_active(self): + """Test the get_active function""" - # pygame.display.get_active(): return bool - # true when the display is active on the display - # - # After pygame.display.set_mode() is called the display Surface will - # be visible on the screen. Most windowed displays can be hidden by - # the user. If the display Surface is hidden or iconified this will - # return False. - # + # Initially, the display is not active + pygame.display.quit() + self.assertEqual(pygame.display.get_active(), False) - self.fail() + # get_active defaults to true after a set_mode + pygame.display.init() + pygame.display.set_mode((640, 480)) + self.assertEqual(pygame.display.get_active(), True) - def test_get_caption(self): + # get_active after init/quit should be False + # since no display is visible + pygame.display.quit() + pygame.display.init() + self.assertEqual(pygame.display.get_active(), False) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + "requires the SDL_VIDEODRIVER to be a non dummy value", + ) + def test_get_active_iconify(self): + """Test the get_active function after an iconify""" + + # According to the docs, get_active should return + # false if the display is iconified + pygame.display.set_mode((640, 480)) + + pygame.event.clear() + pygame.display.iconify() - # __doc__ (as of 2008-08-02) for pygame.display.get_caption: + for _ in range(100): + time.sleep(0.01) + pygame.event.pump() - # pygame.display.get_caption(): return (title, icontitle) - # get the current window caption - # - # Returns the title and icontitle for the display Surface. These will - # often be the same value. - # + self.assertEqual(pygame.display.get_active(), False) + def test_get_caption(self): screen = display.set_mode((100, 100)) + self.assertEqual(display.get_caption()[0], self.default_caption) def test_set_caption(self): + TEST_CAPTION = "test" + screen = display.set_mode((100, 100)) - # __doc__ (as of 2008-08-02) for pygame.display.set_caption: - - # pygame.display.set_caption(title, icontitle=None): return None - # set the current window caption - # - # If the display has a window title, this function will change the - # name on the window. Some systems support an alternate shorter title - # to be used for minimized displays. - # + self.assertIsNone(display.set_caption(TEST_CAPTION)) + self.assertEqual(display.get_caption()[0], TEST_CAPTION) + self.assertEqual(display.get_caption()[1], TEST_CAPTION) + def test_set_caption_kwargs(self): TEST_CAPTION = "test" screen = display.set_mode((100, 100)) - self.assertIsNone(display.set_caption(TEST_CAPTION)) + + self.assertIsNone(display.set_caption(title=TEST_CAPTION)) self.assertEqual(display.get_caption()[0], TEST_CAPTION) self.assertEqual(display.get_caption()[1], TEST_CAPTION) def test_caption_unicode(self): - TEST_CAPTION = u'台' + TEST_CAPTION = "台" display.set_caption(TEST_CAPTION) - import sys - if sys.version_info.major >= 3: - self.assertEqual(display.get_caption()[0], TEST_CAPTION) - else: - self.assertEqual(unicode_(display.get_caption()[0], 'utf8'), TEST_CAPTION) - - def todo_test_get_driver(self): - - # __doc__ (as of 2008-08-02) for pygame.display.get_driver: - - # pygame.display.get_driver(): return name - # get the name of the pygame display backend - # - # Pygame chooses one of many available display backends when it is - # initialized. This returns the internal name used for the display - # backend. This can be used to provide limited information about what - # display capabilities might be accelerated. See the SDL_VIDEODRIVER - # flags in pygame.display.set_mode() to see some of the common - # options. - # + self.assertEqual(display.get_caption()[0], TEST_CAPTION) - self.fail() + def test_get_driver(self): + drivers = [ + "aalib", + "android", + "arm", + "cocoa", + "dga", + "directx", + "directfb", + "dummy", + "emscripten", + "fbcon", + "ggi", + "haiku", + "khronos", + "kmsdrm", + "nacl", + "offscreen", + "pandora", + "psp", + "qnx", + "raspberry", + "svgalib", + "uikit", + "vgl", + "vivante", + "wayland", + "windows", + "windib", + "winrt", + "x11", + ] + driver = display.get_driver() + self.assertIn(driver, drivers) - def todo_test_get_init(self): + display.quit() + with self.assertRaises(pygame.error): + driver = display.get_driver() + + def test_get_init(self): + """Ensures the module's initialization state can be retrieved.""" + # display.init() already called in setUp() + self.assertTrue(display.get_init()) + + # This test can be uncommented when issues #991 and #993 are resolved. + @unittest.skipIf(True, "SDL2 issues") + def test_get_surface(self): + """Ensures get_surface gets the current display surface.""" + lengths = (1, 5, 100) + + for expected_size in ((w, h) for w in lengths for h in lengths): + for expected_depth in (8, 16, 24, 32): + expected_surface = display.set_mode(expected_size, 0, expected_depth) + + surface = pygame.display.get_surface() + + self.assertEqual(surface, expected_surface) + self.assertIsInstance(surface, pygame.Surface) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + + def test_get_surface__mode_not_set(self): + """Ensures get_surface handles the display mode not being set.""" + surface = pygame.display.get_surface() + + self.assertIsNone(surface) + + def test_get_wm_info(self): + wm_info = display.get_wm_info() + # Assert function returns a dictionary type + self.assertIsInstance(wm_info, dict) + + wm_info_potential_keys = { + "colorbuffer", + "connection", + "data", + "dfb", + "display", + "framebuffer", + "fswindow", + "hdc", + "hglrc", + "hinstance", + "lock_func", + "resolveFramebuffer", + "shell_surface", + "surface", + "taskHandle", + "unlock_func", + "wimpVersion", + "window", + "wmwindow", + } + + # If any unexpected dict keys are present, they + # will be stored in set wm_info_remaining_keys + wm_info_remaining_keys = set(wm_info.keys()).difference(wm_info_potential_keys) + + # Assert set is empty (& therefore does not + # contain unexpected dict keys) + self.assertFalse(wm_info_remaining_keys) + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_get_attribute(self): + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the original values of the + # flags before setting them with a different value. + original_values = [] + + original_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES) + ) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO)) - # __doc__ (as of 2008-08-02) for pygame.display.get_init: + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_ACCELERATED_VISUAL) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MAJOR_VERSION) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MINOR_VERSION) + ) + original_values.append(pygame.display.gl_get_attribute(pygame.GL_CONTEXT_FLAGS)) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_PROFILE_MASK) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + original_values.append( + pygame.display.gl_get_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) - # pygame.display.get_init(): return bool - # true if the display module is initialized - # - # Returns True if the pygame.display module is currently initialized. + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8) + pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24) + pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 8) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_RED_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_GREEN_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_BLUE_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_ALPHA_SIZE, 16) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, 1) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, 1) + pygame.display.gl_set_attribute(pygame.GL_STEREO, 0) + pygame.display.gl_set_attribute(pygame.GL_ACCELERATED_VISUAL, 0) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 1) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 1) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_FLAGS, 0) + pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, 0) + pygame.display.gl_set_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT, 0) + pygame.display.gl_set_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE, 0) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0] + + # We create a list where we store the values after getting them + get_values = [] + + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCELERATED_VISUAL)) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MAJOR_VERSION) + ) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MINOR_VERSION) + ) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_CONTEXT_FLAGS)) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_CONTEXT_PROFILE_MASK) + ) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + get_values.append( + pygame.display.gl_get_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) - self.fail() + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(original_values)): + self.assertTrue( + (get_values[i] == original_values[i]) + or (get_values[i] == set_values[i]) + ) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_get_attribute_kwargs(self): + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the original values of the + # flags before setting them with a different value. + original_values = [] + + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES) + ) + original_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO)) - def todo_test_get_surface(self): + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCELERATED_VISUAL) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_FLAGS) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + original_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) - # __doc__ (as of 2008-08-02) for pygame.display.get_surface: + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(flag=pygame.GL_ALPHA_SIZE, value=8) + pygame.display.gl_set_attribute(flag=pygame.GL_DEPTH_SIZE, value=24) + pygame.display.gl_set_attribute(flag=pygame.GL_STENCIL_SIZE, value=8) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_RED_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE, value=16) + pygame.display.gl_set_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_MULTISAMPLESAMPLES, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_STEREO, value=0) + pygame.display.gl_set_attribute(flag=pygame.GL_ACCELERATED_VISUAL, value=0) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION, value=1) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_FLAGS, value=0) + pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK, value=0) + pygame.display.gl_set_attribute( + flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT, value=0 + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE, value=0 + ) - # pygame.display.get_surface(): return Surface - # get a reference to the currently set display surface - # - # Return a reference to the currently set display Surface. If no - # display mode has been set this will return None. - # + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0] - self.fail() + # We create a list where we store the values after getting them + get_values = [] - def todo_test_get_wm_info(self): + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES) + ) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCELERATED_VISUAL) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION) + ) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_FLAGS)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE) + ) - # __doc__ (as of 2008-08-02) for pygame.display.get_wm_info: + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(original_values)): + self.assertTrue( + (get_values[i] == original_values[i]) + or (get_values[i] == set_values[i]) + ) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_set_attribute(self): + # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute: - # pygame.display.get_wm_info(): return dict - # Get information about the current windowing system - # - # Creates a dictionary filled with string keys. The strings and values - # are arbitrarily created by the system. Some systems may have no - # information and an empty dictionary will be returned. Most platforms - # will return a "window" key with the value set to the system id for - # the current display. - # - # New with pygame 1.7.1 + # pygame.display.gl_set_attribute(flag, value): return None + # request an opengl display attribute for the display mode + # + # When calling pygame.display.set_mode() with the pygame.OPENGL flag, + # Pygame automatically handles setting the OpenGL attributes like + # color and doublebuffering. OpenGL offers several other attributes + # you may want control over. Pass one of these attributes as the flag, + # and its appropriate value. This must be called before + # pygame.display.set_mode() + # + # The OPENGL flags are; + # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, + # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, + # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO + + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0] + + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, set_values[0]) + pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, set_values[1]) + pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, set_values[2]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_RED_SIZE, set_values[3]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_GREEN_SIZE, set_values[4]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_BLUE_SIZE, set_values[5]) + pygame.display.gl_set_attribute(pygame.GL_ACCUM_ALPHA_SIZE, set_values[6]) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, set_values[7]) + pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, set_values[8]) + pygame.display.gl_set_attribute(pygame.GL_STEREO, set_values[9]) + + # We create a list where we store the values after getting them + get_values = [] + + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES)) + get_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO)) + + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(set_values)): + self.assertTrue(get_values[i] == set_values[i]) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + ( + "skipping for all because some failures on rasppi and maybe other platforms" + or os.environ.get("SDL_VIDEODRIVER") == "dummy" + ), + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_gl_set_attribute_kwargs(self): + # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute: - self.fail() + # pygame.display.gl_set_attribute(flag, value): return None + # request an opengl display attribute for the display mode + # + # When calling pygame.display.set_mode() with the pygame.OPENGL flag, + # Pygame automatically handles setting the OpenGL attributes like + # color and doublebuffering. OpenGL offers several other attributes + # you may want control over. Pass one of these attributes as the flag, + # and its appropriate value. This must be called before + # pygame.display.set_mode() + # + # The OPENGL flags are; + # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, + # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, + # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO + + screen = display.set_mode((0, 0), pygame.OPENGL) + + # We create a list where we store the values that we set each flag to + set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0] + + # Setting the flags with values supposedly different from the original values + + # assign SDL1-supported values with gl_set_attribute + pygame.display.gl_set_attribute(flag=pygame.GL_ALPHA_SIZE, value=set_values[0]) + pygame.display.gl_set_attribute(flag=pygame.GL_DEPTH_SIZE, value=set_values[1]) + pygame.display.gl_set_attribute( + flag=pygame.GL_STENCIL_SIZE, value=set_values[2] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_RED_SIZE, value=set_values[3] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_GREEN_SIZE, value=set_values[4] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_BLUE_SIZE, value=set_values[5] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_ACCUM_ALPHA_SIZE, value=set_values[6] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_MULTISAMPLEBUFFERS, value=set_values[7] + ) + pygame.display.gl_set_attribute( + flag=pygame.GL_MULTISAMPLESAMPLES, value=set_values[8] + ) + pygame.display.gl_set_attribute(flag=pygame.GL_STEREO, value=set_values[9]) - def todo_test_gl_get_attribute(self): + # We create a list where we store the values after getting them + get_values = [] - # __doc__ (as of 2008-08-02) for pygame.display.gl_get_attribute: + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE)) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE)) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS) + ) + get_values.append( + pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES) + ) + get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO)) + + # We check to see if the values that we get correspond to the values that we set + # them to or to the original values. + for i in range(len(set_values)): + self.assertTrue(get_values[i] == set_values[i]) + + # test using non-flag argument + with self.assertRaises(TypeError): + pygame.display.gl_get_attribute("DUMMY") + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") in ["dummy", "android"], + "iconify is only supported on some video drivers/platforms", + ) + def test_iconify(self): + pygame.display.set_mode((640, 480)) + + self.assertEqual(pygame.display.get_active(), True) + + success = pygame.display.iconify() + + if success: + active_event = window_minimized_event = False + # make sure we cycle the event loop enough to get the display + # hidden. Test that both ACTIVEEVENT and WINDOWMINIMISED event appears + for _ in range(50): + time.sleep(0.01) + for event in pygame.event.get(): + if event.type == pygame.ACTIVEEVENT: + if not event.gain and event.state == pygame.APPACTIVE: + active_event = True + if event.type == pygame.WINDOWMINIMIZED: + window_minimized_event = True + + self.assertTrue(window_minimized_event) + self.assertTrue(active_event) + self.assertFalse(pygame.display.get_active()) - # pygame.display.gl_get_attribute(flag): return value - # get the value for an opengl flag for the current display - # - # After calling pygame.display.set_mode() with the pygame.OPENGL flag, - # it is a good idea to check the value of any requested OpenGL - # attributes. See pygame.display.gl_set_attribute() for a list of - # valid flags. - # + else: + self.fail("Iconify not supported on this platform, please skip") - self.fail() + def test_init(self): + """Ensures the module is initialized after init called.""" + # display.init() already called in setUp(), so quit and re-init + display.quit() + display.init() - def todo_test_gl_set_attribute(self): + self.assertTrue(display.get_init()) - # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute: + def test_init__multiple(self): + """Ensures the module is initialized after multiple init calls.""" + display.init() + display.init() - # pygame.display.gl_set_attribute(flag, value): return None - # request an opengl display attribute for the display mode - # - # When calling pygame.display.set_mode() with the pygame.OPENGL flag, - # Pygame automatically handles setting the OpenGL attributes like - # color and doublebuffering. OpenGL offers several other attributes - # you may want control over. Pass one of these attributes as the flag, - # and its appropriate value. This must be called before - # pygame.display.set_mode() - # - # The OPENGL flags are; - # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE, - # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE, - # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO - - self.fail() - - def todo_test_iconify(self): - - # __doc__ (as of 2008-08-02) for pygame.display.iconify: - - # pygame.display.iconify(): return bool - # iconify the display surface - # - # Request the window for the display surface be iconified or hidden. - # Not all systems and displays support an iconified display. The - # function will return True if successfull. - # - # When the display is iconified pygame.display.get_active() will - # return False. The event queue should receive a ACTIVEEVENT event - # when the window has been iconified. - # - - self.fail() - - def todo_test_init(self): - - # __doc__ (as of 2008-08-02) for pygame.display.init: - - # pygame.display.init(): return None - # initialize the display module - # - # Initializes the pygame display module. The display module cannot do - # anything until it is initialized. This is usually handled for you - # automatically when you call the higher level pygame.init(). - # - # Pygame will select from one of several internal display backends - # when it is initialized. The display mode will be chosen depending on - # the platform and permissions of current user. Before the display - # module is initialized the environment variable SDL_VIDEODRIVER can - # be set to control which backend is used. The systems with multiple - # choices are listed here. - # - # Windows : windib, directx - # Unix : x11, dga, fbcon, directfb, ggi, vgl, svgalib, aalib - # On some platforms it is possible to embed the pygame display into an - # already existing window. To do this, the environment variable - # SDL_WINDOWID must be set to a string containing the window id or - # handle. The environment variable is checked when the pygame display - # is initialized. Be aware that there can be many strange side effects - # when running in an embedded display. - # - # It is harmless to call this more than once, repeated calls have no effect. - - self.fail() + self.assertTrue(display.get_init()) def test_list_modes(self): - modes = pygame.display.list_modes( - depth=0, flags=pygame.FULLSCREEN, display=0 - ) + modes = pygame.display.list_modes(depth=0, flags=pygame.FULLSCREEN, display=0) # modes == -1 means any mode is supported. if modes != -1: self.assertEqual(len(modes[0]), 2) @@ -297,10 +707,9 @@ class DisplayModuleTest(unittest.TestCase): if modes != -1: self.assertEqual(len(modes[0]), 2) self.assertEqual(type(modes[0][0]), int) + self.assertEqual(len(modes), len(set(modes))) - modes = pygame.display.list_modes( - depth=0, flags=0, display=0 - ) + modes = pygame.display.list_modes(depth=0, flags=0, display=0) if modes != -1: self.assertEqual(len(modes[0]), 2) self.assertEqual(type(modes[0][0]), int) @@ -315,126 +724,476 @@ class DisplayModuleTest(unittest.TestCase): pygame.display.mode_ok((128, 128), 0, 32) pygame.display.mode_ok((128, 128), flags=0, depth=32, display=0) - def test_mode_ok_fullscreen(self): modes = pygame.display.list_modes() if modes != -1: size = modes[0] - self.assertNotEqual(pygame.display.mode_ok( - size, - flags=pygame.FULLSCREEN), 0) + self.assertNotEqual( + pygame.display.mode_ok(size, flags=pygame.FULLSCREEN), 0 + ) + + def test_mode_ok_scaled(self): + modes = pygame.display.list_modes() + if modes != -1: + size = modes[0] + self.assertNotEqual(pygame.display.mode_ok(size, flags=pygame.SCALED), 0) def test_get_num_displays(self): self.assertGreater(pygame.display.get_num_displays(), 0) - def todo_test_quit(self): + def test_quit(self): + """Ensures the module is not initialized after quit called.""" + display.quit() - # __doc__ (as of 2008-08-02) for pygame.display.quit: + self.assertFalse(display.get_init()) - # pygame.display.quit(): return None - # uninitialize the display module - # - # This will shut down the entire display module. This means any active - # displays will be closed. This will also be handled automatically - # when the program exits. - # - # It is harmless to call this more than once, repeated calls have no effect. + def test_quit__multiple(self): + """Ensures the module is not initialized after multiple quit calls.""" + display.quit() + display.quit() - self.fail() + self.assertFalse(display.get_init()) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", "Needs a not dummy videodriver" + ) + def test_set_gamma(self): + pygame.display.set_mode((1, 1)) + + gammas = [0.25, 0.5, 0.88, 1.0] + for gamma in gammas: + with self.subTest(gamma=gamma): + with self.assertWarns(DeprecationWarning): + self.assertEqual(pygame.display.set_gamma(gamma), True) + self.assertEqual(pygame.display.set_gamma(gamma), True) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", "Needs a not dummy videodriver" + ) + def test_set_gamma__tuple(self): + pygame.display.set_mode((1, 1)) + + gammas = [(0.5, 0.5, 0.5), (1.0, 1.0, 1.0), (0.25, 0.33, 0.44)] + for r, g, b in gammas: + with self.subTest(r=r, g=g, b=b): + self.assertEqual(pygame.display.set_gamma(r, g, b), True) + + @unittest.skipIf( + not hasattr(pygame.display, "set_gamma_ramp"), + "Not all systems and hardware support gamma ramps", + ) + def test_set_gamma_ramp(self): + # __doc__ (as of 2008-08-02) for pygame.display.set_gamma_ramp: - def todo_test_set_gamma(self): + # change the hardware gamma ramps with a custom lookup + # pygame.display.set_gamma_ramp(red, green, blue): return bool + # set_gamma_ramp(red, green, blue): return bool + # + # Set the red, green, and blue gamma ramps with an explicit lookup + # table. Each argument should be sequence of 256 integers. The + # integers should range between 0 and 0xffff. Not all systems and + # hardware support gamma ramps, if the function succeeds it will + # return True. + # + pygame.display.set_mode((5, 5)) + r = list(range(256)) + g = [number + 256 for number in r] + b = [number + 256 for number in g] + with self.assertWarns(DeprecationWarning): + isSupported = pygame.display.set_gamma_ramp(r, g, b) + if isSupported: + self.assertTrue(pygame.display.set_gamma_ramp(r, g, b)) + else: + self.assertFalse(pygame.display.set_gamma_ramp(r, g, b)) - # __doc__ (as of 2008-08-02) for pygame.display.set_gamma: + def test_set_mode_kwargs(self): + pygame.display.set_mode(size=(1, 1), flags=0, depth=0, display=0) - # pygame.display.set_gamma(red, green=None, blue=None): return bool - # change the hardware gamma ramps - # - # Set the red, green, and blue gamma values on the display hardware. - # If the green and blue arguments are not passed, they will both be - # the same as red. Not all systems and hardware support gamma ramps, - # if the function succeeds it will return True. - # - # A gamma value of 1.0 creates a linear color table. Lower values will - # darken the display and higher values will brighten. - # + def test_set_mode_scaled(self): + surf = pygame.display.set_mode( + size=(1, 1), flags=pygame.SCALED, depth=0, display=0 + ) + winsize = pygame.display.get_window_size() + self.assertEqual( + winsize[0] % surf.get_size()[0], + 0, + "window width should be a multiple of the surface width", + ) + self.assertEqual( + winsize[1] % surf.get_size()[1], + 0, + "window height should be a multiple of the surface height", + ) + self.assertEqual( + winsize[0] / surf.get_size()[0], winsize[1] / surf.get_size()[1] + ) - self.fail() + def test_set_mode_vector2(self): + pygame.display.set_mode(pygame.Vector2(1, 1)) + + def test_set_mode_unscaled(self): + """Ensures a window created with SCALED can become smaller.""" + # see https://github.com/pygame/pygame/issues/2327 + + screen = pygame.display.set_mode((300, 300), pygame.SCALED) + self.assertEqual(screen.get_size(), (300, 300)) + + screen = pygame.display.set_mode((200, 200)) + self.assertEqual(screen.get_size(), (200, 200)) + + def test_screensaver_support(self): + pygame.display.set_allow_screensaver(True) + self.assertTrue(pygame.display.get_allow_screensaver()) + pygame.display.set_allow_screensaver(False) + self.assertFalse(pygame.display.get_allow_screensaver()) + pygame.display.set_allow_screensaver() + self.assertTrue(pygame.display.get_allow_screensaver()) + + # the following test fails always with SDL2 + @unittest.skipIf(True, "set_palette() not supported in SDL2") + def test_set_palette(self): + with self.assertRaises(pygame.error): + palette = [1, 2, 3] + pygame.display.set_palette(palette) + pygame.display.set_mode((1024, 768), 0, 8) + palette = [] + self.assertIsNone(pygame.display.set_palette(palette)) + + with self.assertRaises(ValueError): + palette = 12 + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [[1, 2], [1, 2]] + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [[0, 0, 0, 0, 0]] + [[x, x, x, x, x] for x in range(1, 255)] + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = "qwerty" + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [[123, 123, 123] * 10000] + pygame.display.set_palette(palette) + with self.assertRaises(TypeError): + palette = [1, 2, 3] + pygame.display.set_palette(palette) + + skip_list = ["dummy", "android"] + + @unittest.skipIf(True, "set_palette() not supported in SDL2") + def test_set_palette_kwargs(self): + with self.assertRaises(pygame.error): + palette = [1, 2, 3] + pygame.display.set_palette(palette=palette) + pygame.display.set_mode((1024, 768), 0, 8) + palette = [] + self.assertIsNone(pygame.display.set_palette(palette=palette)) + + with self.assertRaises(ValueError): + palette = 12 + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [[1, 2], [1, 2]] + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [[0, 0, 0, 0, 0]] + [[x, x, x, x, x] for x in range(1, 255)] + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = "qwerty" + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [[123, 123, 123] * 10000] + pygame.display.set_palette(palette=palette) + with self.assertRaises(TypeError): + palette = [1, 2, 3] + pygame.display.set_palette(palette=palette) + + skip_list = ["dummy", "android"] + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") in skip_list, + "requires the SDL_VIDEODRIVER to be non dummy", + ) + def test_toggle_fullscreen(self): + """Test for toggle fullscreen""" + + # try to toggle fullscreen with no active display + # this should result in an error + pygame.display.quit() + with self.assertRaises(pygame.error): + pygame.display.toggle_fullscreen() + + pygame.display.init() + width_height = (640, 480) + test_surf = pygame.display.set_mode(width_height) + + # try to toggle fullscreen + try: + pygame.display.toggle_fullscreen() + + except pygame.error: + self.fail() - def todo_test_set_gamma_ramp(self): + else: + # if toggle success, the width/height should be a + # value found in list_modes + if pygame.display.toggle_fullscreen() == 1: + boolean = ( + test_surf.get_width(), + test_surf.get_height(), + ) in pygame.display.list_modes( + depth=0, flags=pygame.FULLSCREEN, display=0 + ) - # __doc__ (as of 2008-08-02) for pygame.display.set_gamma_ramp: + self.assertEqual(boolean, True) - # change the hardware gamma ramps with a custom lookup - # pygame.display.set_gamma_ramp(red, green, blue): return bool - # set_gamma_ramp(red, green, blue): return bool - # - # Set the red, green, and blue gamma ramps with an explicit lookup - # table. Each argument should be sequence of 256 integers. The - # integers should range between 0 and 0xffff. Not all systems and - # hardware support gamma ramps, if the function succeeds it will - # return True. - # - - self.fail() - - def todo_test_set_icon(self): - - # __doc__ (as of 2008-08-02) for pygame.display.set_icon: - - # pygame.display.set_icon(Surface): return None - # change the system image for the display window - # - # Sets the runtime icon the system will use to represent the display - # window. All windows default to a simple pygame logo for the window - # icon. - # - # You can pass any surface, but most systems want a smaller image - # around 32x32. The image can have colorkey transparency which will be - # passed to the system. - # - # Some systems do not allow the window icon to change after it has - # been shown. This function can be called before - # pygame.display.set_mode() to create the icon before the display mode - # is set. - # - - self.fail() + # if not original width/height should be preserved + else: + self.assertEqual( + (test_surf.get_width(), test_surf.get_height()), width_height + ) - def test_set_mode_kwargs(self): - pygame.display.set_mode(size=(1, 1), flags=0, depth=0, display=0) +class DisplayUpdateTest(unittest.TestCase): + def question(self, qstr): + """this is used in the interactive subclass.""" + def setUp(self): + display.init() + self.screen = pygame.display.set_mode((500, 500)) + self.screen.fill("black") + pygame.display.flip() + pygame.event.pump() # so mac updates + + def tearDown(self): + display.quit() + + def test_update_negative(self): + """takes rects with negative values.""" + self.screen.fill("green") + + r1 = pygame.Rect(0, 0, 100, 100) + pygame.display.update(r1) + + r2 = pygame.Rect(-10, 0, 100, 100) + pygame.display.update(r2) + + r3 = pygame.Rect(-10, 0, -100, -100) + pygame.display.update(r3) + + self.question("Is the screen green in (0, 0, 100, 100)?") + + def test_update_sequence(self): + """only updates the part of the display given by the rects.""" + self.screen.fill("green") + rects = [ + pygame.Rect(0, 0, 100, 100), + pygame.Rect(100, 0, 100, 100), + pygame.Rect(200, 0, 100, 100), + pygame.Rect(300, 300, 100, 100), + ] + pygame.display.update(rects) + pygame.event.pump() # so mac updates + + self.question(f"Is the screen green in {rects}?") + + def test_update_none_skipped(self): + """None is skipped inside sequences.""" + self.screen.fill("green") + rects = ( + None, + pygame.Rect(100, 0, 100, 100), + None, + pygame.Rect(200, 0, 100, 100), + pygame.Rect(300, 300, 100, 100), + ) + pygame.display.update(rects) + pygame.event.pump() # so mac updates + + self.question(f"Is the screen green in {rects}?") + + def test_update_none(self): + """does NOT update the display.""" + self.screen.fill("green") + pygame.display.update(None) + pygame.event.pump() # so mac updates + self.question(f"Is the screen black and NOT green?") + + def test_update_no_args(self): + """does NOT update the display.""" + self.screen.fill("green") + pygame.display.update() + pygame.event.pump() # so mac updates + self.question(f"Is the WHOLE screen green?") + + def test_update_args(self): + """updates the display using the args as a rect.""" + self.screen.fill("green") + pygame.display.update(100, 100, 100, 100) + pygame.event.pump() # so mac updates + self.question("Is the screen green in (100, 100, 100, 100)?") + + def test_update_incorrect_args(self): + """raises a ValueError when inputs are wrong.""" + + with self.assertRaises(ValueError): + pygame.display.update(100, "asdf", 100, 100) + + with self.assertRaises(ValueError): + pygame.display.update([100, "asdf", 100, 100]) + + def test_update_no_init(self): + """raises a pygame.error.""" + + pygame.display.quit() + with self.assertRaises(pygame.error): + pygame.display.update() - def todo_test_set_palette(self): - # __doc__ (as of 2008-08-02) for pygame.display.set_palette: +class DisplayUpdateInteractiveTest(DisplayUpdateTest): + """Because we want these tests to run as interactive and not interactive.""" - # pygame.display.set_palette(palette=None): return None - # set the display color palette for indexed displays - # - # This will change the video display color palette for 8bit displays. - # This does not change the palette for the actual display Surface, - # only the palette that is used to display the Surface. If no palette - # argument is passed, the system default palette will be restored. The - # palette is a sequence of RGB triplets. - # + __tags__ = ["interactive"] - self.fail() + def question(self, qstr): + """since this is the interactive sublcass we ask a question.""" + question(qstr) - def todo_test_toggle_fullscreen(self): - # __doc__ (as of 2008-08-02) for pygame.display.toggle_fullscreen: +class DisplayInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + def test_set_icon_interactive(self): + os.environ["SDL_VIDEO_WINDOW_POS"] = "100,250" + pygame.display.quit() + pygame.display.init() + + test_icon = pygame.Surface((32, 32)) + test_icon.fill((255, 0, 0)) + + pygame.display.set_icon(test_icon) + screen = pygame.display.set_mode((400, 100)) + pygame.display.set_caption("Is the window icon a red square?") + + response = question("Is the display icon red square?") + + self.assertTrue(response) + pygame.display.quit() + + def test_set_gamma_ramp(self): + os.environ["SDL_VIDEO_WINDOW_POS"] = "100,250" + pygame.display.quit() + pygame.display.init() + + screen = pygame.display.set_mode((400, 100)) + screen.fill((100, 100, 100)) + + blue_ramp = [x * 256 for x in range(0, 256)] + blue_ramp[100] = 150 * 256 # Can't tint too far or gamma ramps fail + normal_ramp = [x * 256 for x in range(0, 256)] + # test to see if this platform supports gamma ramps + gamma_success = False + if pygame.display.set_gamma_ramp(normal_ramp, normal_ramp, blue_ramp): + pygame.display.update() + gamma_success = True + + if gamma_success: + response = question("Is the window background tinted blue?") + self.assertTrue(response) + # restore normal ramp + pygame.display.set_gamma_ramp(normal_ramp, normal_ramp, normal_ramp) + + pygame.display.quit() + + +class FullscreenToggleTests(unittest.TestCase): + __tags__ = ["interactive"] + + screen = None + font = None + isfullscreen = False + + WIDTH = 800 + HEIGHT = 600 + + def setUp(self): + pygame.init() + if sys.platform == "win32": + # known issue with windows, must have mode from pygame.display.list_modes() + # or window created with flag pygame.SCALED + self.screen = pygame.display.set_mode( + (self.WIDTH, self.HEIGHT), flags=pygame.SCALED + ) + else: + self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT)) + pygame.display.set_caption("Fullscreen Tests") + self.screen.fill((255, 255, 255)) + pygame.display.flip() + self.font = pygame.font.Font(None, 32) + + def tearDown(self): + if self.isfullscreen: + pygame.display.toggle_fullscreen() + pygame.quit() + + def visual_test(self, fullscreen=False): + text = "" + if fullscreen: + if not self.isfullscreen: + pygame.display.toggle_fullscreen() + self.isfullscreen = True + text = "Is this in fullscreen? [y/n]" + else: + if self.isfullscreen: + pygame.display.toggle_fullscreen() + self.isfullscreen = False + text = "Is this not in fullscreen [y/n]" + s = self.font.render(text, False, (0, 0, 0)) + self.screen.blit(s, (self.WIDTH / 2 - self.font.size(text)[0] / 2, 100)) + pygame.display.flip() + + while True: + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + return False + if event.key == pygame.K_y: + return True + if event.key == pygame.K_n: + return False + if event.type == pygame.QUIT: + return False + + def test_fullscreen_true(self): + self.assertTrue(self.visual_test(fullscreen=True)) + + def test_fullscreen_false(self): + self.assertTrue(self.visual_test(fullscreen=False)) + + +@unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', +) +class DisplayOpenGLTest(unittest.TestCase): + def test_screen_size_opengl(self): + """returns a surface with the same size requested. + |tags:display,slow,opengl| + """ + pygame.display.init() + screen = pygame.display.set_mode((640, 480), pygame.OPENGL) + self.assertEqual((640, 480), screen.get_size()) + - # pygame.display.toggle_fullscreen(): return bool - # switch between fullscreen and windowed displays - # - # Switches the display window between windowed and fullscreen modes. - # This function only works under the unix x11 video driver. For most - # situations it is better to call pygame.display.set_mode() with new - # display flags. - # +class X11CrashTest(unittest.TestCase): + def test_x11_set_mode_crash_gh1654(self): + # Test for https://github.com/pygame/pygame/issues/1654 + # If unfixed, this will trip a segmentation fault + pygame.display.init() + pygame.display.quit() + screen = pygame.display.set_mode((640, 480), 0) + self.assertEqual((640, 480), screen.get_size()) - self.fail() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/draw_test.py b/venv/Lib/site-packages/pygame/tests/draw_test.py index 42387dbe5705ad8bb656d3bded8c64698ffa670f..1ee59ddfa35f4f1fa88c0213b3a18a7a3af286a1 100644 --- a/venv/Lib/site-packages/pygame/tests/draw_test.py +++ b/venv/Lib/site-packages/pygame/tests/draw_test.py @@ -1,16 +1,31 @@ +import math import unittest import sys +import warnings import pygame from pygame import draw from pygame import draw_py from pygame.locals import SRCALPHA from pygame.tests import test_utils +from pygame.math import Vector2 -PY3 = sys.version_info >= (3, 0, 0) -RED = BG_RED = pygame.Color('red') -GREEN = FG_GREEN = pygame.Color('green') +RED = BG_RED = pygame.Color("red") +GREEN = FG_GREEN = pygame.Color("green") + +# Clockwise from the top left corner and ending with the center point. +RECT_POSITION_ATTRIBUTES = ( + "topleft", + "midtop", + "topright", + "midright", + "bottomright", + "midbottom", + "bottomleft", + "midleft", + "center", +) def get_border_values(surface, width, height): @@ -19,10 +34,8 @@ def get_border_values(surface, width, height): """ border_top = [surface.get_at((x, 0)) for x in range(width)] border_left = [surface.get_at((0, y)) for y in range(height)] - border_right = [ - surface.get_at((width - 1, y)) for y in range(height)] - border_bottom = [ - surface.get_at((x, height - 1)) for x in range(width)] + border_right = [surface.get_at((width - 1, y)) for y in range(height)] + border_bottom = [surface.get_at((x, height - 1)) for x in range(width)] return [border_top, border_left, border_right, border_bottom] @@ -36,6 +49,24 @@ def corners(surface): return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)) +def rect_corners_mids_and_center(rect): + """Returns a tuple with each corner, mid, and the center for a given rect. + + Clockwise from the top left corner and ending with the center point. + """ + return ( + rect.topleft, + rect.midtop, + rect.topright, + rect.midright, + rect.bottomright, + rect.midbottom, + rect.bottomleft, + rect.midleft, + rect.center, + ) + + def border_pos_and_color(surface): """Yields each border position and its color for a given surface. @@ -68,41 +99,398 @@ def border_pos_and_color(surface): yield pos, surface.get_at(pos) +def get_color_points(surface, color, bounds_rect=None, match_color=True): + """Get all the points of a given color on the surface within the given + bounds. + + If bounds_rect is None the full surface is checked. + If match_color is True, all points matching the color are returned, + otherwise all points not matching the color are returned. + """ + get_at = surface.get_at # For possible speed up. + + if bounds_rect is None: + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + else: + x_range = range(bounds_rect.left, bounds_rect.right) + y_range = range(bounds_rect.top, bounds_rect.bottom) + + surface.lock() # For possible speed up. + + if match_color: + pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) == color] + else: + pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) != color] + + surface.unlock() + return pts + + +def create_bounding_rect(surface, surf_color, default_pos): + """Create a rect to bound all the pixels that don't match surf_color. + + The default_pos parameter is used to position the bounding rect for the + case where all pixels match the surf_color. + """ + width, height = surface.get_clip().size + xmin, ymin = width, height + xmax, ymax = -1, -1 + get_at = surface.get_at # For possible speed up. + + surface.lock() # For possible speed up. + + for y in range(height): + for x in range(width): + if get_at((x, y)) != surf_color: + xmin = min(x, xmin) + xmax = max(x, xmax) + ymin = min(y, ymin) + ymax = max(y, ymax) + + surface.unlock() + + if -1 == xmax: + # No points means a 0 sized rect positioned at default_pos. + return pygame.Rect(default_pos, (0, 0)) + return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1)) + + +class InvalidBool: + """To help test invalid bool values.""" + + __bool__ = None + + class DrawTestCase(unittest.TestCase): """Base class to test draw module functions.""" - draw_rect = staticmethod(draw.rect) + + draw_rect = staticmethod(draw.rect) draw_polygon = staticmethod(draw.polygon) - draw_circle = staticmethod(draw.circle) + draw_circle = staticmethod(draw.circle) draw_ellipse = staticmethod(draw.ellipse) - draw_arc = staticmethod(draw.arc) - draw_line = staticmethod(draw.line) - draw_lines = staticmethod(draw.lines) - draw_aaline = staticmethod(draw.aaline) + draw_arc = staticmethod(draw.arc) + draw_line = staticmethod(draw.line) + draw_lines = staticmethod(draw.lines) + draw_aaline = staticmethod(draw.aaline) draw_aalines = staticmethod(draw.aalines) class PythonDrawTestCase(unittest.TestCase): """Base class to test draw_py module functions.""" + # draw_py is currently missing some functions. - #draw_rect = staticmethod(draw_py.draw_rect) + # draw_rect = staticmethod(draw_py.draw_rect) draw_polygon = staticmethod(draw_py.draw_polygon) - #draw_circle = staticmethod(draw_py.draw_circle) - #draw_ellipse = staticmethod(draw_py.draw_ellipse) - #draw_arc = staticmethod(draw_py.draw_arc) - draw_line = staticmethod(draw_py.draw_line) - draw_lines = staticmethod(draw_py.draw_lines) - draw_aaline = staticmethod(draw_py.draw_aaline) + # draw_circle = staticmethod(draw_py.draw_circle) + # draw_ellipse = staticmethod(draw_py.draw_ellipse) + # draw_arc = staticmethod(draw_py.draw_arc) + draw_line = staticmethod(draw_py.draw_line) + draw_lines = staticmethod(draw_py.draw_lines) + draw_aaline = staticmethod(draw_py.draw_aaline) draw_aalines = staticmethod(draw_py.draw_aalines) ### Ellipse Testing ########################################################### -class DrawEllipseMixin(object): + +class DrawEllipseMixin: """Mixin tests for drawing ellipses. This class contains all the general ellipse drawing tests. """ + def test_ellipse__args(self): + """Ensures draw ellipse accepts the correct args.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((0, 0), (3, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_without_width(self): + """Ensures draw ellipse accepts the args without a width.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((1, 1), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_with_negative_width(self): + """Ensures draw ellipse accepts the args with negative width.""" + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((2, 3), (3, 2)), -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(2, 3, 0, 0)) + + def test_ellipse__args_with_width_gt_radius(self): + """Ensures draw ellipse accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_ellipse( + pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__kwargs(self): + """Ensures draw ellipse accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__kwargs_order_independent(self): + """Ensures draw ellipse's kwargs are not order dependent.""" + bounds_rect = self.draw_ellipse( + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=0, + rect=pygame.Rect((1, 0), (1, 1)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__args_missing(self): + """Ensures draw ellipse detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(surface, pygame.Color("red")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse() + + def test_ellipse__kwargs_missing(self): + """Ensures draw ellipse detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "width": 2, + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**invalid_kwargs) + + def test_ellipse__arg_invalid_types(self): + """Ensures draw ellipse detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_ellipse(surface, color, rect, "1") + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_ellipse(surface, color, (1, 2, 3, 4, 5), 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_ellipse(surface, 2.3, rect, 0) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_ellipse(rect, color, rect, 2) + + def test_ellipse__kwarg_invalid_types(self): + """Ensures draw ellipse detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "width": 1, + }, + {"surface": surface, "color": color, "rect": rect, "width": 1.1}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse__kwarg_invalid_name(self): + """Ensures draw ellipse detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + + def test_ellipse__args_and_kwargs(self): + """Ensures draw ellipse accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 1)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_ellipse(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_ellipse(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_ellipse(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_ellipse(surface, color, rect, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_width_values(self): + """Ensures draw ellipse accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (3, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_rect_formats(self): + """Ensures draw ellipse accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = (pygame.Rect(pos, (1, 3)), (pos, (2, 1)), (pos[0], pos[1], 1, 1)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__valid_color_formats(self): + """Ensures draw ellipse accepts different color formats.""" + pos = (1, 1) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 2)), + "width": 0, + } + reds = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_ellipse(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_ellipse__invalid_color_formats(self): + """Ensures draw ellipse handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "width": 1, + } + + for expected_color in (2.3, surface): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_ellipse(**kwargs) + def test_ellipse(self): """Tests ellipses of differing sizes on surfaces of differing sizes. @@ -117,8 +505,7 @@ class DrawEllipseMixin(object): """Test for ellipses with the same size as the surface.""" surface = pygame.Surface((width, height)) - self.draw_ellipse(surface, color, (0, 0, width, height), - border_width) + self.draw_ellipse(surface, color, (0, 0, width, height), border_width) # For each of the four borders check if it contains the color borders = get_border_values(surface, width, height) @@ -129,14 +516,14 @@ class DrawEllipseMixin(object): """Test for ellipses that aren't the same size as the surface.""" surface = pygame.Surface((width, height)) - self.draw_ellipse(surface, color, - (left, top, width - 1, height - 1), border_width) + self.draw_ellipse( + surface, color, (left, top, width - 1, height - 1), border_width + ) borders = get_border_values(surface, width, height) # Check if two sides of the ellipse are touching the border - sides_touching = [ - color in border for border in borders].count(True) + sides_touching = [color in border for border in borders].count(True) self.assertEqual(sides_touching, 2) for width, height in sizes: @@ -145,8 +532,163 @@ class DrawEllipseMixin(object): for left, top in left_top: not_same_size(width, height, border_width, left, top) - def _check_1_pixel_sized_ellipse(self, surface, collide_rect, - surface_color, ellipse_color): + def test_ellipse__big_ellipse(self): + """Test for big ellipse that could overflow in algorithm""" + width = 1025 + height = 1025 + border = 1 + x_value_test = int(0.4 * height) + y_value_test = int(0.4 * height) + surface = pygame.Surface((width, height)) + + self.draw_ellipse(surface, (255, 0, 0), (0, 0, width, height), border) + colored_pixels = 0 + for y in range(height): + if surface.get_at((x_value_test, y)) == (255, 0, 0): + colored_pixels += 1 + for x in range(width): + if surface.get_at((x, y_value_test)) == (255, 0, 0): + colored_pixels += 1 + self.assertEqual(colored_pixels, border * 4) + + def test_ellipse__thick_line(self): + """Ensures a thick lined ellipse is drawn correctly.""" + ellipse_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((40, 40)) + rect = pygame.Rect((0, 0), (31, 23)) + rect.center = surface.get_rect().center + + # As the lines get thicker the internals of the ellipse are not + # cleanly defined. So only test up to a few thicknesses before the + # maximum thickness. + for thickness in range(1, min(*rect.size) // 2 - 2): + surface.fill(surface_color) # Clear for each test. + + self.draw_ellipse(surface, ellipse_color, rect, thickness) + + surface.lock() # For possible speed up. + + # Check vertical thickness on the ellipse's top. + x = rect.centerx + y_start = rect.top + y_end = rect.top + thickness - 1 + + for y in range(y_start, y_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels above and below this line. + self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness) + self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness) + + # Check vertical thickness on the ellipse's bottom. + x = rect.centerx + y_start = rect.bottom - thickness + y_end = rect.bottom - 1 + + for y in range(y_start, y_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels above and below this line. + self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness) + self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness) + + # Check horizontal thickness on the ellipse's left. + x_start = rect.left + x_end = rect.left + thickness - 1 + y = rect.centery + + for x in range(x_start, x_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels to the left and right of this line. + self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness) + self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness) + + # Check horizontal thickness on the ellipse's right. + x_start = rect.right - thickness + x_end = rect.right - 1 + y = rect.centery + + for x in range(x_start, x_end + 1): + self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness) + + # Check pixels to the left and right of this line. + self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness) + self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness) + + surface.unlock() + + def test_ellipse__no_holes(self): + width = 80 + height = 70 + surface = pygame.Surface((width + 1, height)) + rect = pygame.Rect(0, 0, width, height) + for thickness in range(1, 37, 5): + surface.fill("BLACK") + self.draw_ellipse(surface, "RED", rect, thickness) + for y in range(height): + number_of_changes = 0 + drawn_pixel = False + for x in range(width + 1): + if ( + not drawn_pixel + and surface.get_at((x, y)) == pygame.Color("RED") + or drawn_pixel + and surface.get_at((x, y)) == pygame.Color("BLACK") + ): + drawn_pixel = not drawn_pixel + number_of_changes += 1 + if y < thickness or y > height - thickness - 1: + self.assertEqual(number_of_changes, 2) + else: + self.assertEqual(number_of_changes, 4) + + def test_ellipse__max_width(self): + """Ensures an ellipse with max width (and greater) is drawn correctly.""" + ellipse_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((40, 40)) + rect = pygame.Rect((0, 0), (31, 21)) + rect.center = surface.get_rect().center + max_thickness = (min(*rect.size) + 1) // 2 + + for thickness in range(max_thickness, max_thickness + 3): + surface.fill(surface_color) # Clear for each test. + + self.draw_ellipse(surface, ellipse_color, rect, thickness) + + surface.lock() # For possible speed up. + + # Check vertical thickness. + for y in range(rect.top, rect.bottom): + self.assertEqual(surface.get_at((rect.centerx, y)), ellipse_color) + + # Check horizontal thickness. + for x in range(rect.left, rect.right): + self.assertEqual(surface.get_at((x, rect.centery)), ellipse_color) + + # Check pixels above and below ellipse. + self.assertEqual( + surface.get_at((rect.centerx, rect.top - 1)), surface_color + ) + self.assertEqual( + surface.get_at((rect.centerx, rect.bottom + 1)), surface_color + ) + + # Check pixels to the left and right of the ellipse. + self.assertEqual( + surface.get_at((rect.left - 1, rect.centery)), surface_color + ) + self.assertEqual( + surface.get_at((rect.right + 1, rect.centery)), surface_color + ) + + surface.unlock() + + def _check_1_pixel_sized_ellipse( + self, surface, collide_rect, surface_color, ellipse_color + ): # Helper method to check the surface for 1 pixel wide and/or high # ellipses. surf_w, surf_h = surface.get_size() @@ -161,8 +703,11 @@ class DrawEllipseMixin(object): else: expected_color = surface_color - self.assertEqual(surface.get_at(pos), expected_color, - 'collide_rect={}, pos={}'.format(collide_rect, pos)) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"collide_rect={collide_rect}, pos={pos}", + ) surface.unlock() @@ -171,8 +716,8 @@ class DrawEllipseMixin(object): An ellipse with a width of 1 pixel is a vertical line. """ - ellipse_color = pygame.Color('red') - surface_color = pygame.Color('black') + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") surf_w, surf_h = 10, 20 surface = pygame.Surface((surf_w, surf_h)) @@ -188,9 +733,7 @@ class DrawEllipseMixin(object): # Test some even and odd heights. for ellipse_h in range(6, 10): - # The ellipse is drawn on the edge of the rect so collide_rect - # needs +1 height to track where it's drawn. - collide_rect.h = ellipse_h + 1 + collide_rect.h = ellipse_h rect.h = ellipse_h # Calculate some variable positions. @@ -200,23 +743,23 @@ class DrawEllipseMixin(object): # Draw the ellipse in different positions: fully on-surface, # partially off-surface, and fully off-surface. - positions = ((off_left, off_top), - (off_left, half_off_top), - (off_left, center_y), - (off_left, half_off_bottom), - (off_left, off_bottom), - - (center_x, off_top), - (center_x, half_off_top), - (center_x, center_y), - (center_x, half_off_bottom), - (center_x, off_bottom), - - (off_right, off_top), - (off_right, half_off_top), - (off_right, center_y), - (off_right, half_off_bottom), - (off_right, off_bottom)) + positions = ( + (off_left, off_top), + (off_left, half_off_top), + (off_left, center_y), + (off_left, half_off_bottom), + (off_left, off_bottom), + (center_x, off_top), + (center_x, half_off_top), + (center_x, center_y), + (center_x, half_off_bottom), + (center_x, off_bottom), + (off_right, off_top), + (off_right, half_off_top), + (off_right, center_y), + (off_right, half_off_bottom), + (off_right, off_bottom), + ) for rect_pos in positions: surface.fill(surface_color) # Clear before each draw. @@ -225,8 +768,9 @@ class DrawEllipseMixin(object): self.draw_ellipse(surface, ellipse_color, rect) - self._check_1_pixel_sized_ellipse(surface, collide_rect, - surface_color, ellipse_color) + self._check_1_pixel_sized_ellipse( + surface, collide_rect, surface_color, ellipse_color + ) def test_ellipse__1_pixel_width_spanning_surface(self): """Ensures an ellipse with a width of 1 is drawn correctly @@ -234,19 +778,21 @@ class DrawEllipseMixin(object): An ellipse with a width of 1 pixel is a vertical line. """ - ellipse_color = pygame.Color('red') - surface_color = pygame.Color('black') + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") surf_w, surf_h = 10, 20 surface = pygame.Surface((surf_w, surf_h)) rect = pygame.Rect((0, 0), (1, surf_h + 2)) # Longer than the surface. # Draw the ellipse in different positions: on-surface and off-surface. - positions = ((-1, -1), # (off_left, off_top) - (0, -1), # (left_edge, off_top) - (surf_w // 2, -1), # (center_x, off_top) - (surf_w - 1, -1), # (right_edge, off_top) - (surf_w, -1)) # (off_right, off_top) + positions = ( + (-1, -1), # (off_left, off_top) + (0, -1), # (left_edge, off_top) + (surf_w // 2, -1), # (center_x, off_top) + (surf_w - 1, -1), # (right_edge, off_top) + (surf_w, -1), + ) # (off_right, off_top) for rect_pos in positions: surface.fill(surface_color) # Clear before each draw. @@ -254,16 +800,17 @@ class DrawEllipseMixin(object): self.draw_ellipse(surface, ellipse_color, rect) - self._check_1_pixel_sized_ellipse(surface, rect, surface_color, - ellipse_color) + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) def test_ellipse__1_pixel_height(self): """Ensures an ellipse with a height of 1 is drawn correctly. An ellipse with a height of 1 pixel is a horizontal line. """ - ellipse_color = pygame.Color('red') - surface_color = pygame.Color('black') + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") surf_w, surf_h = 20, 10 surface = pygame.Surface((surf_w, surf_h)) @@ -279,9 +826,7 @@ class DrawEllipseMixin(object): # Test some even and odd widths. for ellipse_w in range(6, 10): - # The ellipse is drawn on the edge of the rect so collide_rect - # needs +1 width to track where it's drawn. - collide_rect.w = ellipse_w + 1 + collide_rect.w = ellipse_w rect.w = ellipse_w # Calculate some variable positions. @@ -291,23 +836,23 @@ class DrawEllipseMixin(object): # Draw the ellipse in different positions: fully on-surface, # partially off-surface, and fully off-surface. - positions = ((off_left, off_top), - (half_off_left, off_top), - (center_x, off_top), - (half_off_right, off_top), - (off_right, off_top), - - (off_left, center_y), - (half_off_left, center_y), - (center_x, center_y), - (half_off_right, center_y), - (off_right, center_y), - - (off_left, off_bottom), - (half_off_left, off_bottom), - (center_x, off_bottom), - (half_off_right, off_bottom), - (off_right, off_bottom)) + positions = ( + (off_left, off_top), + (half_off_left, off_top), + (center_x, off_top), + (half_off_right, off_top), + (off_right, off_top), + (off_left, center_y), + (half_off_left, center_y), + (center_x, center_y), + (half_off_right, center_y), + (off_right, center_y), + (off_left, off_bottom), + (half_off_left, off_bottom), + (center_x, off_bottom), + (half_off_right, off_bottom), + (off_right, off_bottom), + ) for rect_pos in positions: surface.fill(surface_color) # Clear before each draw. @@ -316,8 +861,9 @@ class DrawEllipseMixin(object): self.draw_ellipse(surface, ellipse_color, rect) - self._check_1_pixel_sized_ellipse(surface, collide_rect, - surface_color, ellipse_color) + self._check_1_pixel_sized_ellipse( + surface, collide_rect, surface_color, ellipse_color + ) def test_ellipse__1_pixel_height_spanning_surface(self): """Ensures an ellipse with a height of 1 is drawn correctly @@ -325,19 +871,21 @@ class DrawEllipseMixin(object): An ellipse with a height of 1 pixel is a horizontal line. """ - ellipse_color = pygame.Color('red') - surface_color = pygame.Color('black') + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") surf_w, surf_h = 20, 10 surface = pygame.Surface((surf_w, surf_h)) rect = pygame.Rect((0, 0), (surf_w + 2, 1)) # Wider than the surface. # Draw the ellipse in different positions: on-surface and off-surface. - positions = ((-1, -1), # (off_left, off_top) - (-1, 0), # (off_left, top_edge) - (-1, surf_h // 2), # (off_left, center_y) - (-1, surf_h - 1), # (off_left, bottom_edge) - (-1, surf_h)) # (off_left, off_bottom) + positions = ( + (-1, -1), # (off_left, off_top) + (-1, 0), # (off_left, top_edge) + (-1, surf_h // 2), # (off_left, center_y) + (-1, surf_h - 1), # (off_left, bottom_edge) + (-1, surf_h), + ) # (off_left, off_bottom) for rect_pos in positions: surface.fill(surface_color) # Clear before each draw. @@ -345,16 +893,17 @@ class DrawEllipseMixin(object): self.draw_ellipse(surface, ellipse_color, rect) - self._check_1_pixel_sized_ellipse(surface, rect, surface_color, - ellipse_color) + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) def test_ellipse__1_pixel_width_and_height(self): """Ensures an ellipse with a width and height of 1 is drawn correctly. An ellipse with a width and height of 1 pixel is a single pixel. """ - ellipse_color = pygame.Color('red') - surface_color = pygame.Color('black') + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("black") surf_w, surf_h = 10, 10 surface = pygame.Surface((surf_w, surf_h)) @@ -374,35 +923,33 @@ class DrawEllipseMixin(object): # Draw the ellipse in different positions: center surface, # top/bottom/left/right edges, and off-surface. - positions = ((off_left, off_top), - (off_left, top_edge), - (off_left, center_y), - (off_left, bottom_edge), - (off_left, off_bottom), - - (left_edge, off_top), - (left_edge, top_edge), - (left_edge, center_y), - (left_edge, bottom_edge), - (left_edge, off_bottom), - - (center_x, off_top), - (center_x, top_edge), - (center_x, center_y), - (center_x, bottom_edge), - (center_x, off_bottom), - - (right_edge, off_top), - (right_edge, top_edge), - (right_edge, center_y), - (right_edge, bottom_edge), - (right_edge, off_bottom), - - (off_right, off_top), - (off_right, top_edge), - (off_right, center_y), - (off_right, bottom_edge), - (off_right, off_bottom)) + positions = ( + (off_left, off_top), + (off_left, top_edge), + (off_left, center_y), + (off_left, bottom_edge), + (off_left, off_bottom), + (left_edge, off_top), + (left_edge, top_edge), + (left_edge, center_y), + (left_edge, bottom_edge), + (left_edge, off_bottom), + (center_x, off_top), + (center_x, top_edge), + (center_x, center_y), + (center_x, bottom_edge), + (center_x, off_bottom), + (right_edge, off_top), + (right_edge, top_edge), + (right_edge, center_y), + (right_edge, bottom_edge), + (right_edge, off_bottom), + (off_right, off_top), + (off_right, top_edge), + (off_right, center_y), + (off_right, bottom_edge), + (off_right, off_bottom), + ) for rect_pos in positions: surface.fill(surface_color) # Clear before each draw. @@ -410,8 +957,99 @@ class DrawEllipseMixin(object): self.draw_ellipse(surface, ellipse_color, rect) - self._check_1_pixel_sized_ellipse(surface, rect, surface_color, - ellipse_color) + self._check_1_pixel_sized_ellipse( + surface, rect, surface_color, ellipse_color + ) + + def test_ellipse__bounding_rect(self): + """Ensures draw ellipse returns the correct bounding rect. + + Tests ellipses on and off the surface and a range of width/thickness + values. + """ + ellipse_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # ellipses off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the ellipse's rect position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + ellipse_rect = pygame.Rect((0, 0), (width, height)) + setattr(ellipse_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_ellipse( + surface, ellipse_color, ellipse_rect, thickness + ) + + # Calculating the expected_rect after the ellipse + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, ellipse_rect.topleft + ) + + self.assertEqual(bounding_rect, expected_rect) + + def test_ellipse__surface_clip(self): + """Ensures draw ellipse respects a surface's clip area. + + Tests drawing the ellipse filled and unfilled. + """ + surfw = surfh = 30 + ellipse_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the ellipse's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the ellipse along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the ellipse without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_ellipse(surface, ellipse_color, pos_rect, width) + expected_pts = get_color_points(surface, ellipse_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the ellipse + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_ellipse(surface, ellipse_color, pos_rect, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the ellipse_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = ellipse_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() class DrawEllipseTest(DrawEllipseMixin, DrawTestCase): @@ -422,283 +1060,1890 @@ class DrawEllipseTest(DrawEllipseMixin, DrawTestCase): """ -@unittest.skip('draw_py.draw_ellipse not supported yet') -class PythonDrawEllipseTest(DrawEllipseMixin, PythonDrawTestCase): - """Test draw_py module function draw_ellipse. +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing ellipses. +# @unittest.skip('draw_py.draw_ellipse not supported yet') +# class PythonDrawEllipseTest(DrawEllipseMixin, PythonDrawTestCase): +# """Test draw_py module function draw_ellipse. +# +# This class inherits the general tests from DrawEllipseMixin. It is also +# the class to add any draw_py.draw_ellipse specific tests to. +# """ - This class inherits the general tests from DrawEllipseMixin. It is also - the class to add any draw_py.draw_ellipse specific tests to. - """ +### Line/Lines/AALine/AALines Testing ######################################### -### Line Testing ############################################################## -class LineMixin(object): - """Mixin test for drawing lines and aalines. +class BaseLineMixin: + """Mixin base for drawing various lines. - This class contains all the general line/lines/aaline/aalines drawing - tests. + This class contains general helper methods and setup for testing the + different types of lines. """ - def setUp(self): - self._colors = ((0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), - (255, 255, 0), (255, 0, 255), (0, 255, 255), - (255, 255, 255)) - + COLORS = ( + (0, 0, 0), + (255, 0, 0), + (0, 255, 0), + (0, 0, 255), + (255, 255, 0), + (255, 0, 255), + (0, 255, 255), + (255, 255, 255), + ) + + @staticmethod + def _create_surfaces(): # Create some surfaces with different sizes, depths, and flags. - self._surfaces = [] + surfaces = [] for size in ((49, 49), (50, 50)): for depth in (8, 16, 24, 32): for flags in (0, SRCALPHA): surface = pygame.display.set_mode(size, flags, depth) - self._surfaces.append(surface) - self._surfaces.append(surface.convert_alpha()) + surfaces.append(surface) + surfaces.append(surface.convert_alpha()) + return surfaces + + @staticmethod + def _rect_lines(rect): + # Yields pairs of end points and their reverse (to test symmetry). + # Uses a rect with the points radiating from its midleft. + for pt in rect_corners_mids_and_center(rect): + if pt in [rect.midleft, rect.center]: + # Don't bother with these points. + continue + yield (rect.midleft, pt) + yield (pt, rect.midleft) - def test_line__color(self): - """Tests if the line drawn is the correct color.""" - pos = (0, 0) - for surface in self._surfaces: - for expected_color in self._colors: - self.draw_line(surface, expected_color, pos, (1, 0)) - self.assertEqual(surface.get_at(pos), expected_color, - 'pos={}'.format(pos)) +### Line Testing ############################################################## - def test_aaline__color(self): - """Tests if the aaline drawn is the correct color.""" - pos = (0, 0) - for surface in self._surfaces: - for expected_color in self._colors: - self.draw_aaline(surface, expected_color, pos, (1, 0)) - self.assertEqual(surface.get_at(pos), expected_color, - 'pos={}'.format(pos)) +class LineMixin(BaseLineMixin): + """Mixin test for drawing a single line. - def test_line__gaps(self): - """Tests if the line drawn contains any gaps.""" - expected_color = (255, 255, 255) - for surface in self._surfaces: - width = surface.get_width() - self.draw_line(surface, expected_color, (0, 0), (width - 1, 0)) + This class contains all the general single line drawing tests. + """ - for x in range(width): - pos = (x, 0) - self.assertEqual(surface.get_at(pos), expected_color, - 'pos={}'.format(pos)) + def test_line__args(self): + """Ensures draw line accepts the correct args.""" + bounds_rect = self.draw_line( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1 + ) - def test_aaline__gaps(self): - """Tests if the aaline drawn contains any gaps. + self.assertIsInstance(bounds_rect, pygame.Rect) - See: #512 + def test_line__args_without_width(self): + """Ensures draw line accepts the args without a width.""" + bounds_rect = self.draw_line( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__kwargs(self): + """Ensures draw line accepts the correct kwargs + with and without a width arg. """ - expected_color = (255, 255, 255) - for surface in self._surfaces: - width = surface.get_width() - self.draw_aaline(surface, expected_color, (0, 0), (width - 1, 0)) + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + start_pos = (1, 1) + end_pos = (2, 2) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_line(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__kwargs_order_independent(self): + """Ensures draw line's kwargs are not order dependent.""" + bounds_rect = self.draw_line( + start_pos=(1, 2), + end_pos=(2, 1), + width=2, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__args_missing(self): + """Ensures draw line detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_line() + + def test_line__kwargs_missing(self): + """Ensures draw line detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "start_pos": (2, 1), + "end_pos": (2, 2), + "width": 1, + } + + for name in ("end_pos", "start_pos", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. - for x in range(width): - pos = (x, 0) - self.assertEqual(surface.get_at(pos), expected_color, - 'pos={}'.format(pos)) + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**invalid_kwargs) + + def test_line__arg_invalid_types(self): + """Ensures draw line detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + start_pos = (0, 1) + end_pos = (1, 2) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_line(surface, color, start_pos, end_pos, "1") + + with self.assertRaises(TypeError): + # Invalid end_pos. + bounds_rect = self.draw_line(surface, color, start_pos, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid start_pos. + bounds_rect = self.draw_line(surface, color, (1,), end_pos) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_line(surface, 2.3, start_pos, end_pos) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_line((1, 2, 3, 4), color, start_pos, end_pos) + + def test_line__kwarg_invalid_types(self): + """Ensures draw line detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + start_pos = (1, 0) + end_pos = (2, 0) + width = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": (0, 0, 0), # Invalid start_pos. + "end_pos": end_pos, + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), # Invalid end_pos. + "width": width, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1.2, + }, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__kwarg_invalid_name(self): + """Ensures draw line detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + start_pos = (1, 1) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__args_and_kwargs(self): + """Ensures draw line accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + start_pos = (0, 1) + end_pos = (1, 2) + width = 0 + kwargs = { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "width": width, + } + + for name in ("surface", "color", "start_pos", "end_pos", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_line(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_line(surface, color, **kwargs) + elif "start_pos" == name: + bounds_rect = self.draw_line(surface, color, start_pos, **kwargs) + elif "end_pos" == name: + bounds_rect = self.draw_line( + surface, color, start_pos, end_pos, **kwargs + ) + else: + bounds_rect = self.draw_line( + surface, color, start_pos, end_pos, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_width_values(self): + """Ensures draw line accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (2, 1) + kwargs = { + "surface": surface, + "color": line_color, + "start_pos": pos, + "end_pos": (2, 2), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_start_pos_formats(self): + """Ensures draw line accepts different start_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": None, + "end_pos": (2, 2), + "width": 2, + } + x, y = 2, 1 # start position + + # The point values can be ints or floats. + for start_pos in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)): + # The point type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["start_pos"] = seq_type(start_pos) + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__valid_end_pos_formats(self): + """Ensures draw line accepts different end_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": (2, 1), + "end_pos": None, + "width": 2, + } + x, y = 2, 2 # end position + + # The point values can be ints or floats. + for end_pos in ((x, y), (x + 0.2, y), (x, y + 0.2), (x + 0.2, y + 0.2)): + # The point type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["end_pos"] = seq_type(end_pos) + + bounds_rect = self.draw_line(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_line__invalid_start_pos_formats(self): + """Ensures draw line handles invalid start_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": None, + "end_pos": (2, 2), + "width": 1, + } + + start_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for start_pos in start_pos_fmts: + kwargs["start_pos"] = start_pos - def test_lines__color(self): - """Tests if the lines drawn are the correct color. + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__invalid_end_pos_formats(self): + """Ensures draw line handles invalid end_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": (2, 2), + "end_pos": None, + "width": 1, + } + + end_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for end_pos in end_pos_fmts: + kwargs["end_pos"] = end_pos - Draws lines around the border of the given surface and checks if all - borders of the surface only contain the given color. - """ - for surface in self._surfaces: - for expected_color in self._colors: - self.draw_lines(surface, expected_color, True, - corners(surface)) + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) + + def test_line__valid_color_formats(self): + """Ensures draw line accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "start_pos": pos, + "end_pos": (2, 1), + "width": 3, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color - for pos, color in border_pos_and_color(surface): - self.assertEqual(color, expected_color, - 'pos={}'.format(pos)) + bounds_rect = self.draw_line(**kwargs) - def test_aalines__color(self): - """Tests if the aalines drawn are the correct color. + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) - Draws aalines around the border of the given surface and checks if all - borders of the surface only contain the given color. - """ - for surface in self._surfaces: - for expected_color in self._colors: - self.draw_aalines(surface, expected_color, True, - corners(surface)) + def test_line__invalid_color_formats(self): + """Ensures draw line handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "start_pos": (1, 1), + "end_pos": (2, 1), + "width": 1, + } - for pos, color in border_pos_and_color(surface): - self.assertEqual(color, expected_color, - 'pos={}'.format(pos)) + for expected_color in (2.3, self): + kwargs["color"] = expected_color - def test_lines__gaps(self): - """Tests if the lines drawn contain any gaps. + with self.assertRaises(TypeError): + bounds_rect = self.draw_line(**kwargs) - Draws lines around the border of the given surface and checks if - all borders of the surface contain any gaps. - """ - expected_color = (255, 255, 255) - for surface in self._surfaces: - self.draw_lines(surface, expected_color, True, corners(surface)) + def test_line__color(self): + """Tests if the line drawn is the correct color.""" + pos = (0, 0) + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_line(surface, expected_color, pos, (1, 0)) - for pos, color in border_pos_and_color(surface): - self.assertEqual(color, expected_color, 'pos={}'.format(pos)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") - def test_aalines__gaps(self): - """Tests if the aalines drawn contain any gaps. + def test_line__color_with_thickness(self): + """Ensures a thick line is drawn using the correct color.""" + from_x = 5 + to_x = 10 + y = 5 + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_line(surface, expected_color, (from_x, y), (to_x, y), 5) + for pos in ((x, y + i) for i in (-2, 0, 2) for x in (from_x, to_x)): + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") - Draws aalines around the border of the given surface and checks if - all borders of the surface contain any gaps. + def test_line__gaps(self): + """Tests if the line drawn contains any gaps.""" + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + width = surface.get_width() + self.draw_line(surface, expected_color, (0, 0), (width - 1, 0)) - See: #512 - """ + for x in range(width): + pos = (x, 0) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_line__gaps_with_thickness(self): + """Ensures a thick line is drawn without any gaps.""" expected_color = (255, 255, 255) - for surface in self._surfaces: - self.draw_aalines(surface, expected_color, True, corners(surface)) + thickness = 5 + for surface in self._create_surfaces(): + width = surface.get_width() - 1 + h = width // 5 + w = h * 5 + self.draw_line(surface, expected_color, (0, 5), (w, 5 + h), thickness) + + for x in range(w + 1): + for y in range(3, 8): + pos = (x, y + ((x + 2) // 5)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_line__bounding_rect(self): + """Ensures draw line returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and a range of + width/thickness values. + """ + if isinstance(self, PythonDrawTestCase): + self.skipTest("bounding rects not supported in draw_py.draw_line") + + line_color = pygame.Color("red") + surf_color = pygame.Color("black") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + helper_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the helper_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move the helper rect to different positions to test line + # endpoints on and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + helper_rect.center = pos + + # Draw using different thicknesses. + for thickness in range(-1, 5): + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_line( + surface, line_color, start, end, thickness + ) + + if 0 < thickness: + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, start + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(start, (0, 0)) + + self.assertEqual( + bounding_rect, + expected_rect, + "start={}, end={}, size={}, thickness={}".format( + start, end, size, thickness + ), + ) + + def test_line__surface_clip(self): + """Ensures draw line respects a surface's clip area.""" + surfw = surfh = 30 + line_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the line's pos. + + for thickness in (1, 3): # Test different line widths. + # Test centering the line along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the line without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_line( + surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness + ) + expected_pts = get_color_points(surface, line_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the line + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_line( + surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness + ) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the line_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = line_color + else: + expected_color = surface_color - for pos, color in border_pos_and_color(surface): - self.assertEqual(color, expected_color, 'pos={}'.format(pos)) + self.assertEqual(surface.get_at(pt), expected_color, pt) + surface.unlock() -class PythonDrawLineTest(LineMixin, DrawTestCase): - """Test draw_py module functions: line, lines, aaline, and aalines. - This class inherits the general tests from LineMixin. It is also the class - to add any draw_py.draw_line/lines/aaline/aalines specific tests to. - """ +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing single lines. +# @unittest.skip('draw_py.draw_line not fully supported yet') +# class PythonDrawLineTest(LineMixin, PythonDrawTestCase): +# """Test draw_py module function line. +# +# This class inherits the general tests from LineMixin. It is also the class +# to add any draw_py.draw_line specific tests to. +# """ -class DrawLineTest(LineMixin, PythonDrawTestCase): - """Test draw module functions: line, lines, aaline, and aalines. +class DrawLineTest(LineMixin, DrawTestCase): + """Test draw module function line. This class inherits the general tests from LineMixin. It is also the class - to add any draw.line/lines/aaline/aalines specific tests to. + to add any draw.line specific tests to. """ - def test_path_data_validation(self): - """Test validation of multi-point drawing methods. - - See bug #521 - """ - surf = pygame.Surface((5, 5)) - rect = pygame.Rect(0, 0, 5, 5) - bad_values = ('text', b'bytes', 1 + 1j, # string, bytes, complex, - object(), (lambda x: x)) # object, function - bad_points = list(bad_values) + [(1,) , (1, 2, 3)] # wrong tuple length - bad_points.extend((1, v) for v in bad_values) # one wrong value - good_path = [(1, 1), (1, 3), (3, 3), (3, 1)] - # A) draw.lines - check_pts = [(x, y) for x in range(5) for y in range(5)] - for method, is_polgon in ((draw.lines, 0), (draw.aalines, 0), - (draw.polygon, 1)): - for val in bad_values: - # 1. at the beginning - draw.rect(surf, RED, rect, 0) - with self.assertRaises(TypeError): - if is_polgon: - method(surf, GREEN, [val] + good_path, 0) - else: - method(surf, GREEN, True, [val] + good_path) - # make sure, nothing was drawn : - self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) - # 2. not at the beginning (was not checked) - draw.rect(surf, RED, rect, 0) - with self.assertRaises(TypeError): - path = good_path[:2] + [val] + good_path[2:] - if is_polgon: - method(surf, GREEN, path, 0) - else: - method(surf, GREEN, True, path) - # make sure, nothing was drawn : - self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) - - def _test_endianness(self, draw_func): - """ test color component order - """ - depths = 24, 32 - for depth in depths: + def test_line_endianness(self): + """test color component order""" + for depth in (24, 32): surface = pygame.Surface((5, 3), 0, depth) - surface.fill(pygame.Color(0,0,0)) - draw_func(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) - self.assertGreater(surface.get_at((1, 1)).r, 0, 'there should be red here') - surface.fill(pygame.Color(0,0,0)) - draw_func(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) - self.assertGreater(surface.get_at((1, 1)).b, 0, 'there should be blue here') + surface.fill(pygame.Color(0, 0, 0)) + self.draw_line(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) - def test_line_endianness(self): - """ test color component order - """ - self._test_endianness(draw.line) + self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here") - def test_aaline_endianness(self): - """ test color component order - """ - self._test_endianness(draw.aaline) + surface.fill(pygame.Color(0, 0, 0)) + self.draw_line(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) - def test_color_validation(self): - surf = pygame.Surface((10, 10)) - colors = 123456, (1, 10, 100), RED # but not '#ab12df' or 'red' ... - points = ((0, 0), (1, 1), (1, 0)) - # 1. valid colors - for col in colors: - draw.line(surf, col, (0, 0), (1, 1)) - draw.aaline(surf, col, (0, 0), (1, 1)) - draw.aalines(surf, col, True, points) - draw.lines(surf, col, True, points) - draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) - draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) - draw.circle(surf, col, (7, 3), 2) - draw.polygon(surf, col, points, 0) - # 2. invalid colors - for col in ('invalid', 1.256, object(), None, '#ab12df', 'red'): - with self.assertRaises(TypeError): - draw.line(surf, col, (0, 0), (1, 1)) - with self.assertRaises(TypeError): - draw.aaline(surf, col, (0, 0), (1, 1)) - with self.assertRaises(TypeError): - draw.aalines(surf, col, True, points) - with self.assertRaises(TypeError): - draw.lines(surf, col, True, points) - with self.assertRaises(TypeError): - draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) - with self.assertRaises(TypeError): - draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) - with self.assertRaises(TypeError): - draw.circle(surf, col, (7, 3), 2) - with self.assertRaises(TypeError): - draw.polygon(surf, col, points, 0) + self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here") + def test_line(self): + # (l, t), (l, t) + self.surf_size = (320, 200) + self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) + self.color = (1, 13, 24, 205) -# Using a separate class to test line anti-aliasing. -class AntiAliasedLineMixin(object): - """Mixin tests for line anti-aliasing. + drawn = draw.line(self.surf, self.color, (1, 0), (200, 0)) + self.assertEqual( + drawn.right, 201, "end point arg should be (or at least was) inclusive" + ) - This class contains all the general anti-aliasing line drawing tests. - """ + # Should be colored where it's supposed to be + for pt in test_utils.rect_area_pts(drawn): + self.assertEqual(self.surf.get_at(pt), self.color) - def setUp(self): - self.surface = pygame.Surface((10, 10)) - draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(drawn): + self.assertNotEqual(self.surf.get_at(pt), self.color) - def _check_antialiasing(self, from_point, to_point, should, check_points, - set_endpoints=True): - """Draw a line between two points and check colors of check_points.""" - if set_endpoints: - should[from_point] = should[to_point] = FG_GREEN + # Line width greater that 1 + line_width = 2 + offset = 5 + a = (offset, offset) + b = (self.surf_size[0] - offset, a[1]) + c = (a[0], self.surf_size[1] - offset) + d = (b[0], c[1]) + e = (a[0] + offset, c[1]) + f = (b[0], c[0] + 5) + lines = [ + (a, d), + (b, c), + (c, b), + (d, a), + (a, b), + (b, a), + (a, c), + (c, a), + (a, e), + (e, a), + (a, f), + (f, a), + (a, a), + ] - def check_one_direction(from_point, to_point, should): - self.draw_aaline(self.surface, FG_GREEN, from_point, to_point, - True) + for p1, p2 in lines: + msg = f"{p1} - {p2}" + if p1[0] <= p2[0]: + plow = p1 + phigh = p2 + else: + plow = p2 + phigh = p1 - for pt in check_points: - color = should.get(pt, BG_RED) - if PY3: # "subTest" is sooo helpful, but does not exist in PY2 - with self.subTest(from_pt=from_point, pt=pt, to=to_point): - self.assertEqual(self.surface.get_at(pt), color) - else: - self.assertEqual(self.surface.get_at(pt), color) - # reset - draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + self.surf.fill((0, 0, 0)) + rec = draw.line(self.surf, (255, 255, 255), p1, p2, line_width) + xinc = yinc = 0 + + if abs(p1[0] - p2[0]) > abs(p1[1] - p2[1]): + yinc = 1 + else: + xinc = 1 + + for i in range(line_width): + p = (p1[0] + xinc * i, p1[1] + yinc * i) + self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) + + p = (p2[0] + xinc * i, p2[1] + yinc * i) + self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) + + p = (plow[0] - 1, plow[1]) + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + p = (plow[0] + xinc * line_width, plow[1] + yinc * line_width) + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + p = (phigh[0] + xinc * line_width, phigh[1] + yinc * line_width) + self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) + + if p1[0] < p2[0]: + rx = p1[0] + else: + rx = p2[0] + + if p1[1] < p2[1]: + ry = p1[1] + else: + ry = p2[1] + + w = abs(p2[0] - p1[0]) + 1 + xinc * (line_width - 1) + h = abs(p2[1] - p1[1]) + 1 + yinc * (line_width - 1) + msg += f", {rec}" + + self.assertEqual(rec, (rx, ry, w, h), msg) + + def test_line_for_gaps(self): + # This checks bug Thick Line Bug #448 + + width = 200 + height = 200 + surf = pygame.Surface((width, height), pygame.SRCALPHA) + + def white_surrounded_pixels(x, y): + offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)] + WHITE = (255, 255, 255, 255) + return len( + [1 for dx, dy in offsets if surf.get_at((x + dx, y + dy)) == WHITE] + ) + + def check_white_line(start, end): + surf.fill((0, 0, 0)) + pygame.draw.line(surf, (255, 255, 255), start, end, 30) + + BLACK = (0, 0, 0, 255) + for x in range(1, width - 1): + for y in range(1, height - 1): + if surf.get_at((x, y)) == BLACK: + self.assertTrue(white_surrounded_pixels(x, y) < 3) + + check_white_line((50, 50), (140, 0)) + check_white_line((50, 50), (0, 120)) + check_white_line((50, 50), (199, 198)) + + +### Lines Testing ############################################################# + + +class LinesMixin(BaseLineMixin): + """Mixin test for drawing lines. + + This class contains all the general lines drawing tests. + """ + + def test_lines__args(self): + """Ensures draw lines accepts the correct args.""" + bounds_rect = self.draw_lines( + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__args_without_width(self): + """Ensures draw lines accepts the args without a width.""" + bounds_rect = self.draw_lines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__kwargs(self): + """Ensures draw lines accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": False, + "points": points, + "width": 1, + }, + {"surface": surface, "color": color, "closed": False, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_lines(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__kwargs_order_independent(self): + """Ensures draw lines's kwargs are not order dependent.""" + bounds_rect = self.draw_lines( + closed=1, + points=((0, 0), (1, 1), (2, 2)), + width=2, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__args_missing(self): + """Ensures draw lines detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface, color, 0) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines() + + def test_lines__kwargs_missing(self): + """Ensures draw lines detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "closed": 1, + "points": ((2, 2), (1, 1)), + "width": 1, + } + + for name in ("points", "closed", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**invalid_kwargs) + + def test_lines__arg_invalid_types(self): + """Ensures draw lines detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + closed = 0 + points = ((1, 2), (2, 1)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_lines(surface, color, closed, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_lines(surface, color, closed, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid closed. + bounds_rect = self.draw_lines(surface, color, InvalidBool(), points) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_lines(surface, 2.3, closed, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_lines((1, 2, 3, 4), color, closed, points) + + def test_lines__kwarg_invalid_types(self): + """Ensures draw lines detects invalid kwarg types.""" + valid_kwargs = { + "surface": pygame.Surface((3, 3)), + "color": pygame.Color("green"), + "closed": False, + "points": ((1, 2), (2, 1)), + "width": 1, + } + + invalid_kwargs = { + "surface": pygame.Surface, + "color": 2.3, + "closed": InvalidBool(), + "points": (0, 0, 0), + "width": 1.2, + } + + for kwarg in ("surface", "color", "closed", "points", "width"): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__kwarg_invalid_name(self): + """Ensures draw lines detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + closed = 1 + points = ((1, 2), (2, 1)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__args_and_kwargs(self): + """Ensures draw lines accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + closed = 0 + points = ((1, 2), (2, 1)) + width = 1 + kwargs = { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "width": width, + } + + for name in ("surface", "color", "closed", "points", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_lines(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_lines(surface, color, **kwargs) + elif "closed" == name: + bounds_rect = self.draw_lines(surface, color, closed, **kwargs) + elif "points" == name: + bounds_rect = self.draw_lines(surface, color, closed, points, **kwargs) + else: + bounds_rect = self.draw_lines( + surface, color, closed, points, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_width_values(self): + """Ensures draw lines accepts different width values.""" + line_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": line_color, + "closed": False, + "points": (pos, (2, 1)), + "width": None, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = line_color if width > 0 else surface_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_points_format(self): + """Ensures draw lines accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": None, + "width": 1, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__invalid_points_formats(self): + """Ensures draw lines handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "width": 1, + } + + points_fmts = ( + ((1, 1), (2,)), # Too few coords. + ((1, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, "2")), # Wrong type. + ((1, 1), {2, 3}), # Wrong type. + ((1, 1), dict(((2, 2), (3, 3)))), # Wrong type. + {(1, 1), (1, 2)}, # Wrong type. + dict(((1, 1), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__invalid_points_values(self): + """Ensures draw lines handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + "width": 1, + } + + for points in ([], ((1, 1),)): # Too few points. + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__valid_closed_values(self): + """Ensures draw lines accepts different closed values.""" + line_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + pos = (1, 2) + kwargs = { + "surface": surface, + "color": line_color, + "closed": None, + "points": ((1, 1), (3, 1), (3, 3), (1, 3)), + "width": 1, + } + + true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) + false_values = (None, "", 0, (), [], False) + + for closed in true_values + false_values: + surface.fill(surface_color) # Clear for each test. + kwargs["closed"] = closed + expected_color = line_color if closed else surface_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__valid_color_formats(self): + """Ensures draw lines accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "closed": False, + "points": (pos, (2, 1)), + "width": 3, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_lines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_lines__invalid_color_formats(self): + """Ensures draw lines handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "closed": False, + "points": ((1, 1), (1, 2)), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_lines(**kwargs) + + def test_lines__color(self): + """Tests if the lines drawn are the correct color. + + Draws lines around the border of the given surface and checks if all + borders of the surface only contain the given color. + """ + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_lines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") + + def test_lines__color_with_thickness(self): + """Ensures thick lines are drawn using the correct color.""" + x_left = y_top = 5 + for surface in self._create_surfaces(): + x_right = surface.get_width() - 5 + y_bottom = surface.get_height() - 5 + endpoints = ( + (x_left, y_top), + (x_right, y_top), + (x_right, y_bottom), + (x_left, y_bottom), + ) + for expected_color in self.COLORS: + self.draw_lines(surface, expected_color, True, endpoints, 3) + + for t in (-1, 0, 1): + for x in range(x_left, x_right + 1): + for y in (y_top, y_bottom): + pos = (x, y + t) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"pos={pos}", + ) + for y in range(y_top, y_bottom + 1): + for x in (x_left, x_right): + pos = (x + t, y) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"pos={pos}", + ) + + def test_lines__gaps(self): + """Tests if the lines drawn contain any gaps. + + Draws lines around the border of the given surface and checks if + all borders of the surface contain any gaps. + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + self.draw_lines(surface, expected_color, True, corners(surface)) + + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") + + def test_lines__gaps_with_thickness(self): + """Ensures thick lines are drawn without any gaps.""" + expected_color = (255, 255, 255) + x_left = y_top = 5 + for surface in self._create_surfaces(): + h = (surface.get_width() - 11) // 5 + w = h * 5 + x_right = x_left + w + y_bottom = y_top + h + endpoints = ((x_left, y_top), (x_right, y_top), (x_right, y_bottom)) + self.draw_lines(surface, expected_color, True, endpoints, 3) + + for x in range(x_left, x_right + 1): + for t in (-1, 0, 1): + pos = (x, y_top + t) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + pos = (x, y_top + t + ((x - 3) // 5)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + for y in range(y_top, y_bottom + 1): + for t in (-1, 0, 1): + pos = (x_right + t, y) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_lines__bounding_rect(self): + """Ensures draw lines returns the correct bounding rect. + + Tests lines with endpoints on and off the surface and a range of + width/thickness values. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("black") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + pos_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the pos_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move pos_rect to different positions to test line endpoints on + # and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + pos_rect.center = pos + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. + + # Draw using different thickness and closed values. + for thickness in range(-1, 5): + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_lines( + surface, line_color, closed, pts, thickness + ) + + if 0 < thickness: + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, pos + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(pos, (0, 0)) + + self.assertEqual(bounding_rect, expected_rect) + + def test_lines__surface_clip(self): + """Ensures draw lines respects a surface's clip area.""" + surfw = surfh = 30 + line_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the lines's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the lines over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) + + for closed in (True, False): # Test closed and not closed. + for thickness in (1, 3): # Test different line widths. + # Get the expected points by drawing the lines without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_lines(surface, line_color, closed, pts, thickness) + expected_pts = get_color_points(surface, line_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the lines + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_lines(surface, line_color, closed, pts, thickness) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the + # expected_pts are the line_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = line_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing lines. +# class PythonDrawLinesTest(LinesMixin, PythonDrawTestCase): +# """Test draw_py module function lines. +# +# This class inherits the general tests from LinesMixin. It is also the +# class to add any draw_py.draw_lines specific tests to. +# """ + + +class DrawLinesTest(LinesMixin, DrawTestCase): + """Test draw module function lines. + + This class inherits the general tests from LinesMixin. It is also the class + to add any draw.lines specific tests to. + """ + + +### AALine Testing ############################################################ + + +class AALineMixin(BaseLineMixin): + """Mixin test for drawing a single aaline. + + This class contains all the general single aaline drawing tests. + """ + + def test_aaline__args(self): + """Ensures draw aaline accepts the correct args.""" + bounds_rect = self.draw_aaline( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__args_without_blend(self): + """Ensures draw aaline accepts the args without a blend.""" + bounds_rect = self.draw_aaline( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__blend_warning(self): + """From pygame 2, blend=False should raise DeprecationWarning.""" + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger DeprecationWarning. + self.draw_aaline( + pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2), False + ) + # Check if there is only one warning and is a DeprecationWarning. + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + + def test_aaline__kwargs(self): + """Ensures draw aaline accepts the correct kwargs""" + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + start_pos = (1, 1) + end_pos = (2, 2) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aaline(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__kwargs_order_independent(self): + """Ensures draw aaline's kwargs are not order dependent.""" + bounds_rect = self.draw_aaline( + start_pos=(1, 2), + end_pos=(2, 1), + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__args_missing(self): + """Ensures draw aaline detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline() + + def test_aaline__kwargs_missing(self): + """Ensures draw aaline detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "start_pos": (2, 1), + "end_pos": (2, 2), + } + + for name in ("end_pos", "start_pos", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**invalid_kwargs) + + def test_aaline__arg_invalid_types(self): + """Ensures draw aaline detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + start_pos = (0, 1) + end_pos = (1, 2) + + with self.assertRaises(TypeError): + # Invalid end_pos. + bounds_rect = self.draw_aaline(surface, color, start_pos, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid start_pos. + bounds_rect = self.draw_aaline(surface, color, (1,), end_pos) + + with self.assertRaises(ValueError): + # Invalid color. + bounds_rect = self.draw_aaline(surface, "invalid-color", start_pos, end_pos) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aaline((1, 2, 3, 4), color, start_pos, end_pos) + + def test_aaline__kwarg_invalid_types(self): + """Ensures draw aaline detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + start_pos = (1, 0) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "start_pos": start_pos, + "end_pos": end_pos, + }, + { + "surface": surface, + "color": color, + "start_pos": (0, 0, 0), # Invalid start_pos. + "end_pos": end_pos, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": (0,), # Invalid end_pos. + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__kwarg_invalid_name(self): + """Ensures draw aaline detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + start_pos = (1, 1) + end_pos = (2, 0) + kwargs_list = [ + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__args_and_kwargs(self): + """Ensures draw aaline accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + start_pos = (0, 1) + end_pos = (1, 2) + kwargs = { + "surface": surface, + "color": color, + "start_pos": start_pos, + "end_pos": end_pos, + } + + for name in ("surface", "color", "start_pos", "end_pos"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aaline(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aaline(surface, color, **kwargs) + elif "start_pos" == name: + bounds_rect = self.draw_aaline(surface, color, start_pos, **kwargs) + elif "end_pos" == name: + bounds_rect = self.draw_aaline( + surface, color, start_pos, end_pos, **kwargs + ) + else: + bounds_rect = self.draw_aaline( + surface, color, start_pos, end_pos, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__valid_start_pos_formats(self): + """Ensures draw aaline accepts different start_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": None, + "end_pos": (2, 2), + } + x, y = 2, 1 # start position + positions = ((x, y), (x + 0.01, y), (x, y + 0.01), (x + 0.01, y + 0.01)) + + for start_pos in positions: + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["start_pos"] = seq_type(start_pos) + + bounds_rect = self.draw_aaline(**kwargs) + + color = surface.get_at((x, y)) + for i, sub_color in enumerate(expected_color): + # The color could be slightly off the expected color due to + # any fractional position arguments. + self.assertGreaterEqual(color[i] + 6, sub_color, start_pos) + self.assertIsInstance(bounds_rect, pygame.Rect, start_pos) + + def test_aaline__valid_end_pos_formats(self): + """Ensures draw aaline accepts different end_pos formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "start_pos": (2, 1), + "end_pos": None, + } + x, y = 2, 2 # end position + positions = ((x, y), (x + 0.02, y), (x, y + 0.02), (x + 0.02, y + 0.02)) + + for end_pos in positions: + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["end_pos"] = seq_type(end_pos) + + bounds_rect = self.draw_aaline(**kwargs) + + color = surface.get_at((x, y)) + for i, sub_color in enumerate(expected_color): + # The color could be slightly off the expected color due to + # any fractional position arguments. + self.assertGreaterEqual(color[i] + 15, sub_color, end_pos) + self.assertIsInstance(bounds_rect, pygame.Rect, end_pos) + + def test_aaline__invalid_start_pos_formats(self): + """Ensures draw aaline handles invalid start_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": None, + "end_pos": (2, 2), + } + + start_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for start_pos in start_pos_fmts: + kwargs["start_pos"] = start_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__invalid_end_pos_formats(self): + """Ensures draw aaline handles invalid end_pos formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "start_pos": (2, 2), + "end_pos": None, + } + + end_pos_fmts = ( + (2,), # Too few coords. + (2, 1, 0), # Too many coords. + (2, "1"), # Wrong type. + {2, 1}, # Wrong type. + dict(((2, 1),)), + ) # Wrong type. + + for end_pos in end_pos_fmts: + kwargs["end_pos"] = end_pos + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__valid_color_formats(self): + """Ensures draw aaline accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "start_pos": pos, + "end_pos": (2, 1), + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_aaline(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aaline__invalid_color_formats(self): + """Ensures draw aaline handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "start_pos": (1, 1), + "end_pos": (2, 1), + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aaline(**kwargs) + + def test_aaline__color(self): + """Tests if the aaline drawn is the correct color.""" + pos = (0, 0) + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aaline(surface, expected_color, pos, (1, 0)) + + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_aaline__gaps(self): + """Tests if the aaline drawn contains any gaps. + + See: #512 + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + width = surface.get_width() + self.draw_aaline(surface, expected_color, (0, 0), (width - 1, 0)) + + for x in range(width): + pos = (x, 0) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + + def test_aaline__bounding_rect(self): + """Ensures draw aaline returns the correct bounding rect. + + Tests lines with endpoints on and off the surface. + """ + line_color = pygame.Color("red") + surf_color = pygame.Color("blue") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + helper_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the helper_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move the helper rect to different positions to test line + # endpoints on and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + helper_rect.center = pos + + for start, end in self._rect_lines(helper_rect): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aaline(surface, line_color, start, end) + + # Calculating the expected_rect after the line is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, start) + + self.assertEqual(bounding_rect, expected_rect) + + def test_aaline__surface_clip(self): + """Ensures draw aaline respects a surface's clip area.""" + surfw = surfh = 30 + aaline_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the aaline's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aaline over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + + # Get the expected points by drawing the aaline without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom) + + expected_pts = get_color_points(surface, surface_color, clip_rect, False) + + # Clear the surface and set the clip area. Redraw the aaline + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) + + surface.unlock() + + +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing single aalines. +# class PythonDrawAALineTest(AALineMixin, PythonDrawTestCase): +# """Test draw_py module function aaline. +# +# This class inherits the general tests from AALineMixin. It is also the +# class to add any draw_py.draw_aaline specific tests to. +# """ + + +class DrawAALineTest(AALineMixin, DrawTestCase): + """Test draw module function aaline. + + This class inherits the general tests from AALineMixin. It is also the + class to add any draw.aaline specific tests to. + """ + + def test_aaline_endianness(self): + """test color component order""" + for depth in (24, 32): + surface = pygame.Surface((5, 3), 0, depth) + surface.fill(pygame.Color(0, 0, 0)) + self.draw_aaline(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here") + + surface.fill(pygame.Color(0, 0, 0)) + self.draw_aaline(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1) + + self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here") + + def _check_antialiasing( + self, from_point, to_point, should, check_points, set_endpoints=True + ): + """Draw a line between two points and check colors of check_points.""" + if set_endpoints: + should[from_point] = should[to_point] = FG_GREEN + + def check_one_direction(from_point, to_point, should): + self.draw_aaline(self.surface, FG_GREEN, from_point, to_point, True) + + for pt in check_points: + color = should.get(pt, BG_RED) + with self.subTest(from_pt=from_point, pt=pt, to=to_point): + self.assertEqual(self.surface.get_at(pt), color) + + # reset + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) # it is important to test also opposite direction, the algorithm # is (#512) or was not symmetric @@ -708,8 +2953,12 @@ class AntiAliasedLineMixin(object): def test_short_non_antialiased_lines(self): """test very short not anti aliased lines in all directions.""" + # Horizontal, vertical and diagonal lines should not be anti-aliased, # even with draw.aaline ... + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)] def check_both_directions(from_pt, to_pt, other_points): @@ -732,16 +2981,24 @@ class AntiAliasedLineMixin(object): check_both_directions((6, 4), (4, 6), [(5, 5)]) def test_short_line_anti_aliasing(self): + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)] def check_both_directions(from_pt, to_pt, should): self._check_antialiasing(from_pt, to_pt, should, check_points) - # lets say dx = abs(x0 - x1) ; dy = abs(y0 - y1) brown = (127, 127, 0) + reddish = (191, 63, 0) + greenish = (63, 191, 0) + + # lets say dx = abs(x0 - x1) ; dy = abs(y0 - y1) + # dy / dx = 0.5 check_both_directions((4, 4), (6, 5), {(5, 4): brown, (5, 5): brown}) check_both_directions((4, 5), (6, 4), {(5, 4): brown, (5, 5): brown}) + # dy / dx = 2 check_both_directions((4, 4), (5, 6), {(4, 5): brown, (5, 5): brown}) check_both_directions((5, 4), (4, 6), {(4, 5): brown, (5, 5): brown}) @@ -749,309 +3006,694 @@ class AntiAliasedLineMixin(object): # some little longer lines; so we need to check more points: check_points = [(i, j) for i in range(2, 9) for j in range(2, 9)] # dy / dx = 0.25 - reddish = (191, 63, 0) - greenish = (63, 191, 0) - should = {(4, 3): greenish, (5, 3): brown, (6, 3): reddish, - (4, 4): reddish, (5, 4): brown, (6, 4): greenish} + should = { + (4, 3): greenish, + (5, 3): brown, + (6, 3): reddish, + (4, 4): reddish, + (5, 4): brown, + (6, 4): greenish, + } check_both_directions((3, 3), (7, 4), should) - should = {(4, 3): reddish, (5, 3): brown, (6, 3): greenish, - (4, 4): greenish, (5, 4): brown, (6, 4): reddish} + + should = { + (4, 3): reddish, + (5, 3): brown, + (6, 3): greenish, + (4, 4): greenish, + (5, 4): brown, + (6, 4): reddish, + } check_both_directions((3, 4), (7, 3), should) + # dy / dx = 4 - should = {(4, 4): greenish, (4, 5): brown, (4, 6): reddish, - (5, 4): reddish, (5, 5): brown, (5, 6): greenish, - } + should = { + (4, 4): greenish, + (4, 5): brown, + (4, 6): reddish, + (5, 4): reddish, + (5, 5): brown, + (5, 6): greenish, + } check_both_directions((4, 3), (5, 7), should) - should = {(4, 4): reddish, (4, 5): brown, (4, 6): greenish, - (5, 4): greenish, (5, 5): brown, (5, 6): reddish} + + should = { + (4, 4): reddish, + (4, 5): brown, + (4, 6): greenish, + (5, 4): greenish, + (5, 5): brown, + (5, 6): reddish, + } check_both_directions((5, 3), (4, 7), should) def test_anti_aliasing_float_coordinates(self): """Float coordinates should be blended smoothly.""" + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + check_points = [(i, j) for i in range(5) for j in range(5)] brown = (127, 127, 0) + reddish = (191, 63, 0) + greenish = (63, 191, 0) # 0. identical point : current implementation does no smoothing... - expected = {(1, 2): FG_GREEN} - self._check_antialiasing((1.5, 2), (1.5, 2), expected, - check_points, set_endpoints=False) expected = {(2, 2): FG_GREEN} - self._check_antialiasing((2.5, 2.7), (2.5, 2.7), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (1.5, 2), (1.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(2, 3): FG_GREEN} + self._check_antialiasing( + (2.49, 2.7), (2.49, 2.7), expected, check_points, set_endpoints=False + ) # 1. horizontal lines # a) blend endpoints expected = {(1, 2): brown, (2, 2): FG_GREEN} - self._check_antialiasing((1.5, 2), (2, 2), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (1.5, 2), (2, 2), expected, check_points, set_endpoints=False + ) expected = {(1, 2): brown, (2, 2): FG_GREEN, (3, 2): brown} - self._check_antialiasing((1.5, 2), (2.5, 2), expected, - check_points, set_endpoints=False) - expected = {(2, 2): brown, (1, 2): FG_GREEN, } - self._check_antialiasing((1, 2), (1.5, 2), expected, - check_points, set_endpoints=False) - expected = {(1, 2): brown, (2, 2): (63, 191, 0)} - self._check_antialiasing((1.5, 2), (1.75, 2), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (1.5, 2), (2.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(2, 2): brown, (1, 2): FG_GREEN} + self._check_antialiasing( + (1, 2), (1.5, 2), expected, check_points, set_endpoints=False + ) + expected = {(1, 2): brown, (2, 2): greenish} + self._check_antialiasing( + (1.5, 2), (1.75, 2), expected, check_points, set_endpoints=False + ) # b) blend y-coordinate - expected = {(x, y): brown for x in range(2, 5) for y in (1, 2)} - self._check_antialiasing((2, 1.5), (4, 1.5), expected, - check_points, set_endpoints=False) + expected = {(x, y): brown for x in range(2, 5) for y in (1, 2)} + self._check_antialiasing( + (2, 1.5), (4, 1.5), expected, check_points, set_endpoints=False + ) # 2. vertical lines # a) blend endpoints expected = {(2, 1): brown, (2, 2): FG_GREEN, (2, 3): brown} - self._check_antialiasing((2, 1.5), (2, 2.5), expected, - check_points, set_endpoints=False) - expected = {(2, 1): brown, (2, 2): (63, 191, 0)} - self._check_antialiasing((2, 1.5), (2, 1.75), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (2, 1.5), (2, 2.5), expected, check_points, set_endpoints=False + ) + expected = {(2, 1): brown, (2, 2): greenish} + self._check_antialiasing( + (2, 1.5), (2, 1.75), expected, check_points, set_endpoints=False + ) # b) blend x-coordinate expected = {(x, y): brown for x in (1, 2) for y in range(2, 5)} - self._check_antialiasing((1.5, 2), (1.5, 4), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (1.5, 2), (1.5, 4), expected, check_points, set_endpoints=False + ) # 3. diagonal lines # a) blend endpoints expected = {(1, 1): brown, (2, 2): FG_GREEN, (3, 3): brown} - self._check_antialiasing((1.5, 1.5), (2.5, 2.5), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (1.5, 1.5), (2.5, 2.5), expected, check_points, set_endpoints=False + ) expected = {(3, 1): brown, (2, 2): FG_GREEN, (1, 3): brown} - self._check_antialiasing((2.5, 1.5), (1.5, 2.5), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (2.5, 1.5), (1.5, 2.5), expected, check_points, set_endpoints=False + ) # b) blend sidewards expected = {(2, 1): brown, (2, 2): brown, (3, 2): brown, (3, 3): brown} - self._check_antialiasing((2, 1.5), (3, 2.5), expected, - check_points, set_endpoints=False) - - reddish = (191, 63, 0) - greenish = (63, 191, 0) - expected = {(2, 1): greenish, (2, 2): reddish, - (3, 2): greenish, (3, 3): reddish, - (4, 3): greenish, (4, 4): reddish} - self._check_antialiasing((2, 1.25), (4, 3.25), expected, - check_points, set_endpoints=False) + self._check_antialiasing( + (2, 1.5), (3, 2.5), expected, check_points, set_endpoints=False + ) + + expected = { + (2, 1): greenish, + (2, 2): reddish, + (3, 2): greenish, + (3, 3): reddish, + (4, 3): greenish, + (4, 4): reddish, + } + + self._check_antialiasing( + (2, 1.25), (4, 3.25), expected, check_points, set_endpoints=False + ) def test_anti_aliasing_at_and_outside_the_border(self): + """Ensures antialiasing works correct at a surface's borders.""" + + self.surface = pygame.Surface((10, 10)) + draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0) + check_points = [(i, j) for i in range(10) for j in range(10)] reddish = (191, 63, 0) brown = (127, 127, 0) greenish = (63, 191, 0) from_point, to_point = (3, 3), (7, 4) - should = {(4, 3): greenish, (5, 3): brown, (6, 3): reddish, - (4, 4): reddish, (5, 4): brown, (6, 4): greenish} - - for dx, dy in ((-4, 0), (4, 0), # moved to left and right borders - (0, -5), (0, -4), (0, -3), # upper border - (0, 5), (0, 6), (0, 7), # lower border - (-4, -4), (-4, -3), (-3, -4)): # upper left corner + should = { + (4, 3): greenish, + (5, 3): brown, + (6, 3): reddish, + (4, 4): reddish, + (5, 4): brown, + (6, 4): greenish, + } + + for dx, dy in ( + (-4, 0), + (4, 0), # moved to left and right borders + (0, -5), + (0, -4), + (0, -3), # upper border + (0, 5), + (0, 6), + (0, 7), # lower border + (-4, -4), + (-4, -3), + (-3, -4), + ): # upper left corner first = from_point[0] + dx, from_point[1] + dy - second = to_point[0] + dx, to_point[1] + dy - expected = {(x + dx, y + dy): color - for (x, y), color in should.items()} + second = to_point[0] + dx, to_point[1] + dy + expected = {(x + dx, y + dy): color for (x, y), color in should.items()} + self._check_antialiasing(first, second, expected, check_points) -@unittest.expectedFailure -class AntiAliasingLineTest(AntiAliasedLineMixin, DrawTestCase): - """Test anti-aliasing for draw. +### AALines Testing ########################################################### - This class inherits the general tests from AntiAliasedLineMixin. It is - also the class to add any anti-aliasing draw specific tests to. - """ -class PythonAntiAliasingLineTest(AntiAliasedLineMixin, PythonDrawTestCase): - """Test anti-aliasing for draw_py. +class AALinesMixin(BaseLineMixin): + """Mixin test for drawing aalines. - This class inherits the general tests from AntiAliasedLineMixin. It is - also the class to add any anti-aliasing draw_py specific tests to. + This class contains all the general aalines drawing tests. """ + def test_aalines__args(self): + """Ensures draw aalines accepts the correct args.""" + bounds_rect = self.draw_aalines( + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__args_without_blend(self): + """Ensures draw aalines accepts the args without a blend.""" + bounds_rect = self.draw_aalines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__blend_warning(self): + """From pygame 2, blend=False should raise DeprecationWarning.""" + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger DeprecationWarning. + self.draw_aalines( + pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)), False + ) + # Check if there is only one warning and is a DeprecationWarning. + self.assertEqual(len(w), 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + + def test_aalines__kwargs(self): + """Ensures draw aalines accepts the correct kwargs.""" + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + {"surface": surface, "color": color, "closed": False, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_aalines(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__kwargs_order_independent(self): + """Ensures draw aalines's kwargs are not order dependent.""" + bounds_rect = self.draw_aalines( + closed=1, + points=((0, 0), (1, 1), (2, 2)), + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__args_missing(self): + """Ensures draw aalines detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface, color, 0) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines() + + def test_aalines__kwargs_missing(self): + """Ensures draw aalines detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((3, 2)), + "color": pygame.Color("red"), + "closed": 1, + "points": ((2, 2), (1, 1)), + } + + for name in ("points", "closed", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. -### Draw Module Testing ####################################################### - -# These tests should eventually be moved to their appropriate mixin/class. -class DrawModuleTest(unittest.TestCase): + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**invalid_kwargs) + + def test_aalines__arg_invalid_types(self): + """Ensures draw aalines detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + closed = 0 + points = ((1, 2), (2, 1)) + + with self.assertRaises(TypeError): + # Invalid blend. + bounds_rect = self.draw_aalines(surface, color, closed, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_aalines(surface, color, closed, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid closed. + bounds_rect = self.draw_aalines(surface, color, InvalidBool(), points) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_aalines(surface, 2.3, closed, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_aalines((1, 2, 3, 4), color, closed, points) + + def test_aalines__kwarg_invalid_types(self): + """Ensures draw aalines detects invalid kwarg types.""" + valid_kwargs = { + "surface": pygame.Surface((3, 3)), + "color": pygame.Color("green"), + "closed": False, + "points": ((1, 2), (2, 1)), + } + + invalid_kwargs = { + "surface": pygame.Surface, + "color": 2.3, + "closed": InvalidBool(), + "points": (0, 0, 0), + } + + for kwarg in ("surface", "color", "closed", "points"): + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] - def setUp(self): - (self.surf_w, self.surf_h) = self.surf_size = (320, 200) - self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) - self.color = (1, 13, 24, 205) + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__kwarg_invalid_name(self): + """Ensures draw aalines detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + closed = 1 + points = ((1, 2), (2, 1)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__args_and_kwargs(self): + """Ensures draw aalines accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 2)) + color = (255, 255, 0, 0) + closed = 0 + points = ((1, 2), (2, 1)) + kwargs = { + "surface": surface, + "color": color, + "closed": closed, + "points": points, + } + + for name in ("surface", "color", "closed", "points"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_aalines(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_aalines(surface, color, **kwargs) + elif "closed" == name: + bounds_rect = self.draw_aalines(surface, color, closed, **kwargs) + elif "points" == name: + bounds_rect = self.draw_aalines( + surface, color, closed, points, **kwargs + ) + else: + bounds_rect = self.draw_aalines( + surface, color, closed, points, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_points_format(self): + """Ensures draw aalines accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "closed": False, + "points": None, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__invalid_points_formats(self): + """Ensures draw aalines handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + } + + points_fmts = ( + ((1, 1), (2,)), # Too few coords. + ((1, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, "2")), # Wrong type. + ((1, 1), {2, 3}), # Wrong type. + ((1, 1), dict(((2, 2), (3, 3)))), # Wrong type. + {(1, 1), (1, 2)}, # Wrong type. + dict(((1, 1), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points - def test_rect__fill(self): - # __doc__ (as of 2008-06-25) for pygame.draw.rect: + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__invalid_points_values(self): + """Ensures draw aalines handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "closed": False, + "points": None, + } + + for points in ([], ((1, 1),)): # Too few points. + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_aalines(**kwargs) + + def test_aalines__valid_closed_values(self): + """Ensures draw aalines accepts different closed values.""" + line_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((5, 5)) + pos = (1, 3) + kwargs = { + "surface": surface, + "color": line_color, + "closed": None, + "points": ((1, 1), (4, 1), (4, 4), (1, 4)), + } + + true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) + false_values = (None, "", 0, (), [], False) + + for closed in true_values + false_values: + surface.fill(surface_color) # Clear for each test. + kwargs["closed"] = closed + expected_color = line_color if closed else surface_color + + bounds_rect = self.draw_aalines(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_aalines__valid_color_formats(self): + """Ensures draw aalines accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + pos = (1, 1) + kwargs = { + "surface": surface, + "color": None, + "closed": False, + "points": (pos, (2, 1)), + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color - # pygame.draw.rect(Surface, color, Rect, width=0): return Rect - # draw a rectangle shape + bounds_rect = self.draw_aalines(**kwargs) - rect = pygame.Rect(10, 10, 25, 20) - drawn = draw.rect(self.surf, self.color, rect, 0) + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) - self.assertEqual(drawn, rect) + def test_aalines__invalid_color_formats(self): + """Ensures draw aalines handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "closed": False, + "points": ((1, 1), (1, 2)), + } - # Should be colored where it's supposed to be - for pt in test_utils.rect_area_pts(rect): - color_at_pt = self.surf.get_at(pt) - self.assertEqual(color_at_pt, self.color) + for expected_color in (2.3, self): + kwargs["color"] = expected_color - # And not where it shouldn't - for pt in test_utils.rect_outer_bounds(rect): - color_at_pt = self.surf.get_at(pt) - self.assertNotEqual(color_at_pt, self.color) - - # Issue #310: Cannot draw rectangles that are 1 pixel high - bgcolor = pygame.Color('black') - self.surf.fill(bgcolor) - hrect = pygame.Rect(1, 1, self.surf_w - 2, 1) - vrect = pygame.Rect(1, 3, 1, self.surf_h - 4) - drawn = draw.rect(self.surf, self.color, hrect, 0) - self.assertEqual(drawn, hrect) - x, y = hrect.topleft - w, h = hrect.size - self.assertEqual(self.surf.get_at((x - 1, y)), bgcolor) - self.assertEqual(self.surf.get_at((x + w, y)), bgcolor) - for i in range(x, x + w): - self.assertEqual(self.surf.get_at((i, y)), self.color) - drawn = draw.rect(self.surf, self.color, vrect, 0) - self.assertEqual(drawn, vrect) - x, y = vrect.topleft - w, h = vrect.size - self.assertEqual(self.surf.get_at((x, y - 1)), bgcolor) - self.assertEqual(self.surf.get_at((x, y + h)), bgcolor) - for i in range(y, y + h): - self.assertEqual(self.surf.get_at((x, i)), self.color) + with self.assertRaises(TypeError): + bounds_rect = self.draw_aalines(**kwargs) - def test_rect__one_pixel_lines(self): - rect = pygame.Rect(10, 10, 56, 20) + def test_aalines__color(self): + """Tests if the aalines drawn are the correct color. - drawn = draw.rect(self.surf, self.color, rect, 1) - self.assertEqual(drawn, rect) + Draws aalines around the border of the given surface and checks if all + borders of the surface only contain the given color. + """ + for surface in self._create_surfaces(): + for expected_color in self.COLORS: + self.draw_aalines(surface, expected_color, True, corners(surface)) - # Should be colored where it's supposed to be - for pt in test_utils.rect_perimeter_pts(drawn): - color_at_pt = self.surf.get_at(pt) - self.assertEqual(color_at_pt, self.color) + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") - # And not where it shouldn't - for pt in test_utils.rect_outer_bounds(drawn): - color_at_pt = self.surf.get_at(pt) - self.assertNotEqual(color_at_pt, self.color) + def test_aalines__gaps(self): + """Tests if the aalines drawn contain any gaps. - # See DrawLineTest class for additional draw.line() and draw.aaline() - # tests. - def test_line(self): - # (l, t), (l, t) - drawn = draw.line(self.surf, self.color, (1, 0), (200, 0)) - self.assertEqual(drawn.right, 201, - "end point arg should be (or at least was) inclusive") + Draws aalines around the border of the given surface and checks if + all borders of the surface contain any gaps. - # Should be colored where it's supposed to be - for pt in test_utils.rect_area_pts(drawn): - self.assertEqual(self.surf.get_at(pt), self.color) + See: #512 + """ + expected_color = (255, 255, 255) + for surface in self._create_surfaces(): + self.draw_aalines(surface, expected_color, True, corners(surface)) - # And not where it shouldn't - for pt in test_utils.rect_outer_bounds(drawn): - self.assertNotEqual(self.surf.get_at(pt), self.color) + for pos, color in border_pos_and_color(surface): + self.assertEqual(color, expected_color, f"pos={pos}") - # Line width greater that 1 - line_width = 2 - offset = 5 - a = (offset, offset) - b = (self.surf_size[0] - offset, a[1]) - c = (a[0], self.surf_size[1] - offset) - d = (b[0], c[1]) - e = (a[0] + offset, c[1]) - f = (b[0], c[0] + 5) - lines = [(a, d), (b, c), (c, b), (d, a), - (a, b), (b, a), (a, c), (c, a), - (a, e), (e, a), (a, f), (f, a), - (a, a),] - for p1, p2 in lines: - msg = "%s - %s" % (p1, p2) - if p1[0] <= p2[0]: - plow = p1 - phigh = p2 - else: - plow = p2 - phigh = p1 - self.surf.fill((0, 0, 0)) - rec = draw.line(self.surf, (255, 255, 255), p1, p2, line_width) - xinc = yinc = 0 - if abs(p1[0] - p2[0]) > abs(p1[1] - p2[1]): - yinc = 1 - else: - xinc = 1 - for i in range(line_width): - p = (p1[0] + xinc * i, p1[1] + yinc * i) - self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) - p = (p2[0] + xinc * i, p2[1] + yinc * i) - self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg) - p = (plow[0] - 1, plow[1]) - self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) - p = (plow[0] + xinc * line_width, plow[1] + yinc * line_width) - self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) - p = (phigh[0] + xinc * line_width, phigh[1] + yinc * line_width) - self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg) - if p1[0] < p2[0]: - rx = p1[0] - else: - rx = p2[0] - if p1[1] < p2[1]: - ry = p1[1] - else: - ry = p2[1] - w = abs(p2[0] - p1[0]) + 1 + xinc * (line_width - 1) - h = abs(p2[1] - p1[1]) + 1 + yinc * (line_width - 1) - msg += ", %s" % (rec,) - self.assertEqual(rec, (rx, ry, w, h), msg) + def test_aalines__bounding_rect(self): + """Ensures draw aalines returns the correct bounding rect. - @unittest.expectedFailure - def test_line_for_gaps(self): - """ |tags: ignore| + Tests lines with endpoints on and off the surface and blending + enabled and disabled. """ - # __doc__ (as of 2008-06-25) for pygame.draw.line: - - # pygame.draw.line(Surface, color, start_pos, end_pos, width=1): return Rect - # draw a straight line segment + line_color = pygame.Color("red") + surf_color = pygame.Color("blue") + width = height = 30 + # Using a rect to help manage where the lines are drawn. + pos_rect = pygame.Rect((0, 0), (width, height)) + + # Testing surfaces of different sizes. One larger than the pos_rect + # and one smaller (to test lines that span the surface). + for size in ((width + 5, height + 5), (width - 5, height - 5)): + surface = pygame.Surface(size, 0, 32) + surf_rect = surface.get_rect() + + # Move pos_rect to different positions to test line endpoints on + # and off the surface. + for pos in rect_corners_mids_and_center(surf_rect): + pos_rect.center = pos + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. + + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_aalines(surface, line_color, closed, pts) + + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, pos) + + self.assertEqual(bounding_rect, expected_rect) + + def test_aalines__surface_clip(self): + """Ensures draw aalines respects a surface's clip area.""" + surfw = surfh = 30 + aaline_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the aalines's pos. + + # Test centering the pos_rect along the clip rect's edge to allow for + # drawing the aalines over the clip_rect's bounds. + for center in rect_corners_mids_and_center(clip_rect): + pos_rect.center = center + pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) + for closed in (True, False): # Test closed and not closed. + # Get the expected points by drawing the aalines without + # the clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aalines(surface, aaline_color, closed, pts) + + expected_pts = get_color_points( + surface, surface_color, clip_rect, False + ) + + # Clear the surface and set the clip area. Redraw the + # aalines and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_aalines(surface, aaline_color, closed, pts) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) - # This checks bug Thick Line Bug #448 + surface.unlock() - width = 200 - height = 200 - surf = pygame.Surface((width, height), pygame.SRCALPHA) - def white_surrounded_pixels(x, y): - offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)] - WHITE = (255, 255, 255, 255) - return len([1 for dx, dy in offsets - if surf.get_at((x+dx, y+dy)) == WHITE]) +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing aalines. +# class PythonDrawAALinesTest(AALinesMixin, PythonDrawTestCase): +# """Test draw_py module function aalines. +# +# This class inherits the general tests from AALinesMixin. It is also the +# class to add any draw_py.draw_aalines specific tests to. +# """ - def check_white_line(start, end): - surf.fill((0, 0, 0)) - pygame.draw.line(surf, (255, 255, 255), start, end, 30) - BLACK = (0, 0, 0, 255) - for x in range(1, width-1): - for y in range(1, height-1): - if surf.get_at((x, y)) == BLACK: - self.assertTrue(white_surrounded_pixels(x, y) < 3) +class DrawAALinesTest(AALinesMixin, DrawTestCase): + """Test draw module function aalines. - check_white_line((50, 50), (140, 0)) - check_white_line((50, 50), (0, 120)) - check_white_line((50, 50), (199, 198)) + This class inherits the general tests from AALinesMixin. It is also the + class to add any draw.aalines specific tests to. + """ ### Polygon Testing ########################################################### SQUARE = ([0, 0], [3, 0], [3, 3], [0, 3]) DIAMOND = [(1, 3), (3, 5), (5, 3), (3, 1)] -CROSS = ([2, 0], [4, 0], [4, 2], [6, 2], - [6, 4], [4, 4], [4, 6], [2, 6], - [2, 4], [0, 4], [0, 2], [2, 2]) - - -class DrawPolygonMixin(object): +CROSS = ( + [2, 0], + [4, 0], + [4, 2], + [6, 2], + [6, 4], + [4, 4], + [4, 6], + [2, 6], + [2, 4], + [0, 4], + [0, 2], + [2, 2], +) + + +class DrawPolygonMixin: """Mixin tests for drawing polygons. This class contains all the general polygon drawing tests. @@ -1060,6 +3702,337 @@ class DrawPolygonMixin(object): def setUp(self): self.surface = pygame.Surface((20, 20)) + def test_polygon__args(self): + """Ensures draw polygon accepts the correct args.""" + bounds_rect = self.draw_polygon( + pygame.Surface((3, 3)), (0, 10, 0, 50), ((0, 0), (1, 1), (2, 2)), 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__args_without_width(self): + """Ensures draw polygon accepts the args without a width.""" + bounds_rect = self.draw_polygon( + pygame.Surface((2, 2)), (0, 0, 0, 50), ((0, 0), (1, 1), (2, 2)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__kwargs(self): + """Ensures draw polygon accepts the correct kwargs + with and without a width arg. + """ + surface = pygame.Surface((4, 4)) + color = pygame.Color("yellow") + points = ((0, 0), (1, 1), (2, 2)) + kwargs_list = [ + {"surface": surface, "color": color, "points": points, "width": 1}, + {"surface": surface, "color": color, "points": points}, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_polygon(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__kwargs_order_independent(self): + """Ensures draw polygon's kwargs are not order dependent.""" + bounds_rect = self.draw_polygon( + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + width=0, + points=((0, 1), (1, 2), (2, 3)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__args_missing(self): + """Ensures draw polygon detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon() + + def test_polygon__kwargs_missing(self): + """Ensures draw polygon detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "points": ((2, 1), (2, 2), (2, 3)), + "width": 1, + } + + for name in ("points", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**invalid_kwargs) + + def test_polygon__arg_invalid_types(self): + """Ensures draw polygon detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + points = ((0, 1), (1, 2), (1, 3)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_polygon(surface, color, points, "1") + + with self.assertRaises(TypeError): + # Invalid points. + bounds_rect = self.draw_polygon(surface, color, (1, 2, 3)) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_polygon(surface, 2.3, points) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_polygon((1, 2, 3, 4), color, points) + + def test_polygon__kwarg_invalid_types(self): + """Ensures draw polygon detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + points = ((0, 0), (1, 0), (2, 0)) + width = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "points": points, + "width": width, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "points": points, + "width": width, + }, + { + "surface": surface, + "color": color, + "points": ((1,), (1,), (1,)), # Invalid points. + "width": width, + }, + {"surface": surface, "color": color, "points": points, "width": 1.2}, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__kwarg_invalid_name(self): + """Ensures draw polygon detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + points = ((1, 1), (1, 2), (1, 3)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "points": points, + "width": 1, + "invalid": 1, + }, + {"surface": surface, "color": color, "points": points, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__args_and_kwargs(self): + """Ensures draw polygon accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + points = ((0, 1), (1, 2), (2, 3)) + width = 0 + kwargs = {"surface": surface, "color": color, "points": points, "width": width} + + for name in ("surface", "color", "points", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_polygon(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_polygon(surface, color, **kwargs) + elif "points" == name: + bounds_rect = self.draw_polygon(surface, color, points, **kwargs) + else: + bounds_rect = self.draw_polygon(surface, color, points, width, **kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__valid_width_values(self): + """Ensures draw polygon accepts different width values.""" + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": None, + } + pos = kwargs["points"][0] + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__valid_points_format(self): + """Ensures draw polygon accepts different points formats.""" + expected_color = (10, 20, 30, 255) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "points": None, + "width": 0, + } + + # The point type can be a tuple/list/Vector2. + point_types = ( + (tuple, tuple, tuple, tuple), # all tuples + (list, list, list, list), # all lists + (Vector2, Vector2, Vector2, Vector2), # all Vector2s + (list, Vector2, tuple, Vector2), + ) # mix + + # The point values can be ints or floats. + point_values = ( + ((1, 1), (2, 1), (2, 2), (1, 2)), + ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)), + ) + + # Each sequence of points can be a tuple or a list. + seq_types = (tuple, list) + + for point_type in point_types: + for values in point_values: + check_pos = values[0] + points = [point_type[i](pt) for i, pt in enumerate(values)] + + for seq_type in seq_types: + surface.fill(surface_color) # Clear for each test. + kwargs["points"] = seq_type(points) + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(check_pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__invalid_points_formats(self): + """Ensures draw polygon handles invalid points formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "points": None, + "width": 0, + } + + points_fmts = ( + ((1, 1), (2, 1), (2,)), # Too few coords. + ((1, 1), (2, 1), (2, 2, 2)), # Too many coords. + ((1, 1), (2, 1), (2, "2")), # Wrong type. + ((1, 1), (2, 1), {2, 3}), # Wrong type. + ((1, 1), (2, 1), dict(((2, 2), (3, 3)))), # Wrong type. + {(1, 1), (2, 1), (2, 2), (1, 2)}, # Wrong type. + dict(((1, 1), (2, 2), (3, 3), (4, 4))), + ) # Wrong type. + + for points in points_fmts: + kwargs["points"] = points + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__invalid_points_values(self): + """Ensures draw polygon handles invalid points values correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "points": None, + "width": 0, + } + + points_fmts = ( + tuple(), # Too few points. + ((1, 1),), # Too few points. + ((1, 1), (2, 1)), + ) # Too few points. + + for points in points_fmts: + for seq_type in (tuple, list): # Test as tuples and lists. + kwargs["points"] = seq_type(points) + + with self.assertRaises(ValueError): + bounds_rect = self.draw_polygon(**kwargs) + + def test_polygon__valid_color_formats(self): + """Ensures draw polygon accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": 0, + } + pos = kwargs["points"][0] + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_polygon(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_polygon__invalid_color_formats(self): + """Ensures draw polygon handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "points": ((1, 1), (2, 1), (2, 2), (1, 2)), + "width": 0, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_polygon(**kwargs) + def test_draw_square(self): self.draw_polygon(self.surface, RED, SQUARE, 0) # note : there is a discussion (#234) if draw.polygon should include or @@ -1083,7 +4056,7 @@ class DrawPolygonMixin(object): # 1. one-pixel-high, filled pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0) self.draw_polygon(self.surface, GREEN, [(x, 2) for x, _y in CROSS], 0) - cross_size = 6 # the maximum x or y coordinate of the cross + cross_size = 6 # the maximum x or y coordinate of the cross for x in range(cross_size + 1): self.assertEqual(self.surface.get_at((x, 1)), RED) self.assertEqual(self.surface.get_at((x, 2)), GREEN) @@ -1123,7 +4096,7 @@ class DrawPolygonMixin(object): for y in range(10): if (x, y) in inside: self.assertEqual(self.surface.get_at((x, y)), RED) - elif (x in range(2, 5) and y <7) or (y in range(2, 5) and x < 7): + elif (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7): # we are on the border of the cross: self.assertEqual(self.surface.get_at((x, y)), GREEN) else: @@ -1136,9 +4109,11 @@ class DrawPolygonMixin(object): inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)] for x in range(10): for y in range(10): - if (x in range(2, 5) and y <7) or (y in range(2, 5) and x < 7): + if (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7): # we are on the border of the cross: - self.assertEqual(self.surface.get_at((x, y)), GREEN, msg=str((x, y))) + self.assertEqual( + self.surface.get_at((x, y)), GREEN, msg=str((x, y)) + ) else: # we are outside self.assertEqual(self.surface.get_at((x, y)), RED) @@ -1146,9 +4121,14 @@ class DrawPolygonMixin(object): def test_illumine_shape(self): """non-regression on issue #313""" rect = pygame.Rect((0, 0, 20, 20)) - path_data = [(0, 0), (rect.width-1, 0), # upper border - (rect.width-5, 5-1), (5-1, 5-1), # upper inner - (5- 1, rect.height-5), (0, rect.height-1)] # lower diagonal + path_data = [ + (0, 0), + (rect.width - 1, 0), # upper border + (rect.width - 5, 5 - 1), + (5 - 1, 5 - 1), # upper inner + (5 - 1, rect.height - 5), + (0, rect.height - 1), + ] # lower diagonal # The shape looks like this (the numbers are the indices of path_data) # 0**********************1 <-- upper border @@ -1174,18 +4154,130 @@ class DrawPolygonMixin(object): self.draw_polygon(self.surface, GREEN, path_data[:4], 0) for x in range(20): self.assertEqual(self.surface.get_at((x, 0)), GREEN) # upper border - for x in range(4, rect.width-5 +1): + for x in range(4, rect.width - 5 + 1): self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner # 2. with the corners 4 & 5 pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0) self.draw_polygon(self.surface, GREEN, path_data, 0) - for x in range(4, rect.width-5 +1): + for x in range(4, rect.width - 5 + 1): self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner def test_invalid_points(self): - self.assertRaises(TypeError, lambda: self.draw_polygon(self.surface, - RED, ((0, 0), (0, 20), (20, 20), 20), 0)) + self.assertRaises( + TypeError, + lambda: self.draw_polygon( + self.surface, RED, ((0, 0), (0, 20), (20, 20), 20), 0 + ), + ) + + def test_polygon__bounding_rect(self): + """Ensures draw polygon returns the correct bounding rect. + + Tests polygons on and off the surface and a range of width/thickness + values. + """ + polygon_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # polygons off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # A rect (pos_rect) is used to help create and position the + # polygon. Each of this rect's position attributes will be set to + # the pos value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + pos_rect = pygame.Rect((0, 0), (width, height)) + setattr(pos_rect, attr, pos) + # Points form a triangle with no fully + # horizontal/vertical lines. + vertices = ( + pos_rect.midleft, + pos_rect.midtop, + pos_rect.bottomright, + ) + + for thickness in range(4): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_polygon( + surface, polygon_color, vertices, thickness + ) + + # Calculating the expected_rect after the polygon + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, vertices[0] + ) + + self.assertEqual( + bounding_rect, + expected_rect, + f"thickness={thickness}", + ) + + def test_polygon__surface_clip(self): + """Ensures draw polygon respects a surface's clip area. + + Tests drawing the polygon filled and unfilled. + """ + surfw = surfh = 30 + polygon_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (8, 10)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the polygon's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the polygon along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the polygon without the + # clip area set. + pos_rect.center = center + vertices = ( + pos_rect.topleft, + pos_rect.topright, + pos_rect.bottomright, + pos_rect.bottomleft, + ) + surface.set_clip(None) + surface.fill(surface_color) + self.draw_polygon(surface, polygon_color, vertices, width) + expected_pts = get_color_points(surface, polygon_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the polygon + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_polygon(surface, polygon_color, vertices, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the polygon_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = polygon_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() class DrawPolygonTest(DrawPolygonMixin, DrawTestCase): @@ -1196,24 +4288,646 @@ class DrawPolygonTest(DrawPolygonMixin, DrawTestCase): """ -class PythonDrawPolygonTest(DrawPolygonMixin, PythonDrawTestCase): - """Test draw_py module function draw_polygon. - - This class inherits the general tests from DrawPolygonMixin. It is also - the class to add any draw_py.draw_polygon specific tests to. - """ +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever fully supports drawing polygons. +# @unittest.skip('draw_py.draw_polygon not fully supported yet') +# class PythonDrawPolygonTest(DrawPolygonMixin, PythonDrawTestCase): +# """Test draw_py module function draw_polygon. +# +# This class inherits the general tests from DrawPolygonMixin. It is also +# the class to add any draw_py.draw_polygon specific tests to. +# """ ### Rect Testing ############################################################## -class DrawRectMixin(object): + +class DrawRectMixin: """Mixin tests for drawing rects. This class contains all the general rect drawing tests. """ - def todo_test_circle(self): - self.fail() + def test_rect__args(self): + """Ensures draw rect accepts the correct args.""" + bounds_rect = self.draw_rect( + pygame.Surface((2, 2)), + (20, 10, 20, 150), + pygame.Rect((0, 0), (1, 1)), + 2, + 1, + 2, + 3, + 4, + 5, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__args_without_width(self): + """Ensures draw rect accepts the args without a width and borders.""" + bounds_rect = self.draw_rect( + pygame.Surface((3, 5)), (0, 0, 0, 255), pygame.Rect((0, 0), (1, 1)) + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__kwargs(self): + """Ensures draw rect accepts the correct kwargs + with and without a width and border_radius arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((5, 5)), + "color": pygame.Color("red"), + "rect": pygame.Rect((0, 0), (1, 2)), + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": pygame.Surface((1, 2)), + "color": (0, 100, 200), + "rect": (0, 0, 1, 1), + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_rect(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__kwargs_order_independent(self): + """Ensures draw rect's kwargs are not order dependent.""" + bounds_rect = self.draw_rect( + color=(0, 1, 2), + border_radius=10, + surface=pygame.Surface((2, 3)), + border_top_left_radius=5, + width=-2, + border_top_right_radius=20, + border_bottom_right_radius=0, + rect=pygame.Rect((0, 0), (0, 0)), + border_bottom_left_radius=15, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__args_missing(self): + """Ensures draw rect detects any missing required args.""" + surface = pygame.Surface((1, 1)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(surface, pygame.Color("white")) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect() + + def test_rect__kwargs_missing(self): + """Ensures draw rect detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 3)), + "color": pygame.Color("red"), + "rect": pygame.Rect((0, 0), (2, 2)), + "width": 5, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + } + + for name in ("rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**invalid_kwargs) + + def test_rect__arg_invalid_types(self): + """Ensures draw rect detects invalid arg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("white") + rect = pygame.Rect((1, 1), (1, 1)) + + with self.assertRaises(TypeError): + # Invalid border_bottom_right_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_bottom_right_radius="rad" + ) + + with self.assertRaises(TypeError): + # Invalid border_bottom_left_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_bottom_left_radius="rad" + ) + + with self.assertRaises(TypeError): + # Invalid border_top_right_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_top_right_radius="rad" + ) + + with self.assertRaises(TypeError): + # Invalid border_top_left_radius. + bounds_rect = self.draw_rect( + surface, color, rect, 2, border_top_left_radius="draw" + ) + + with self.assertRaises(TypeError): + # Invalid border_radius. + bounds_rect = self.draw_rect(surface, color, rect, 2, "rad") + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_rect(surface, color, rect, "2", 4) + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_rect(surface, color, (1, 2, 3), 2, 6) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_rect(surface, 2.3, rect, 3, 8) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_rect(rect, color, rect, 4, 10) + + def test_rect__kwarg_invalid_types(self): + """Ensures draw rect detects invalid kwarg types.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("red") + rect = pygame.Rect((0, 0), (1, 1)) + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": (1, 1, 2), # Invalid rect. + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1.1, # Invalid width. + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10.5, # Invalid border_radius. + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5.5, # Invalid top_left_radius. + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": "a", # Invalid top_right_radius. + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": "c", # Invalid bottom_left_radius + "border_bottom_right_radius": 0, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": "d", # Invalid bottom_right. + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__kwarg_invalid_name(self): + """Ensures draw rect detects invalid kwarg names.""" + surface = pygame.Surface((2, 1)) + color = pygame.Color("green") + rect = pygame.Rect((0, 0), (3, 3)) + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "width": 1, + "border_radius": 10, + "border_top_left_radius": 5, + "border_top_right_radius": 20, + "border_bottom_left_radius": 15, + "border_bottom_right_radius": 0, + "invalid": 1, + }, + {"surface": surface, "color": color, "rect": rect, "invalid": 1}, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__args_and_kwargs(self): + """Ensures draw rect accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 255, 0) + rect = pygame.Rect((1, 0), (2, 5)) + width = 0 + kwargs = {"surface": surface, "color": color, "rect": rect, "width": width} + + for name in ("surface", "color", "rect", "width"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_rect(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_rect(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_rect(surface, color, rect, **kwargs) + else: + bounds_rect = self.draw_rect(surface, color, rect, width, **kwargs) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__valid_width_values(self): + """Ensures draw rect accepts different width values.""" + pos = (1, 1) + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + color = (1, 2, 3, 255) + kwargs = { + "surface": surface, + "color": color, + "rect": pygame.Rect(pos, (2, 2)), + "width": None, + } + + for width in (-1000, -10, -1, 0, 1, 10, 1000): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__valid_rect_formats(self): + """Ensures draw rect accepts different rect formats.""" + pos = (1, 1) + expected_color = pygame.Color("yellow") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0} + rects = ( + pygame.Rect(pos, (1, 1)), + (pos, (2, 2)), + (pos[0], pos[1], 3, 3), + [pos, (2.1, 2.2)], + ) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__invalid_rect_formats(self): + """Ensures draw rect handles invalid rect formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("red"), + "rect": None, + "width": 0, + } + + invalid_fmts = ( + [], + [1], + [1, 2], + [1, 2, 3], + [1, 2, 3, 4, 5], + {1, 2, 3, 4}, + [1, 2, 3, "4"], + ) + + for rect in invalid_fmts: + kwargs["rect"] = rect + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__valid_color_formats(self): + """Ensures draw rect accepts different color formats.""" + pos = (1, 1) + red_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 1)), + "width": 3, + } + reds = ((255, 0, 0), (255, 0, 0, 255), surface.map_rgb(red_color), red_color) + + for color in reds: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = red_color + + bounds_rect = self.draw_rect(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_rect__invalid_color_formats(self): + """Ensures draw rect handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (1, 1)), + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_rect(**kwargs) + + def test_rect__fill(self): + self.surf_w, self.surf_h = self.surf_size = (320, 200) + self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + rect = pygame.Rect(10, 10, 25, 20) + drawn = self.draw_rect(self.surf, self.color, rect, 0) + + self.assertEqual(drawn, rect) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_area_pts(rect): + color_at_pt = self.surf.get_at(pt) + + self.assertEqual(color_at_pt, self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(rect): + color_at_pt = self.surf.get_at(pt) + + self.assertNotEqual(color_at_pt, self.color) + + # Issue #310: Cannot draw rectangles that are 1 pixel high + bgcolor = pygame.Color("black") + self.surf.fill(bgcolor) + hrect = pygame.Rect(1, 1, self.surf_w - 2, 1) + vrect = pygame.Rect(1, 3, 1, self.surf_h - 4) + + drawn = self.draw_rect(self.surf, self.color, hrect, 0) + + self.assertEqual(drawn, hrect) + + x, y = hrect.topleft + w, h = hrect.size + + self.assertEqual(self.surf.get_at((x - 1, y)), bgcolor) + self.assertEqual(self.surf.get_at((x + w, y)), bgcolor) + for i in range(x, x + w): + self.assertEqual(self.surf.get_at((i, y)), self.color) + + drawn = self.draw_rect(self.surf, self.color, vrect, 0) + + self.assertEqual(drawn, vrect) + + x, y = vrect.topleft + w, h = vrect.size + + self.assertEqual(self.surf.get_at((x, y - 1)), bgcolor) + self.assertEqual(self.surf.get_at((x, y + h)), bgcolor) + for i in range(y, y + h): + self.assertEqual(self.surf.get_at((x, i)), self.color) + + def test_rect__one_pixel_lines(self): + self.surf = pygame.Surface((320, 200), pygame.SRCALPHA) + self.color = (1, 13, 24, 205) + + rect = pygame.Rect(10, 10, 56, 20) + + drawn = self.draw_rect(self.surf, self.color, rect, 1) + + self.assertEqual(drawn, rect) + + # Should be colored where it's supposed to be + for pt in test_utils.rect_perimeter_pts(drawn): + color_at_pt = self.surf.get_at(pt) + + self.assertEqual(color_at_pt, self.color) + + # And not where it shouldn't + for pt in test_utils.rect_outer_bounds(drawn): + color_at_pt = self.surf.get_at(pt) + + self.assertNotEqual(color_at_pt, self.color) + + def test_rect__draw_line_width(self): + surface = pygame.Surface((100, 100)) + surface.fill("black") + color = pygame.Color(255, 255, 255) + rect_width = 80 + rect_height = 50 + line_width = 10 + pygame.draw.rect( + surface, color, pygame.Rect(0, 0, rect_width, rect_height), line_width + ) + for i in range(line_width): + self.assertEqual(surface.get_at((i, i)), color) + self.assertEqual(surface.get_at((rect_width - i - 1, i)), color) + self.assertEqual(surface.get_at((i, rect_height - i - 1)), color) + self.assertEqual( + surface.get_at((rect_width - i - 1, rect_height - i - 1)), color + ) + self.assertEqual(surface.get_at((line_width, line_width)), (0, 0, 0)) + self.assertEqual( + surface.get_at((rect_width - line_width - 1, line_width)), (0, 0, 0) + ) + self.assertEqual( + surface.get_at((line_width, rect_height - line_width - 1)), (0, 0, 0) + ) + self.assertEqual( + surface.get_at((rect_width - line_width - 1, rect_height - line_width - 1)), + (0, 0, 0), + ) + + def test_rect__bounding_rect(self): + """Ensures draw rect returns the correct bounding rect. + + Tests rects on and off the surface and a range of width/thickness + values. + """ + rect_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # rects off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the rect's position attributes will be set to the pos + # value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes and thickness values. + for width, height in sizes: + rect = pygame.Rect((0, 0), (width, height)) + setattr(rect, attr, pos) + + for thickness in range(4): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_rect( + surface, rect_color, rect, thickness + ) + + # Calculating the expected_rect after the rect is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, rect.topleft + ) + + self.assertEqual( + bounding_rect, + expected_rect, + f"thickness={thickness}", + ) + + def test_rect__surface_clip(self): + """Ensures draw rect respects a surface's clip area. + + Tests drawing the rect filled and unfilled. + """ + surfw = surfh = 30 + rect_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (8, 10)) + clip_rect.center = surface.get_rect().center + test_rect = clip_rect.copy() # Manages the rect's pos. + + for width in (0, 1): # Filled and unfilled. + # Test centering the rect along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the rect without the + # clip area set. + test_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_rect(surface, rect_color, test_rect, width) + expected_pts = get_color_points(surface, rect_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the rect + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_rect(surface, rect_color, test_rect, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the rect_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = rect_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() class DrawRectTest(DrawRectMixin, DrawTestCase): @@ -1224,24 +4938,837 @@ class DrawRectTest(DrawRectMixin, DrawTestCase): """ -class PythonDrawRectTest(DrawRectMixin, PythonDrawTestCase): - """Test draw_py module function draw_rect. - - This class inherits the general tests from DrawRectMixin. It is also the - class to add any draw_py.draw_rect specific tests to. - """ +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing rects. +# @unittest.skip('draw_py.draw_rect not supported yet') +# class PythonDrawRectTest(DrawRectMixin, PythonDrawTestCase): +# """Test draw_py module function draw_rect. +# +# This class inherits the general tests from DrawRectMixin. It is also the +# class to add any draw_py.draw_rect specific tests to. +# """ ### Circle Testing ############################################################ -class DrawCircleMixin(object): + +class DrawCircleMixin: """Mixin tests for drawing circles. This class contains all the general circle drawing tests. """ - def todo_test_circle(self): - self.fail() + def test_circle__args(self): + """Ensures draw circle accepts the correct args.""" + bounds_rect = self.draw_circle( + pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), 3, 1, 1, 0, 1, 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_without_width(self): + """Ensures draw circle accepts the args without a width and + quadrants.""" + bounds_rect = self.draw_circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_with_negative_width(self): + """Ensures draw circle accepts the args with negative width.""" + bounds_rect = self.draw_circle( + pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1, -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0)) + + def test_circle__args_with_width_gt_radius(self): + """Ensures draw circle accepts the args with width > radius.""" + bounds_rect = self.draw_circle( + pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 2, 3, 0, 0, 0, 0 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(0, 0, 2, 2)) + + def test_circle__kwargs(self): + """Ensures draw circle accepts the correct kwargs + with and without a width and quadrant arguments. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "center": (2, 2), + "radius": 2, + "width": 1, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": False, + "draw_bottom_right": True, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "center": (1, 1), + "radius": 1, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_circle(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__kwargs_order_independent(self): + """Ensures draw circle's kwargs are not order dependent.""" + bounds_rect = self.draw_circle( + draw_top_right=False, + color=(10, 20, 30), + surface=pygame.Surface((3, 2)), + width=0, + draw_bottom_left=False, + center=(1, 0), + draw_bottom_right=False, + radius=2, + draw_top_left=True, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__args_missing(self): + """Ensures draw circle detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("blue") + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface, color, (0, 0)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle() + + def test_circle__kwargs_missing(self): + """Ensures draw circle detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "center": (1, 0), + "radius": 2, + "width": 1, + "draw_top_right": False, + "draw_top_left": False, + "draw_bottom_left": False, + "draw_bottom_right": True, + } + + for name in ("radius", "center", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**invalid_kwargs) + + def test_circle__arg_invalid_types(self): + """Ensures draw circle detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + center = (1, 1) + radius = 1 + + with self.assertRaises(TypeError): + # Invalid draw_top_right. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, "a", 1, 1, 1 + ) + + with self.assertRaises(TypeError): + # Invalid draw_top_left. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, 1, "b", 1, 1 + ) + + with self.assertRaises(TypeError): + # Invalid draw_bottom_left. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, 1, 1, "c", 1 + ) + + with self.assertRaises(TypeError): + # Invalid draw_bottom_right. + bounds_rect = self.draw_circle( + surface, color, center, radius, 1, 1, 1, 1, "d" + ) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_circle(surface, color, center, radius, "1") + + with self.assertRaises(TypeError): + # Invalid radius. + bounds_rect = self.draw_circle(surface, color, center, "2") + + with self.assertRaises(TypeError): + # Invalid center. + bounds_rect = self.draw_circle(surface, color, (1, 2, 3), radius) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_circle(surface, 2.3, center, radius) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_circle((1, 2, 3, 4), color, center, radius) + + def test_circle__kwarg_invalid_types(self): + """Ensures draw circle detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + center = (0, 1) + radius = 1 + width = 1 + quadrant = 1 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": (1, 1, 1), # Invalid center. + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": "1", # Invalid radius. + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": 1.2, # Invalid width. + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": "True", # Invalid draw_top_right + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": "True", # Invalid draw_top_left + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": 3.14, # Invalid draw_bottom_left + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": "quadrant", # Invalid draw_bottom_right + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__kwarg_invalid_name(self): + """Ensures draw circle detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + center = (0, 0) + radius = 2 + kwargs_list = [ + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": 1, + "quadrant": 1, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + }, + { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__args_and_kwargs(self): + """Ensures draw circle accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + center = (1, 0) + radius = 2 + width = 0 + draw_top_right = True + draw_top_left = False + draw_bottom_left = False + draw_bottom_right = True + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": width, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for name in ( + "surface", + "color", + "center", + "radius", + "width", + "draw_top_right", + "draw_top_left", + "draw_bottom_left", + "draw_bottom_right", + ): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_circle(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_circle(surface, color, **kwargs) + elif "center" == name: + bounds_rect = self.draw_circle(surface, color, center, **kwargs) + elif "radius" == name: + bounds_rect = self.draw_circle(surface, color, center, radius, **kwargs) + elif "width" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, **kwargs + ) + elif "draw_top_right" == name: + bounds_rect = self.draw_circle( + surface, color, center, radius, width, draw_top_right, **kwargs + ) + elif "draw_top_left" == name: + bounds_rect = self.draw_circle( + surface, + color, + center, + radius, + width, + draw_top_right, + draw_top_left, + **kwargs, + ) + elif "draw_bottom_left" == name: + bounds_rect = self.draw_circle( + surface, + color, + center, + radius, + width, + draw_top_right, + draw_top_left, + draw_bottom_left, + **kwargs, + ) + else: + bounds_rect = self.draw_circle( + surface, + color, + center, + radius, + width, + draw_top_right, + draw_top_left, + draw_bottom_left, + draw_bottom_right, + **kwargs, + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_width_values(self): + """Ensures draw circle accepts different width values.""" + center = (2, 2) + radius = 1 + pos = (center[0] - radius, center[1]) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": radius, + "width": None, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for width in (-100, -10, -1, 0, 1, 10, 100): + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = color if width >= 0 else surface_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_radius_values(self): + """Ensures draw circle accepts different radius values.""" + pos = center = (2, 2) + surface_color = pygame.Color("white") + surface = pygame.Surface((3, 4)) + color = (10, 20, 30, 255) + kwargs = { + "surface": surface, + "color": color, + "center": center, + "radius": None, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for radius in (-10, -1, 0, 1, 10): + surface.fill(surface_color) # Clear for each test. + kwargs["radius"] = radius + expected_color = color if radius > 0 else surface_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_center_formats(self): + """Ensures draw circle accepts different center formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((4, 4)) + kwargs = { + "surface": surface, + "color": expected_color, + "center": None, + "radius": 1, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + x, y = 2, 2 # center position + + # The center values can be ints or floats. + for center in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)): + # The center type can be a tuple/list/Vector2. + for seq_type in (tuple, list, Vector2): + surface.fill(surface_color) # Clear for each test. + kwargs["center"] = seq_type(center) + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at((x, y)), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__valid_color_formats(self): + """Ensures draw circle accepts different color formats.""" + center = (2, 2) + radius = 1 + pos = (center[0] - radius, center[1]) + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((3, 4)) + kwargs = { + "surface": surface, + "color": None, + "center": center, + "radius": radius, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_circle(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_circle__invalid_color_formats(self): + """Ensures draw circle handles invalid color formats correctly.""" + kwargs = { + "surface": pygame.Surface((4, 3)), + "color": None, + "center": (1, 2), + "radius": 1, + "width": 0, + "draw_top_right": True, + "draw_top_left": True, + "draw_bottom_left": True, + "draw_bottom_right": True, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_circle(**kwargs) + + def test_circle__floats(self): + """Ensure that floats are accepted.""" + draw.circle( + surface=pygame.Surface((4, 4)), + color=(255, 255, 127), + center=(1.5, 1.5), + radius=1.3, + width=0, + draw_top_right=True, + draw_top_left=True, + draw_bottom_left=True, + draw_bottom_right=True, + ) + + draw.circle( + surface=pygame.Surface((4, 4)), + color=(255, 255, 127), + center=Vector2(1.5, 1.5), + radius=1.3, + width=0, + draw_top_right=True, + draw_top_left=True, + draw_bottom_left=True, + draw_bottom_right=True, + ) + + draw.circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1.3, 1.3), 1.2) + + # def test_circle_clip(self): + # """ maybe useful to help work out circle clip algorithm.""" + # MAX = max + # MIN = min + # posx=30 + # posy=15 + # radius=1 + # l=29 + # t=14 + # r=30 + # b=16 + # clip_rect_x=0 + # clip_rect_y=0 + # clip_rect_w=30 + # clip_rect_h=30 + + # l = MAX(posx - radius, clip_rect_x) + # t = MAX(posy - radius, clip_rect_y) + # r = MIN(posx + radius, clip_rect_x + clip_rect_w) + # b = MIN(posy + radius, clip_rect_y + clip_rect_h) + + # l, t, MAX(r - l, 0), MAX(b - t, 0) + + def test_circle__bounding_rect(self): + """Ensures draw circle returns the correct bounding rect. + + Tests circles on and off the surface and a range of width/thickness + values. + """ + circle_color = pygame.Color("red") + surf_color = pygame.Color("black") + max_radius = 3 + surface = pygame.Surface((30, 30), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # circles off and partially off the surface. Make this rect such that + # when centering the test circle on one of its corners, the circle is + # drawn fully off the test surface, but a rect bounding the circle + # would still overlap with the test surface. + big_rect = surf_rect.inflate(max_radius * 2 - 1, max_radius * 2 - 1) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Test using different radius and thickness values. + for radius in range(max_radius + 1): + for thickness in range(radius + 1): + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_circle( + surface, circle_color, pos, radius, thickness + ) + + # Calculating the expected_rect after the circle is + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect(surface, surf_color, pos) + + # print("pos:%s:, radius:%s:, thickness:%s:" % (pos, radius, thickness)) + # self.assertEqual(bounding_rect, expected_rect) + with self.subTest( + surface=surface, + circle_color=circle_color, + pos=pos, + radius=radius, + thickness=thickness, + ): + self.assertEqual(bounding_rect, expected_rect) + + def test_circle_negative_radius(self): + """Ensures negative radius circles return zero sized bounding rect.""" + surf = pygame.Surface((200, 200)) + color = (0, 0, 0, 50) + center = surf.get_height() // 2, surf.get_height() // 2 + + bounding_rect = self.draw_circle(surf, color, center, radius=-1, width=1) + self.assertEqual(bounding_rect.size, (0, 0)) + + def test_circle_zero_radius(self): + """Ensures zero radius circles does not draw a center pixel. + + NOTE: This is backwards incompatible behaviour with 1.9.x. + """ + surf = pygame.Surface((200, 200)) + circle_color = pygame.Color("red") + surf_color = pygame.Color("black") + center = (100, 100) + radius = 0 + width = 1 + + bounding_rect = self.draw_circle(surf, circle_color, center, radius, width) + expected_rect = create_bounding_rect(surf, surf_color, center) + self.assertEqual(bounding_rect, expected_rect) + self.assertEqual(bounding_rect, pygame.Rect(100, 100, 0, 0)) + + def test_circle__surface_clip(self): + """Ensures draw circle respects a surface's clip area. + + Tests drawing the circle filled and unfilled. + """ + surfw = surfh = 25 + circle_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (10, 10)) + clip_rect.center = surface.get_rect().center + radius = clip_rect.w // 2 + 1 + + for width in (0, 1): # Filled and unfilled. + # Test centering the circle along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the circle without the + # clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_circle(surface, circle_color, center, radius, width) + expected_pts = get_color_points(surface, circle_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the circle + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_circle(surface, circle_color, center, radius, width) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the circle_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = circle_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() + + def test_circle_shape(self): + """Ensures there are no holes in the circle, and no overdrawing. + + Tests drawing a thick circle. + Measures the distance of the drawn pixels from the circle center. + """ + surfw = surfh = 100 + circle_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + (cx, cy) = center = (50, 50) + radius = 45 + width = 25 + + dest_rect = self.draw_circle(surface, circle_color, center, radius, width) + + for pt in test_utils.rect_area_pts(dest_rect): + x, y = pt + sqr_distance = (x - cx) ** 2 + (y - cy) ** 2 + if (radius - width + 1) ** 2 < sqr_distance < (radius - 1) ** 2: + self.assertEqual(surface.get_at(pt), circle_color) + if ( + sqr_distance < (radius - width - 1) ** 2 + or sqr_distance > (radius + 1) ** 2 + ): + self.assertEqual(surface.get_at(pt), surface_color) + + def test_circle__diameter(self): + """Ensures draw circle is twice size of radius high and wide.""" + surf = pygame.Surface((200, 200)) + color = (0, 0, 0, 50) + center = surf.get_height() // 2, surf.get_height() // 2 + width = 1 + radius = 6 + for radius in range(1, 65): + bounding_rect = self.draw_circle(surf, color, center, radius, width) + self.assertEqual(bounding_rect.width, radius * 2) + self.assertEqual(bounding_rect.height, radius * 2) + + def test_x_bounds(self): + """ensures a circle is drawn properly when there is a negative x, or a big x.""" + + surf = pygame.Surface((200, 200)) + bgcolor = (0, 0, 0, 255) + surf.fill(bgcolor) + color = (255, 0, 0, 255) + width = 1 + radius = 10 + + where = (0, 30) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual( + bounding_rect1, + pygame.Rect(0, where[1] - radius, where[0] + radius, radius * 2), + ) + self.assertEqual( + surf.get_at((where[0] if where[0] > 0 else 0, where[1])), color + ) + self.assertEqual(surf.get_at((where[0] + radius + 1, where[1])), bgcolor) + self.assertEqual(surf.get_at((where[0] + radius - 1, where[1])), color) + + surf.fill(bgcolor) + where = (-1e30, 80) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual(bounding_rect1, pygame.Rect(where[0], where[1], 0, 0)) + self.assertEqual(surf.get_at((0 + radius, where[1])), bgcolor) + + surf.fill(bgcolor) + where = (surf.get_width() + radius * 2, 80) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual(bounding_rect1, pygame.Rect(where[0], where[1], 0, 0)) + self.assertEqual(surf.get_at((0, where[1])), bgcolor) + self.assertEqual(surf.get_at((0 + radius // 2, where[1])), bgcolor) + self.assertEqual(surf.get_at((surf.get_width() - 1, where[1])), bgcolor) + self.assertEqual(surf.get_at((surf.get_width() - radius, where[1])), bgcolor) + + surf.fill(bgcolor) + where = (-1, 80) + bounding_rect1 = self.draw_circle(surf, color, where, radius=radius) + self.assertEqual( + bounding_rect1, + pygame.Rect(0, where[1] - radius, where[0] + radius, radius * 2), + ) + self.assertEqual( + surf.get_at((where[0] if where[0] > 0 else 0, where[1])), color + ) + self.assertEqual(surf.get_at((where[0] + radius, where[1])), bgcolor) + self.assertEqual(surf.get_at((where[0] + radius - 1, where[1])), color) + class DrawCircleTest(DrawCircleMixin, DrawTestCase): """Test draw module function circle. @@ -1251,24 +5778,610 @@ class DrawCircleTest(DrawCircleMixin, DrawTestCase): """ -class PythonDrawCircleTest(DrawCircleMixin, PythonDrawTestCase): - """Test draw_py module function draw_circle." - - This class inherits the general tests from DrawCircleMixin. It is also - the class to add any draw_py.draw_circle specific tests to. - """ +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing circles. +# @unittest.skip('draw_py.draw_circle not supported yet') +# class PythonDrawCircleTest(DrawCircleMixin, PythonDrawTestCase): +# """Test draw_py module function draw_circle." +# +# This class inherits the general tests from DrawCircleMixin. It is also +# the class to add any draw_py.draw_circle specific tests to. +# """ ### Arc Testing ############################################################### -class DrawArcMixin(object): + +class DrawArcMixin: """Mixin tests for drawing arcs. This class contains all the general arc drawing tests. """ - def todo_test_arc(self): - self.fail() + def test_arc__args(self): + """Ensures draw arc accepts the correct args.""" + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (0, 10, 0, 50), (1, 1, 2, 2), 0, 1, 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_without_width(self): + """Ensures draw arc accepts the args without a width.""" + bounds_rect = self.draw_arc( + pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((0, 0), (2, 2)), 1.1, 2.1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_with_negative_width(self): + """Ensures draw arc accepts the args with negative width.""" + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), (1, 1, 2, 2), 0, 1, -1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0)) + + def test_arc__args_with_width_gt_radius(self): + """Ensures draw arc accepts the args with + width > rect.w // 2 and width > rect.h // 2. + """ + rect = pygame.Rect((0, 0), (4, 4)) + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.w // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + bounds_rect = self.draw_arc( + pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.h // 2 + 1 + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__kwargs(self): + """Ensures draw arc accepts the correct kwargs + with and without a width arg. + """ + kwargs_list = [ + { + "surface": pygame.Surface((4, 4)), + "color": pygame.Color("yellow"), + "rect": pygame.Rect((0, 0), (3, 2)), + "start_angle": 0.5, + "stop_angle": 3, + "width": 1, + }, + { + "surface": pygame.Surface((2, 1)), + "color": (0, 10, 20), + "rect": (0, 0, 2, 2), + "start_angle": 1, + "stop_angle": 3.1, + }, + ] + + for kwargs in kwargs_list: + bounds_rect = self.draw_arc(**kwargs) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__kwargs_order_independent(self): + """Ensures draw arc's kwargs are not order dependent.""" + bounds_rect = self.draw_arc( + stop_angle=1, + start_angle=2.2, + color=(1, 2, 3), + surface=pygame.Surface((3, 2)), + width=1, + rect=pygame.Rect((1, 0), (2, 3)), + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__args_missing(self): + """Ensures draw arc detects any missing required args.""" + surface = pygame.Surface((1, 1)) + color = pygame.Color("red") + rect = pygame.Rect((0, 0), (2, 2)) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color, rect, 0.1) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color, rect) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface, color) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(surface) + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc() + + def test_arc__kwargs_missing(self): + """Ensures draw arc detects any missing required kwargs.""" + kwargs = { + "surface": pygame.Surface((1, 2)), + "color": pygame.Color("red"), + "rect": pygame.Rect((1, 0), (2, 2)), + "start_angle": 0.1, + "stop_angle": 2, + "width": 1, + } + + for name in ("stop_angle", "start_angle", "rect", "color", "surface"): + invalid_kwargs = dict(kwargs) + invalid_kwargs.pop(name) # Pop from a copy. + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**invalid_kwargs) + + def test_arc__arg_invalid_types(self): + """Ensures draw arc detects invalid arg types.""" + surface = pygame.Surface((2, 2)) + color = pygame.Color("blue") + rect = pygame.Rect((1, 1), (3, 3)) + + with self.assertRaises(TypeError): + # Invalid width. + bounds_rect = self.draw_arc(surface, color, rect, 0, 1, "1") + + with self.assertRaises(TypeError): + # Invalid stop_angle. + bounds_rect = self.draw_arc(surface, color, rect, 0, "1", 1) + + with self.assertRaises(TypeError): + # Invalid start_angle. + bounds_rect = self.draw_arc(surface, color, rect, "1", 0, 1) + + with self.assertRaises(TypeError): + # Invalid rect. + bounds_rect = self.draw_arc(surface, color, (1, 2, 3, 4, 5), 0, 1, 1) + + with self.assertRaises(TypeError): + # Invalid color. + bounds_rect = self.draw_arc(surface, 2.3, rect, 0, 1, 1) + + with self.assertRaises(TypeError): + # Invalid surface. + bounds_rect = self.draw_arc(rect, color, rect, 0, 1, 1) + + def test_arc__kwarg_invalid_types(self): + """Ensures draw arc detects invalid kwarg types.""" + surface = pygame.Surface((3, 3)) + color = pygame.Color("green") + rect = pygame.Rect((0, 1), (4, 2)) + start = 3 + stop = 4 + kwargs_list = [ + { + "surface": pygame.Surface, # Invalid surface. + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": 2.3, # Invalid color. + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": (0, 0, 0), # Invalid rect. + "start_angle": start, + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": "1", # Invalid start_angle. + "stop_angle": stop, + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": "1", # Invalid stop_angle. + "width": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1.1, + }, + ] # Invalid width. + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc__kwarg_invalid_name(self): + """Ensures draw arc detects invalid kwarg names.""" + surface = pygame.Surface((2, 3)) + color = pygame.Color("cyan") + rect = pygame.Rect((0, 1), (2, 2)) + start = 0.9 + stop = 2.3 + kwargs_list = [ + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": 1, + "invalid": 1, + }, + { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "invalid": 1, + }, + ] + + for kwargs in kwargs_list: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc__args_and_kwargs(self): + """Ensures draw arc accepts a combination of args/kwargs""" + surface = pygame.Surface((3, 1)) + color = (255, 255, 0, 0) + rect = pygame.Rect((1, 0), (2, 3)) + start = 0.6 + stop = 2 + width = 1 + kwargs = { + "surface": surface, + "color": color, + "rect": rect, + "start_angle": start, + "stop_angle": stop, + "width": width, + } + + for name in ("surface", "color", "rect", "start_angle", "stop_angle"): + kwargs.pop(name) + + if "surface" == name: + bounds_rect = self.draw_arc(surface, **kwargs) + elif "color" == name: + bounds_rect = self.draw_arc(surface, color, **kwargs) + elif "rect" == name: + bounds_rect = self.draw_arc(surface, color, rect, **kwargs) + elif "start_angle" == name: + bounds_rect = self.draw_arc(surface, color, rect, start, **kwargs) + elif "stop_angle" == name: + bounds_rect = self.draw_arc(surface, color, rect, start, stop, **kwargs) + else: + bounds_rect = self.draw_arc( + surface, color, rect, start, stop, width, **kwargs + ) + + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__valid_width_values(self): + """Ensures draw arc accepts different width values.""" + arc_color = pygame.Color("yellow") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": arc_color, + "rect": rect, + "start_angle": 0, + "stop_angle": 7, + "width": None, + } + + for width in (-50, -10, -3, -2, -1, 0, 1, 2, 3, 10, 50): + msg = f"width={width}" + surface.fill(surface_color) # Clear for each test. + kwargs["width"] = width + expected_color = arc_color if width > 0 else surface_color + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_stop_angle_values(self): + """Ensures draw arc accepts different stop_angle values.""" + expected_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": rect, + "start_angle": -17, + "stop_angle": None, + "width": 1, + } + + for stop_angle in (-10, -5.5, -1, 0, 1, 5.5, 10): + msg = f"stop_angle={stop_angle}" + surface.fill(surface_color) # Clear for each test. + kwargs["stop_angle"] = stop_angle + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_start_angle_values(self): + """Ensures draw arc accepts different start_angle values.""" + expected_color = pygame.Color("blue") + surface_color = pygame.Color("white") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": rect, + "start_angle": None, + "stop_angle": 17, + "width": 1, + } + + for start_angle in (-10.0, -5.5, -1, 0, 1, 5.5, 10.0): + msg = f"start_angle={start_angle}" + surface.fill(surface_color) # Clear for each test. + kwargs["start_angle"] = start_angle + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color, msg) + self.assertIsInstance(bounds_rect, pygame.Rect, msg) + + def test_arc__valid_rect_formats(self): + """Ensures draw arc accepts different rect formats.""" + expected_color = pygame.Color("red") + surface_color = pygame.Color("black") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": expected_color, + "rect": None, + "start_angle": 0, + "stop_angle": 7, + "width": 1, + } + rects = (rect, (rect.topleft, rect.size), (rect.x, rect.y, rect.w, rect.h)) + + for rect in rects: + surface.fill(surface_color) # Clear for each test. + kwargs["rect"] = rect + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__valid_color_formats(self): + """Ensures draw arc accepts different color formats.""" + green_color = pygame.Color("green") + surface_color = pygame.Color("black") + surface = pygame.Surface((6, 6)) + rect = pygame.Rect((0, 0), (4, 4)) + rect.center = surface.get_rect().center + pos = rect.centerx + 1, rect.centery + 1 + kwargs = { + "surface": surface, + "color": None, + "rect": rect, + "start_angle": 0, + "stop_angle": 7, + "width": 1, + } + greens = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(green_color), + green_color, + ) + + for color in greens: + surface.fill(surface_color) # Clear for each test. + kwargs["color"] = color + + if isinstance(color, int): + expected_color = surface.unmap_rgb(color) + else: + expected_color = green_color + + bounds_rect = self.draw_arc(**kwargs) + + self.assertEqual(surface.get_at(pos), expected_color) + self.assertIsInstance(bounds_rect, pygame.Rect) + + def test_arc__invalid_color_formats(self): + """Ensures draw arc handles invalid color formats correctly.""" + pos = (1, 1) + surface = pygame.Surface((4, 3)) + kwargs = { + "surface": surface, + "color": None, + "rect": pygame.Rect(pos, (2, 2)), + "start_angle": 5, + "stop_angle": 6.1, + "width": 1, + } + + for expected_color in (2.3, self): + kwargs["color"] = expected_color + + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + + def test_arc(self): + """Ensure draw arc works correctly.""" + black = pygame.Color("black") + red = pygame.Color("red") + + # create an image object of width 100, height 150, filled with black. + surface = pygame.Surface((100, 150)) + surface.fill(black) + + # rectangle that contains for the ellipse arc. + # 0 pixel from left, 0 pixel from top + # 80 pixels wide, 40 pixels high + rect = (0, 0, 80, 40) + + # angle of the arc in radians + start_angle = 0.0 + stop_angle = 3.14 + + # thickness, and it grows inward from the rectangle + width = 3 + + # draw an elliptical arc + pygame.draw.arc(surface, red, rect, start_angle, stop_angle, width) + + # Save the drawn arc + pygame.image.save(surface, "arc.png") + + # arc is red + x = 20 + for y in range(2, 5): + self.assertEqual(surface.get_at((x, y)), red) + + # the rest area in surface is black + self.assertEqual(surface.get_at((0, 0)), black) + + def test_arc__bounding_rect(self): + """Ensures draw arc returns the correct bounding rect. + + Tests arcs on and off the surface and a range of width/thickness + values. + """ + arc_color = pygame.Color("red") + surf_color = pygame.Color("black") + min_width = min_height = 5 + max_width = max_height = 7 + sizes = ((min_width, min_height), (max_width, max_height)) + surface = pygame.Surface((20, 20), 0, 32) + surf_rect = surface.get_rect() + # Make a rect that is bigger than the surface to help test drawing + # arcs off and partially off the surface. + big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1) + + # Max angle allows for a full circle to be drawn. + start_angle = 0 + stop_angles = (0, 2, 3, 5, math.ceil(2 * math.pi)) + + for pos in rect_corners_mids_and_center( + surf_rect + ) + rect_corners_mids_and_center(big_rect): + # Each of the arc's rect position attributes will be set to the pos + # value. + for attr in RECT_POSITION_ATTRIBUTES: + # Test using different rect sizes, thickness values and stop + # angles. + for width, height in sizes: + arc_rect = pygame.Rect((0, 0), (width, height)) + setattr(arc_rect, attr, pos) + + for thickness in (0, 1, 2, 3, min(width, height)): + for stop_angle in stop_angles: + surface.fill(surf_color) # Clear for each test. + + bounding_rect = self.draw_arc( + surface, + arc_color, + arc_rect, + start_angle, + stop_angle, + thickness, + ) + + # Calculating the expected_rect after the arc + # is drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, arc_rect.topleft + ) + + self.assertEqual( + bounding_rect, + expected_rect, + f"thickness={thickness}", + ) + + def test_arc__surface_clip(self): + """Ensures draw arc respects a surface's clip area.""" + surfw = surfh = 30 + start = 0.1 + end = 0 # end < start so a full circle will be drawn + arc_color = pygame.Color("red") + surface_color = pygame.Color("green") + surface = pygame.Surface((surfw, surfh)) + surface.fill(surface_color) + + clip_rect = pygame.Rect((0, 0), (11, 11)) + clip_rect.center = surface.get_rect().center + pos_rect = clip_rect.copy() # Manages the arc's pos. + + for thickness in (1, 3): # Different line widths. + # Test centering the arc along the clip rect's edge. + for center in rect_corners_mids_and_center(clip_rect): + # Get the expected points by drawing the arc without the + # clip area set. + pos_rect.center = center + surface.set_clip(None) + surface.fill(surface_color) + self.draw_arc(surface, arc_color, pos_rect, start, end, thickness) + expected_pts = get_color_points(surface, arc_color, clip_rect) + + # Clear the surface and set the clip area. Redraw the arc + # and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) + + self.draw_arc(surface, arc_color, pos_rect, start, end, thickness) + + surface.lock() # For possible speed up. + + # Check all the surface points to ensure only the expected_pts + # are the arc_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + expected_color = arc_color + else: + expected_color = surface_color + + self.assertEqual(surface.get_at(pt), expected_color, pt) + + surface.unlock() class DrawArcTest(DrawArcMixin, DrawTestCase): @@ -1279,16 +6392,117 @@ class DrawArcTest(DrawArcMixin, DrawTestCase): """ -class PythonDrawArcTest(DrawArcMixin, PythonDrawTestCase): - """Test draw_py module function draw_arc. +# Commented out to avoid cluttering the test output. Add back in if draw_py +# ever properly supports drawing arcs. +# @unittest.skip('draw_py.draw_arc not supported yet') +# class PythonDrawArcTest(DrawArcMixin, PythonDrawTestCase): +# """Test draw_py module function draw_arc. +# +# This class inherits the general tests from DrawArcMixin. It is also the +# class to add any draw_py.draw_arc specific tests to. +# """ - This class inherits the general tests from DrawArcMixin. It is also the - class to add any draw_py.draw_arc specific tests to. - """ + +### Draw Module Testing ####################################################### + + +class DrawModuleTest(unittest.TestCase): + """General draw module tests.""" + + def test_path_data_validation(self): + """Test validation of multi-point drawing methods. + + See bug #521 + """ + surf = pygame.Surface((5, 5)) + rect = pygame.Rect(0, 0, 5, 5) + bad_values = ( + "text", + b"bytes", + 1 + 1j, # string, bytes, complex, + object(), + (lambda x: x), + ) # object, function + bad_points = list(bad_values) + [(1,), (1, 2, 3)] # wrong tuple length + bad_points.extend((1, v) for v in bad_values) # one wrong value + good_path = [(1, 1), (1, 3), (3, 3), (3, 1)] + # A) draw.lines + check_pts = [(x, y) for x in range(5) for y in range(5)] + + for method, is_polgon in ( + (draw.lines, 0), + (draw.aalines, 0), + (draw.polygon, 1), + ): + for val in bad_values: + # 1. at the beginning + draw.rect(surf, RED, rect, 0) + with self.assertRaises(TypeError): + if is_polgon: + method(surf, GREEN, [val] + good_path, 0) + else: + method(surf, GREEN, True, [val] + good_path) + + # make sure, nothing was drawn : + self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) + + # 2. not at the beginning (was not checked) + draw.rect(surf, RED, rect, 0) + with self.assertRaises(TypeError): + path = good_path[:2] + [val] + good_path[2:] + if is_polgon: + method(surf, GREEN, path, 0) + else: + method(surf, GREEN, True, path) + + # make sure, nothing was drawn : + self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts)) + + def test_color_validation(self): + surf = pygame.Surface((10, 10)) + colors = 123456, (1, 10, 100), RED, "#ab12df", "red" + points = ((0, 0), (1, 1), (1, 0)) + + # 1. valid colors + for col in colors: + draw.line(surf, col, (0, 0), (1, 1)) + draw.aaline(surf, col, (0, 0), (1, 1)) + draw.aalines(surf, col, True, points) + draw.lines(surf, col, True, points) + draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) + draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + draw.circle(surf, col, (7, 3), 2) + draw.polygon(surf, col, points, 0) + + # 2. invalid colors + for col in (1.256, object(), None): + with self.assertRaises(TypeError): + draw.line(surf, col, (0, 0), (1, 1)) + + with self.assertRaises(TypeError): + draw.aaline(surf, col, (0, 0), (1, 1)) + + with self.assertRaises(TypeError): + draw.aalines(surf, col, True, points) + + with self.assertRaises(TypeError): + draw.lines(surf, col, True, points) + + with self.assertRaises(TypeError): + draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150) + + with self.assertRaises(TypeError): + draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1) + + with self.assertRaises(TypeError): + draw.circle(surf, col, (7, 3), 2) + + with self.assertRaises(TypeError): + draw.polygon(surf, col, points, 0) ############################################################################### -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/event_test.py b/venv/Lib/site-packages/pygame/tests/event_test.py index fccb4cf512f00e63aa7d427074be41d86a362811..896498a4a93e7476af3f0680be19f1bf91765cc8 100644 --- a/venv/Lib/site-packages/pygame/tests/event_test.py +++ b/venv/Lib/site-packages/pygame/tests/event_test.py @@ -1,14 +1,13 @@ -import os +import collections +import time import unittest +import os import pygame -from pygame.compat import as_unicode - -################################################################################ -events = ( -# pygame.NOEVENT, -# pygame.ACTIVEEVENT, +EVENT_TYPES = ( + # pygame.NOEVENT, + # pygame.ACTIVEEVENT, pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION, @@ -24,20 +23,74 @@ events = ( pygame.QUIT, pygame.SYSWMEVENT, pygame.USEREVENT, -# pygame.NUMEVENTS, + # pygame.NUMEVENTS, +) + +EVENT_TEST_PARAMS = collections.defaultdict(dict) +EVENT_TEST_PARAMS.update( + { + pygame.KEYDOWN: {"key": pygame.K_SPACE}, + pygame.KEYUP: {"key": pygame.K_SPACE}, + pygame.MOUSEMOTION: dict(), + pygame.MOUSEBUTTONDOWN: dict(button=1), + pygame.MOUSEBUTTONUP: dict(button=1), + } +) + + +NAMES_AND_EVENTS = ( + ("NoEvent", pygame.NOEVENT), + ("ActiveEvent", pygame.ACTIVEEVENT), + ("KeyDown", pygame.KEYDOWN), + ("KeyUp", pygame.KEYUP), + ("MouseMotion", pygame.MOUSEMOTION), + ("MouseButtonDown", pygame.MOUSEBUTTONDOWN), + ("MouseButtonUp", pygame.MOUSEBUTTONUP), + ("JoyAxisMotion", pygame.JOYAXISMOTION), + ("JoyBallMotion", pygame.JOYBALLMOTION), + ("JoyHatMotion", pygame.JOYHATMOTION), + ("JoyButtonDown", pygame.JOYBUTTONDOWN), + ("JoyButtonUp", pygame.JOYBUTTONUP), + ("VideoResize", pygame.VIDEORESIZE), + ("VideoExpose", pygame.VIDEOEXPOSE), + ("Quit", pygame.QUIT), + ("SysWMEvent", pygame.SYSWMEVENT), + ("MidiIn", pygame.MIDIIN), + ("MidiOut", pygame.MIDIOUT), + ("UserEvent", pygame.USEREVENT), + ("Unknown", 0xFFFF), + ("FingerMotion", pygame.FINGERMOTION), + ("FingerDown", pygame.FINGERDOWN), + ("FingerUp", pygame.FINGERUP), + ("MultiGesture", pygame.MULTIGESTURE), + ("MouseWheel", pygame.MOUSEWHEEL), + ("TextInput", pygame.TEXTINPUT), + ("TextEditing", pygame.TEXTEDITING), + ("ControllerAxisMotion", pygame.CONTROLLERAXISMOTION), + ("ControllerButtonDown", pygame.CONTROLLERBUTTONDOWN), + ("ControllerButtonUp", pygame.CONTROLLERBUTTONUP), + ("ControllerDeviceAdded", pygame.CONTROLLERDEVICEADDED), + ("ControllerDeviceRemoved", pygame.CONTROLLERDEVICEREMOVED), + ("ControllerDeviceMapped", pygame.CONTROLLERDEVICEREMAPPED), + ("DropFile", pygame.DROPFILE), + ("AudioDeviceAdded", pygame.AUDIODEVICEADDED), + ("AudioDeviceRemoved", pygame.AUDIODEVICEREMOVED), + ("DropText", pygame.DROPTEXT), + ("DropBegin", pygame.DROPBEGIN), + ("DropComplete", pygame.DROPCOMPLETE), ) class EventTypeTest(unittest.TestCase): def test_Event(self): """Ensure an Event object can be created.""" - e = pygame.event.Event(pygame.USEREVENT, some_attr=1, other_attr='1') + e = pygame.event.Event(pygame.USEREVENT, some_attr=1, other_attr="1") self.assertEqual(e.some_attr, 1) self.assertEqual(e.other_attr, "1") - # Event now uses tp_dictoffset and tp_members: request 62 - # on Motherhamster Bugzilla. + # Event now uses tp_dictoffset and tp_members: + # https://github.com/pygame/pygame/issues/62 self.assertEqual(e.type, pygame.USEREVENT) self.assertIs(e.dict, e.__dict__) @@ -49,30 +102,56 @@ class EventTypeTest(unittest.TestCase): self.assertEqual(e.new_attr, 15) - # For Python 2.x a TypeError is raised for a readonly member; - # for Python 3.x it is an AttributeError. - self.assertRaises((TypeError, AttributeError), setattr, e, 'type', 0) - self.assertRaises((TypeError, AttributeError), setattr, e, 'dict', None) + self.assertRaises(AttributeError, setattr, e, "type", 0) + self.assertRaises(AttributeError, setattr, e, "dict", None) # Ensure attributes are visible to dir(), part of the original # posted request. d = dir(e) - attrs = ('type', 'dict', '__dict__', 'some_attr', 'other_attr', - 'new_attr') + attrs = ("type", "dict", "__dict__", "some_attr", "other_attr", "new_attr") for attr in attrs: self.assertIn(attr, d) + # redundant type field as kwarg + self.assertRaises(ValueError, pygame.event.Event, 10, type=100) + def test_as_str(self): # Bug reported on Pygame mailing list July 24, 2011: # For Python 3.x str(event) to raises an UnicodeEncodeError when # an event attribute is a string with a non-ascii character. try: - str(pygame.event.Event(events[0], a=as_unicode(r"\xed"))) + str(pygame.event.Event(EVENT_TYPES[0], a="\xed")) except UnicodeEncodeError: self.fail("Event object raised exception for non-ascii character") # Passed. + def test_event_bool(self): + self.assertFalse(pygame.event.Event(pygame.NOEVENT)) + for event_type in [ + pygame.MOUSEBUTTONDOWN, + pygame.ACTIVEEVENT, + pygame.WINDOWLEAVE, + pygame.USEREVENT_DROPFILE, + ]: + self.assertTrue(pygame.event.Event(event_type)) + + def test_event_equality(self): + """Ensure that events can be compared correctly.""" + a = pygame.event.Event(EVENT_TYPES[0], a=1) + b = pygame.event.Event(EVENT_TYPES[0], a=1) + c = pygame.event.Event(EVENT_TYPES[1], a=1) + d = pygame.event.Event(EVENT_TYPES[0], a=2) + + self.assertTrue(a == a) + self.assertFalse(a != a) + self.assertTrue(a == b) + self.assertFalse(a != b) + self.assertTrue(a != c) + self.assertFalse(a == c) + self.assertTrue(a != d) + self.assertFalse(a == d) + race_condition_notification = """ This test is dependent on timing. The event queue is cleared in preparation for @@ -80,6 +159,7 @@ tests. There is a small window where outside events from the OS may have effecte results. Try running the test again. """ + class EventModuleArgsTest(unittest.TestCase): def setUp(self): pygame.display.init() @@ -96,8 +176,13 @@ class EventModuleArgsTest(unittest.TestCase): pygame.event.get(pump=False) pygame.event.get(pump=True) pygame.event.get(eventtype=None) - pygame.event.get(eventtype=pygame.USEREVENT, - pump=False) + pygame.event.get(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.get(eventtype=pygame.USEREVENT, pump=False) + + # event type out of range + self.assertRaises(ValueError, pygame.event.get, 0x00010000) + self.assertRaises(TypeError, pygame.event.get, 1 + 2j) + self.assertRaises(TypeError, pygame.event.get, "foo") def test_clear(self): pygame.event.clear() @@ -107,8 +192,12 @@ class EventModuleArgsTest(unittest.TestCase): pygame.event.clear(pump=False) pygame.event.clear(pump=True) pygame.event.clear(eventtype=None) - pygame.event.clear(eventtype=pygame.USEREVENT, - pump=False) + pygame.event.clear(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.clear(eventtype=pygame.USEREVENT, pump=False) + + # event type out of range + self.assertRaises(ValueError, pygame.event.clear, 0x0010FFFFF) + self.assertRaises(TypeError, pygame.event.get, ["a", "b", "c"]) def test_peek(self): pygame.event.peek() @@ -118,11 +207,101 @@ class EventModuleArgsTest(unittest.TestCase): pygame.event.peek(pump=False) pygame.event.peek(pump=True) pygame.event.peek(eventtype=None) - pygame.event.peek(eventtype=pygame.USEREVENT, - pump=False) + pygame.event.peek(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) + pygame.event.peek(eventtype=pygame.USEREVENT, pump=False) + + class Foo: + pass + + # event type out of range + self.assertRaises(ValueError, pygame.event.peek, -1) + self.assertRaises(ValueError, pygame.event.peek, [-10]) + self.assertRaises(TypeError, pygame.event.peek, Foo()) + + +class EventCustomTypeTest(unittest.TestCase): + """Those tests are special in that they need the _custom_event counter to + be reset before and/or after being run.""" + + def setUp(self): + pygame.quit() + pygame.init() + pygame.display.init() + + def tearDown(self): + pygame.quit() + + def test_custom_type(self): + self.assertEqual(pygame.event.custom_type(), pygame.USEREVENT + 1) + atype = pygame.event.custom_type() + atype2 = pygame.event.custom_type() + + self.assertEqual(atype, atype2 - 1) + + ev = pygame.event.Event(atype) + pygame.event.post(ev) + queue = pygame.event.get(atype) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, atype) + + def test_custom_type__end_boundary(self): + """Ensure custom_type() raises error when no more custom types. + + The last allowed custom type number should be (pygame.NUMEVENTS - 1). + """ + last = -1 + start = pygame.event.custom_type() + 1 + for _ in range(start, pygame.NUMEVENTS): + last = pygame.event.custom_type() + + self.assertEqual(last, pygame.NUMEVENTS - 1) + with self.assertRaises(pygame.error): + pygame.event.custom_type() + + def test_custom_type__reset(self): + """Ensure custom events get 'deregistered' by quit().""" + before = pygame.event.custom_type() + self.assertEqual(before, pygame.event.custom_type() - 1) + pygame.quit() + pygame.init() + pygame.display.init() + self.assertEqual(before, pygame.event.custom_type()) class EventModuleTest(unittest.TestCase): + def _assertCountEqual(self, *args, **kwargs): + # Handle method name differences between Python versions. + # Is this still needed? + self.assertCountEqual(*args, **kwargs) + + def _assertExpectedEvents(self, expected, got): + """Find events like expected events, raise on unexpected or missing, + ignore additional event properties if expected properties are present.""" + + # This does greedy matching, don't encode an NP-hard problem + # into your input data, *please* + items_left = got[:] + for expected_element in expected: + for item in items_left: + for key in expected_element.__dict__: + if item.__dict__[key] != expected_element.__dict__[key]: + break + else: + # found item! + items_left.remove(item) + break + else: + raise AssertionError( + "Expected " + + str(expected_element) + + " among remaining events " + + str(items_left) + + " out of " + + str(got) + ) + if len(items_left) > 0: + raise AssertionError("Unexpected Events: " + str(items_left)) + def setUp(self): pygame.display.init() pygame.event.clear() # flush events @@ -131,52 +310,129 @@ class EventModuleTest(unittest.TestCase): pygame.event.clear() # flush events pygame.display.quit() + def test_event_numevents(self): + """Ensures NUMEVENTS does not exceed the maximum SDL number of events.""" + # Ref: https://www.libsdl.org/tmp/SDL/include/SDL_events.h + MAX_SDL_EVENTS = 0xFFFF # SDL_LASTEVENT = 0xFFFF + + self.assertLessEqual(pygame.NUMEVENTS, MAX_SDL_EVENTS) + def test_event_attribute(self): - e1 = pygame.event.Event(pygame.USEREVENT, attr1='attr1') - self.assertEqual(e1.attr1, 'attr1') + e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1") + self.assertEqual(e1.attr1, "attr1") def test_set_blocked(self): """Ensure events can be blocked from the queue.""" - event = events[0] + event = EVENT_TYPES[0] + unblocked_event = EVENT_TYPES[1] pygame.event.set_blocked(event) self.assertTrue(pygame.event.get_blocked(event)) + self.assertFalse(pygame.event.get_blocked(unblocked_event)) + + posted = pygame.event.post( + pygame.event.Event(event, **EVENT_TEST_PARAMS[event]) + ) + self.assertFalse(posted) + + # post an unblocked event + posted = pygame.event.post( + pygame.event.Event(unblocked_event, **EVENT_TEST_PARAMS[unblocked_event]) + ) + self.assertTrue(posted) - pygame.event.post(pygame.event.Event(event)) ret = pygame.event.get() should_be_blocked = [e for e in ret if e.type == event] + should_be_allowed_types = [e.type for e in ret if e.type != event] self.assertEqual(should_be_blocked, []) + self.assertTrue(unblocked_event in should_be_allowed_types) + + def test_set_blocked__event_sequence(self): + """Ensure a sequence of event types can be blocked.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + pygame.WINDOWFOCUSLOST, + pygame.USEREVENT, + ] + + pygame.event.set_blocked(event_types) + + for etype in event_types: + self.assertTrue(pygame.event.get_blocked(etype)) def test_set_blocked_all(self): """Ensure all events can be unblocked at once.""" pygame.event.set_blocked(None) - for e in events: + for e in EVENT_TYPES: self.assertTrue(pygame.event.get_blocked(e)) def test_post__and_poll(self): """Ensure events can be posted to the queue.""" - e1 = pygame.event.Event(pygame.USEREVENT, attr1='attr1') + e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1") pygame.event.post(e1) posted_event = pygame.event.poll() - self.assertEqual(e1.attr1, posted_event.attr1, - race_condition_notification) + self.assertEqual(e1.attr1, posted_event.attr1, race_condition_notification) # fuzzing event types - for i in range(1, 11): - pygame.event.post(pygame.event.Event(events[i])) + for i in range(1, 13): + pygame.event.post( + pygame.event.Event(EVENT_TYPES[i], **EVENT_TEST_PARAMS[EVENT_TYPES[i]]) + ) + + self.assertEqual( + pygame.event.poll().type, EVENT_TYPES[i], race_condition_notification + ) + + def test_post_and_get_keydown(self): + """Ensure keydown events can be posted to the queue.""" + activemodkeys = pygame.key.get_mods() + + events = [ + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_p), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_y, mod=activemodkeys), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_g, unicode="g"), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a, unicode=None), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_m, mod=None, window=None), + pygame.event.Event( + pygame.KEYDOWN, key=pygame.K_e, mod=activemodkeys, unicode="e" + ), + ] - self.assertEqual(pygame.event.poll().type, events[i], - race_condition_notification) + for e in events: + pygame.event.post(e) + posted_event = pygame.event.poll() + self.assertEqual(e, posted_event, race_condition_notification) def test_post_large_user_event(self): - pygame.event.post(pygame.event.Event(pygame.USEREVENT, {'a': "a" * 1024})) + pygame.event.post( + pygame.event.Event( + pygame.USEREVENT, {"a": "a" * 1024}, test=list(range(100)) + ) + ) e = pygame.event.poll() self.assertEqual(e.type, pygame.USEREVENT) self.assertEqual(e.a, "a" * 1024) + self.assertEqual(e.test, list(range(100))) + + def test_post_blocked(self): + """ + Test blocked events are not posted. Also test whether post() + returns a boolean correctly + """ + pygame.event.set_blocked(pygame.USEREVENT) + self.assertFalse(pygame.event.post(pygame.event.Event(pygame.USEREVENT))) + self.assertFalse(pygame.event.poll()) + pygame.event.set_allowed(pygame.USEREVENT) + self.assertTrue(pygame.event.post(pygame.event.Event(pygame.USEREVENT))) + self.assertEqual(pygame.event.poll(), pygame.event.Event(pygame.USEREVENT)) def test_get(self): """Ensure get() retrieves all the events on the queue.""" @@ -196,11 +452,117 @@ class EventModuleTest(unittest.TestCase): self.assertEqual(len(queue), 1) self.assertEqual(queue[0].type, pygame.USEREVENT) + TESTEVENTS = 10 + for _ in range(TESTEVENTS): + pygame.event.post(ev) + q = pygame.event.get([pygame.USEREVENT]) + self.assertEqual(len(q), TESTEVENTS) + for event in q: + self.assertEqual(event, ev) + + def test_get_exclude_throw(self): + self.assertRaises( + pygame.error, pygame.event.get, pygame.KEYDOWN, False, pygame.KEYUP + ) + + def test_get_exclude(self): + pygame.event.post(pygame.event.Event(pygame.USEREVENT)) + pygame.event.post(pygame.event.Event(pygame.KEYDOWN)) + + queue = pygame.event.get(exclude=pygame.KEYDOWN) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, pygame.USEREVENT) + + pygame.event.post(pygame.event.Event(pygame.KEYUP)) + pygame.event.post(pygame.event.Event(pygame.USEREVENT)) + queue = pygame.event.get(exclude=(pygame.KEYDOWN, pygame.KEYUP)) + self.assertEqual(len(queue), 1) + self.assertEqual(queue[0].type, pygame.USEREVENT) + + queue = pygame.event.get() + self.assertEqual(len(queue), 2) + + def test_get__empty_queue(self): + """Ensure get() works correctly on an empty queue.""" + expected_events = [] + pygame.event.clear() + + # Ensure all events can be checked. + retrieved_events = pygame.event.get() + + self.assertListEqual(retrieved_events, expected_events) + + # Ensure events can be checked individually. + for event_type in EVENT_TYPES: + retrieved_events = pygame.event.get(event_type) + + self.assertListEqual(retrieved_events, expected_events) + + # Ensure events can be checked as a sequence. + retrieved_events = pygame.event.get(EVENT_TYPES) + + self.assertListEqual(retrieved_events, expected_events) + + def test_get__event_sequence(self): + """Ensure get() can handle a sequence of event types.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + other_event_type = pygame.MOUSEBUTTONUP + + # Test when no events in the queue. + expected_events = [] + pygame.event.clear() + retrieved_events = pygame.event.get(event_types) + + # don't use self._assertCountEqual here. This checks for + # expected properties in events, and ignores unexpected ones, for + # forward compatibility with SDL2. + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + # Test when an event type not in the list is in the queue. + expected_events = [] + pygame.event.clear() + pygame.event.post( + pygame.event.Event(other_event_type, **EVENT_TEST_PARAMS[other_event_type]) + ) + + retrieved_events = pygame.event.get(event_types) + + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + # Test when 1 event type in the list is in the queue. + expected_events = [ + pygame.event.Event(event_types[0], **EVENT_TEST_PARAMS[event_types[0]]) + ] + pygame.event.clear() + pygame.event.post(expected_events[0]) + + retrieved_events = pygame.event.get(event_types) + + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + # Test all events in the list are in the queue. + pygame.event.clear() + expected_events = [] + + for etype in event_types: + expected_events.append( + pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype]) + ) + pygame.event.post(expected_events[-1]) + + retrieved_events = pygame.event.get(event_types) + + self._assertExpectedEvents(expected=expected_events, got=retrieved_events) + + def test_get_clears_queue(self): + """Ensure get() clears the event queue after a call""" + pygame.event.get() # should clear the queue completely by getting all events + self.assertEqual(pygame.event.get(), []) + def test_clear(self): """Ensure clear() removes all the events on the queue.""" - for e in events: - pygame.event.post(pygame.event.Event(e)) - + for e in EVENT_TYPES: + pygame.event.post(pygame.event.Event(e, **EVENT_TEST_PARAMS[e])) poll_event = pygame.event.poll() self.assertNotEqual(poll_event.type, pygame.NOEVENT) @@ -208,42 +570,159 @@ class EventModuleTest(unittest.TestCase): pygame.event.clear() poll_event = pygame.event.poll() - self.assertEqual(poll_event.type, pygame.NOEVENT, - race_condition_notification) + self.assertEqual(poll_event.type, pygame.NOEVENT, race_condition_notification) - def test_event_name(self): - """Ensure event_name() returns the correct event name.""" - self.assertEqual(pygame.event.event_name(pygame.KEYDOWN), "KeyDown") - self.assertEqual(pygame.event.event_name(pygame.USEREVENT), - "UserEvent") + def test_clear__empty_queue(self): + """Ensure clear() works correctly on an empty queue.""" + expected_events = [] + pygame.event.clear() - def test_wait(self): - """Ensure wait() waits for an event on the queue.""" - event = pygame.event.Event(events[0]) - pygame.event.post(event) - wait_event = pygame.event.wait() + # Test calling clear() on an already empty queue. + pygame.event.clear() - self.assertEqual(wait_event.type, event.type) + retrieved_events = pygame.event.get() + + self.assertListEqual(retrieved_events, expected_events) + + def test_clear__event_sequence(self): + """Ensure a sequence of event types can be cleared from the queue.""" + cleared_event_types = EVENT_TYPES[:5] + expected_event_types = EVENT_TYPES[5:10] + expected_events = [] + + # Add the events to the queue. + for etype in cleared_event_types: + pygame.event.post(pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype])) + + for etype in expected_events: + expected_events.append( + pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype]) + ) + pygame.event.post(expected_events[-1]) + + # Clear the cleared_events from the queue. + pygame.event.clear(cleared_event_types) + + # Check the rest of the events in the queue. + remaining_events = pygame.event.get() + + self._assertCountEqual(remaining_events, expected_events) + + def test_event_name(self): + """Ensure event_name() returns the correct event name.""" + for expected_name, event in NAMES_AND_EVENTS: + self.assertEqual( + pygame.event.event_name(event), expected_name, f"0x{event:X}" + ) + + def test_event_name__userevent_range(self): + """Ensures event_name() returns the correct name for user events. + + Tests the full range of user events. + """ + expected_name = "UserEvent" + + for event in range(pygame.USEREVENT, pygame.NUMEVENTS): + self.assertEqual( + pygame.event.event_name(event), expected_name, f"0x{event:X}" + ) + + def test_event_name__userevent_boundary(self): + """Ensures event_name() does not return 'UserEvent' for events + just outside the user event range. + """ + unexpected_name = "UserEvent" + + for event in (pygame.USEREVENT - 1, pygame.NUMEVENTS): + self.assertNotEqual( + pygame.event.event_name(event), unexpected_name, f"0x{event:X}" + ) + + def test_event_name__kwargs(self): + """Ensure event_name() returns the correct event name when kwargs used.""" + for expected_name, event in NAMES_AND_EVENTS: + self.assertEqual( + pygame.event.event_name(type=event), expected_name, f"0x{event:X}" + ) def test_peek(self): """Ensure queued events can be peeked at.""" event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] for event_type in event_types: - pygame.event.post(pygame.event.Event(event_type)) + pygame.event.post( + pygame.event.Event(event_type, **EVENT_TEST_PARAMS[event_type]) + ) + # Ensure events can be checked individually. for event_type in event_types: self.assertTrue(pygame.event.peek(event_type)) + # Ensure events can be checked as a sequence. self.assertTrue(pygame.event.peek(event_types)) - def test_peek_empty(self): + def test_peek__event_sequence(self): + """Ensure peek() can handle a sequence of event types.""" + event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION] + other_event_type = pygame.MOUSEBUTTONUP + + # Test when no events in the queue. + pygame.event.clear() + peeked = pygame.event.peek(event_types) + + self.assertFalse(peeked) + + # Test when an event type not in the list is in the queue. + pygame.event.clear() + pygame.event.post( + pygame.event.Event(other_event_type, **EVENT_TEST_PARAMS[other_event_type]) + ) + + peeked = pygame.event.peek(event_types) + + self.assertFalse(peeked) + + # Test when 1 event type in the list is in the queue. + pygame.event.clear() + pygame.event.post( + pygame.event.Event(event_types[0], **EVENT_TEST_PARAMS[event_types[0]]) + ) + + peeked = pygame.event.peek(event_types) + + self.assertTrue(peeked) + + # Test all events in the list are in the queue. pygame.event.clear() - self.assertFalse(pygame.event.peek()) + for etype in event_types: + pygame.event.post(pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype])) + + peeked = pygame.event.peek(event_types) + + self.assertTrue(peeked) + + def test_peek__empty_queue(self): + """Ensure peek() works correctly on an empty queue.""" + pygame.event.clear() + + # Ensure all events can be checked. + peeked = pygame.event.peek() + + self.assertFalse(peeked) + + # Ensure events can be checked individually. + for event_type in EVENT_TYPES: + peeked = pygame.event.peek(event_type) + self.assertFalse(peeked) + + # Ensure events can be checked as a sequence. + peeked = pygame.event.peek(EVENT_TYPES) + + self.assertFalse(peeked) def test_set_allowed(self): """Ensure a blocked event type can be unblocked/allowed.""" - event = events[0] + event = EVENT_TYPES[0] pygame.event.set_blocked(event) self.assertTrue(pygame.event.get_blocked(event)) @@ -252,27 +731,51 @@ class EventModuleTest(unittest.TestCase): self.assertFalse(pygame.event.get_blocked(event)) + def test_set_allowed__event_sequence(self): + """Ensure a sequence of blocked event types can be unblocked/allowed.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + ] + pygame.event.set_blocked(event_types) + + pygame.event.set_allowed(event_types) + + for etype in event_types: + self.assertFalse(pygame.event.get_blocked(etype)) + def test_set_allowed_all(self): """Ensure all events can be unblocked/allowed at once.""" pygame.event.set_blocked(None) - for e in events: + for e in EVENT_TYPES: self.assertTrue(pygame.event.get_blocked(e)) pygame.event.set_allowed(None) - for e in events: + for e in EVENT_TYPES: self.assertFalse(pygame.event.get_blocked(e)) def test_pump(self): """Ensure pump() functions properly.""" pygame.event.pump() - @unittest.skipIf(os.environ.get('SDL_VIDEODRIVER') == 'dummy', - 'requires the SDL_VIDEODRIVER to be a non "dummy" value') + # @unittest.skipIf( + # os.environ.get("SDL_VIDEODRIVER") == "dummy", + # 'requires the SDL_VIDEODRIVER to be a non "dummy" value', + # ) + # Fails on SDL 2.0.18 + @unittest.skip("flaky test, and broken on 2.0.18 windows") def test_set_grab__and_get_symmetric(self): - """Ensure event grabbing can be enabled and disabled.""" - surf = pygame.display.set_mode((10,10)) + """Ensure event grabbing can be enabled and disabled. + + WARNING: Moving the mouse off the display during this test can cause it + to fail. + """ + surf = pygame.display.set_mode((10, 10)) pygame.event.set_grab(True) self.assertTrue(pygame.event.get_grab()) @@ -281,60 +784,166 @@ class EventModuleTest(unittest.TestCase): self.assertFalse(pygame.event.get_grab()) - def test_event_equality(self): - a = pygame.event.Event(events[0], a=1) - b = pygame.event.Event(events[0], a=1) - c = pygame.event.Event(events[1], a=1) - d = pygame.event.Event(events[0], a=2) + def test_get_blocked(self): + """Ensure an event's blocked state can be retrieved.""" + # Test each event is not blocked. + pygame.event.set_allowed(None) - self.assertTrue(a == a) - self.assertFalse(a != a) - self.assertTrue(a == b) - self.assertFalse(a != b) - self.assertTrue(a != c) - self.assertFalse(a == c) - self.assertTrue(a != d) - self.assertFalse(a == d) + for etype in EVENT_TYPES: + blocked = pygame.event.get_blocked(etype) + + self.assertFalse(blocked) + + # Test each event type is blocked. + pygame.event.set_blocked(None) + + for etype in EVENT_TYPES: + blocked = pygame.event.get_blocked(etype) + + self.assertTrue(blocked) - def todo_test_get_blocked(self): + def test_get_blocked__event_sequence(self): + """Ensure get_blocked() can handle a sequence of event types.""" + event_types = [ + pygame.KEYDOWN, + pygame.KEYUP, + pygame.MOUSEMOTION, + pygame.MOUSEBUTTONDOWN, + pygame.MOUSEBUTTONUP, + pygame.WINDOWMINIMIZED, + pygame.USEREVENT, + ] - # __doc__ (as of 2008-08-02) for pygame.event.get_blocked: + # Test no event types in the list are blocked. + blocked = pygame.event.get_blocked(event_types) - # pygame.event.get_blocked(type): return bool - # test if a type of event is blocked from the queue - # - # Returns true if the given event type is blocked from the queue. + self.assertFalse(blocked) - self.fail() + # Test when 1 event type in the list is blocked. + pygame.event.set_blocked(event_types[2]) - def todo_test_get_grab(self): + blocked = pygame.event.get_blocked(event_types) - # __doc__ (as of 2008-08-02) for pygame.event.get_grab: + self.assertTrue(blocked) + + # Test all event types in the list are blocked. + pygame.event.set_blocked(event_types) + + blocked = pygame.event.get_blocked(event_types) + + self.assertTrue(blocked) + + # @unittest.skipIf( + # os.environ.get("SDL_VIDEODRIVER") == "dummy", + # 'requires the SDL_VIDEODRIVER to be a non "dummy" value', + # ) + # Fails on SDL 2.0.18 + @unittest.skip("flaky test, and broken on 2.0.18 windows") + def test_get_grab(self): + """Ensure get_grab() works as expected""" + surf = pygame.display.set_mode((10, 10)) + # Test 5 times + for i in range(5): + pygame.event.set_grab(i % 2) + self.assertEqual(pygame.event.get_grab(), i % 2) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + "requires the SDL_VIDEODRIVER to be a non dummy value", + ) + @unittest.skipIf(pygame.get_sdl_version() < (2, 0, 16), "Needs at least SDL 2.0.16") + def test_set_keyboard_grab_and_get_keyboard_grab(self): + """Ensure set_keyboard_grab() and get_keyboard_grab() work as expected""" + + surf = pygame.display.set_mode((10, 10)) + + pygame.event.set_keyboard_grab(True) + self.assertTrue(pygame.event.get_keyboard_grab()) + + pygame.event.set_keyboard_grab(False) + self.assertFalse(pygame.event.get_keyboard_grab()) + + def test_poll(self): + """Ensure poll() works as expected""" + pygame.event.clear() + ev = pygame.event.poll() + # poll() on empty queue should return NOEVENT + self.assertEqual(ev.type, pygame.NOEVENT) + + # test poll returns stuff in same order + e1 = pygame.event.Event(pygame.USEREVENT) + e2 = pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a) + e3 = pygame.event.Event(pygame.KEYUP, key=pygame.K_a) + pygame.event.post(e1) + pygame.event.post(e2) + pygame.event.post(e3) + + self.assertEqual(pygame.event.poll().type, e1.type) + self.assertEqual(pygame.event.poll().type, e2.type) + self.assertEqual(pygame.event.poll().type, e3.type) + self.assertEqual(pygame.event.poll().type, pygame.NOEVENT) + + +class EventModuleTestsWithTiming(unittest.TestCase): + __tags__ = ["timing"] + + def setUp(self): + pygame.display.init() + pygame.event.clear() # flush events + + def tearDown(self): + pygame.event.clear() # flush events + pygame.display.quit() + + def test_event_wait(self): + """Ensure wait() waits for an event on the queue.""" + # Test case without timeout. + event = pygame.event.Event(EVENT_TYPES[0], **EVENT_TEST_PARAMS[EVENT_TYPES[0]]) + pygame.event.post(event) + wait_event = pygame.event.wait() + + self.assertEqual(wait_event.type, event.type) + + # Test case with timeout and no event in the queue. + wait_event = pygame.event.wait(100) + self.assertEqual(wait_event.type, pygame.NOEVENT) + + # Test case with timeout and an event in the queue. + event = pygame.event.Event(EVENT_TYPES[0], **EVENT_TEST_PARAMS[EVENT_TYPES[0]]) + pygame.event.post(event) + wait_event = pygame.event.wait(100) + + self.assertEqual(wait_event.type, event.type) - # pygame.event.get_grab(): return bool - # test if the program is sharing input devices - # - # Returns true when the input events are grabbed for this application. - # Use pygame.event.set_grab() to control this state. - # + # test wait with timeout waits for the correct duration + pygame.time.set_timer(pygame.USEREVENT, 50, 3) - self.fail() + for wait_time, expected_type, expected_time in ( + (60, pygame.USEREVENT, 50), + (65, pygame.USEREVENT, 50), + (20, pygame.NOEVENT, 20), + (45, pygame.USEREVENT, 30), + (70, pygame.NOEVENT, 70), + ): + start_time = time.perf_counter() + self.assertEqual(pygame.event.wait(wait_time).type, expected_type) + self.assertAlmostEqual( + time.perf_counter() - start_time, expected_time / 1000, delta=0.01 + ) - def todo_test_poll(self): + # test wait without timeout waits for the full duration + pygame.time.set_timer(pygame.USEREVENT, 100, 1) - # __doc__ (as of 2008-08-02) for pygame.event.poll: + start_time = time.perf_counter() + self.assertEqual(pygame.event.wait().type, pygame.USEREVENT) + self.assertAlmostEqual(time.perf_counter() - start_time, 0.1, delta=0.01) - # pygame.event.poll(): return Event - # get a single event from the queue - # - # Returns a single event from the queue. If the event queue is empty - # an event of type pygame.NOEVENT will be returned immediately. The - # returned event is removed from the queue. - # + # test wait returns no event if event is arriving later + pygame.time.set_timer(pygame.USEREVENT, 50, 1) + self.assertEqual(pygame.event.wait(40).type, pygame.NOEVENT) - self.fail() ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/fastevent_tags.py b/venv/Lib/site-packages/pygame/tests/fastevent_tags.py deleted file mode 100644 index c660bef5d9b02cf7ed56ce2938c37078d024601d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/fastevent_tags.py +++ /dev/null @@ -1 +0,0 @@ -__tags__ = [] diff --git a/venv/Lib/site-packages/pygame/tests/fastevent_test.py b/venv/Lib/site-packages/pygame/tests/fastevent_test.py deleted file mode 100644 index 34723a56999fe236b449b3dabb78d46e9c82cb67..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/fastevent_test.py +++ /dev/null @@ -1,150 +0,0 @@ -import unittest -from pygame.tests.event_test import race_condition_notification -import pygame -from pygame import event, fastevent -from pygame.compat import geterror - -################################################################################ - -class FasteventModuleTest(unittest.TestCase): - - def setUp(self): - pygame.display.init() - fastevent.init() - event.clear() - - def tearDown(self): - # fastevent.quit() # Does not exist! - pygame.display.quit() - - def test_init(self): - # Test if module initialized after multiple init() calls. - fastevent.init() - fastevent.init() - - self.assertTrue(fastevent.get_init()) - - def test_auto_quit(self): - # Test if module uninitialized after calling pygame.quit(). - pygame.quit() - - self.assertFalse(fastevent.get_init()) - - def test_get_init(self): - # Test if get_init() gets the init state. - self.assertTrue(fastevent.get_init()) - - def test_get(self): - # __doc__ (as of 2008-08-02) for pygame.fastevent.get: - - # pygame.fastevent.get() -> list of Events - # get all events from the queue - - for _ in range(1, 11): - event.post(event.Event(pygame.USEREVENT)) - - self.assertListEqual([e.type for e in fastevent.get()], - [pygame.USEREVENT] * 10, - race_condition_notification) - - def test_poll(self): - - # __doc__ (as of 2008-08-02) for pygame.fastevent.poll: - - # pygame.fastevent.poll() -> Event - # get an available event - # - # Returns next event on queue. If there is no event waiting on the - # queue, this will return an event with type NOEVENT. - - self.assertEqual(fastevent.poll().type, pygame.NOEVENT, - race_condition_notification) - - def test_post(self): - - # __doc__ (as of 2008-08-02) for pygame.fastevent.post: - - # pygame.fastevent.post(Event) -> None - # place an event on the queue - # - # This will post your own event objects onto the event queue. - # You can past any event type you want, but some care must be - # taken. For example, if you post a MOUSEBUTTONDOWN event to the - # queue, it is likely any code receiving the event will expect - # the standard MOUSEBUTTONDOWN attributes to be available, like - # 'pos' and 'button'. - # - # Because pygame.fastevent.post() may have to wait for the queue - # to empty, you can get into a dead lock if you try to append an - # event on to a full queue from the thread that processes events. - # For that reason I do not recommend using this function in the - # main thread of an SDL program. - - for _ in range(1, 11): - fastevent.post(event.Event(pygame.USEREVENT)) - - self.assertListEqual([e.type for e in event.get()], - [pygame.USEREVENT] * 10, - race_condition_notification) - - try: - # Special case for post: METH_O. - fastevent.post(1) - except TypeError: - e = geterror() - msg = ("argument 1 must be %s, not %s" % - (fastevent.Event.__name__, type(1).__name__)) - self.assertEqual(str(e), msg) - else: - self.fail() - - def test_post__clear(self): - """Ensure posted events can be cleared.""" - for _ in range(10): - fastevent.post(event.Event(pygame.USEREVENT)) - - event.clear() - - self.assertListEqual(fastevent.get(), []) - self.assertListEqual(event.get(), []) - - def todo_test_pump(self): - - # __doc__ (as of 2008-08-02) for pygame.fastevent.pump: - - # pygame.fastevent.pump() -> None - # update the internal messages - # - # For each frame of your game, you will need to make some sort - # of call to the event queue. This ensures your program can internally - # interact with the rest of the operating system. If you are not using - # other event functions in your game, you should call pump() to allow - # pygame to handle internal actions. - # - # There are important things that must be dealt with internally in the - # event queue. The main window may need to be repainted. Certain joysticks - # must be polled for their values. If you fail to make a call to the event - # queue for too long, the system may decide your program has locked up. - - self.fail() - - def test_wait(self): - - # __doc__ (as of 2008-08-02) for pygame.fastevent.wait: - - # pygame.fastevent.wait() -> Event - # wait for an event - # - # Returns the current event on the queue. If there are no messages - # waiting on the queue, this will not return until one is - # available. Sometimes it is important to use this wait to get - # events from the queue, it will allow your application to idle - # when the user isn't doing anything with it. - - event.post(pygame.event.Event(1)) - self.assertEqual(fastevent.wait().type, 1, race_condition_notification) - -################################################################################ - -if __name__ == '__main__': - unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/font_tags.py b/venv/Lib/site-packages/pygame/tests/font_tags.py deleted file mode 100644 index c660bef5d9b02cf7ed56ce2938c37078d024601d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/font_tags.py +++ /dev/null @@ -1 +0,0 @@ -__tags__ = [] diff --git a/venv/Lib/site-packages/pygame/tests/font_test.py b/venv/Lib/site-packages/pygame/tests/font_test.py index 9f5980782835986230e5bc6fa555cc5a010b4e1f..98d6989f3dbff62ab8271bc1644c05557e577d86 100644 --- a/venv/Lib/site-packages/pygame/tests/font_test.py +++ b/venv/Lib/site-packages/pygame/tests/font_test.py @@ -1,51 +1,64 @@ -# -*- coding: utf8 -*- - +# -*- coding: utf-8 -*- +from re import T import sys import os import unittest +import pathlib import platform import pygame from pygame import font as pygame_font # So font can be replaced with ftfont -from pygame.compat import as_unicode, unicode_, as_bytes, xrange_, filesystem_errors -from pygame.compat import PY_MAJOR_VERSION -FONTDIR = os.path.join(os.path.dirname (os.path.abspath (__file__)), - 'fixtures', 'fonts') -UCS_4 = sys.maxunicode > 0xFFFF +FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + def equal_images(s1, s2): size = s1.get_size() if s2.get_size() != size: return False w, h = size - for x in xrange_(w): - for y in xrange_(h): + for x in range(w): + for y in range(h): if s1.get_at((x, y)) != s2.get_at((x, y)): return False return True -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() -@unittest.skipIf(IS_PYPY, 'pypy skip known failure') # TODO -class FontModuleTest( unittest.TestCase ): - +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontModuleTest(unittest.TestCase): def setUp(self): pygame_font.init() def tearDown(self): pygame_font.quit() + def test_get_sdl_ttf_version(self): + def test_ver_tuple(ver): + self.assertIsInstance(ver, tuple) + self.assertEqual(len(ver), 3) + for i in ver: + self.assertIsInstance(i, int) + + if pygame_font.__name__ != "pygame.ftfont": + compiled = pygame_font.get_sdl_ttf_version() + linked = pygame_font.get_sdl_ttf_version(linked=True) + + test_ver_tuple(compiled) + test_ver_tuple(linked) + + self.assertTrue(linked >= compiled) + def test_SysFont(self): # Can only check that a font object is returned. fonts = pygame_font.get_fonts() - if 'arial' in fonts: + if "arial" in fonts: # Try to use arial font if it is there, rather than a random font # which can be different depending on installed fonts on the system. - font_name = 'arial' + font_name = "arial" else: font_name = sorted(fonts)[0] o = pygame_font.SysFont(font_name, 20) @@ -54,23 +67,23 @@ class FontModuleTest( unittest.TestCase ): self.assertTrue(isinstance(o, pygame_font.FontType)) o = pygame_font.SysFont(font_name, 20, bold=True) self.assertTrue(isinstance(o, pygame_font.FontType)) - o = pygame_font.SysFont('thisisnotafont', 20) + o = pygame_font.SysFont("thisisnotafont", 20) self.assertTrue(isinstance(o, pygame_font.FontType)) def test_get_default_font(self): - self.assertEqual(pygame_font.get_default_font(), 'freesansbold.ttf') + self.assertEqual(pygame_font.get_default_font(), "freesansbold.ttf") def test_get_fonts_returns_something(self): fnts = pygame_font.get_fonts() self.assertTrue(fnts) # to test if some files exist... - #def XXtest_has_file_osx_10_5_sdk(self): + # def XXtest_has_file_osx_10_5_sdk(self): # import os # f = "/Developer/SDKs/MacOSX10.5.sdk/usr/X11/include/ft2build.h" # self.assertEqual(os.path.exists(f), True) - #def XXtest_has_file_osx_10_4_sdk(self): + # def XXtest_has_file_osx_10_4_sdk(self): # import os # f = "/Developer/SDKs/MacOSX10.4u.sdk/usr/X11R6/include/ft2build.h" # self.assertEqual(os.path.exists(f), True) @@ -80,17 +93,10 @@ class FontModuleTest( unittest.TestCase ): self.assertTrue(fnts, msg=repr(fnts)) - if (PY_MAJOR_VERSION >= 3): - # For Python 3.x, names will always be unicode strings. - name_types = (str,) - else: - # For Python 2.x, names may be either unicode or ascii strings. - name_types = (str, unicode) - for name in fnts: # note, on ubuntu 2.6 they are all unicode strings. - self.assertTrue(isinstance(name, name_types), name) + self.assertTrue(isinstance(name, str), name) # Font names can be comprised of only numeric characters, so # just checking name.islower() will not work as expected here. self.assertFalse(any(c.isupper() for c in name)) @@ -112,42 +118,113 @@ class FontModuleTest( unittest.TestCase ): for font in fonts: path = pygame_font.match_font(font) self.assertFalse(path is None) - self.assertTrue(os.path.isabs(path)) + self.assertTrue(os.path.isabs(path) and os.path.isfile(path)) + + def test_match_font_name(self): + """That match_font accepts names of various types""" + font = pygame_font.get_fonts()[0] + font_path = pygame_font.match_font(font) + self.assertIsNotNone(font_path) + font_b = font.encode() + not_a_font = "thisisnotafont" + not_a_font_b = b"thisisnotafont" + good_font_names = [ + # Check single name bytes. + font_b, + # Check string of comma-separated names. + ",".join([not_a_font, font, not_a_font]), + # Check list of names. + [not_a_font, font, not_a_font], + # Check generator: + (name for name in [not_a_font, font, not_a_font]), + # Check comma-separated bytes. + b",".join([not_a_font_b, font_b, not_a_font_b]), + # Check list of bytes. + [not_a_font_b, font_b, not_a_font_b], + # Check mixed list of bytes and string. + [font, not_a_font, font_b, not_a_font_b], + ] + for font_name in good_font_names: + self.assertEqual(pygame_font.match_font(font_name), font_path, font_name) + + def test_not_match_font_name(self): + """match_font return None when names of various types do not exist""" + not_a_font = "thisisnotafont" + not_a_font_b = b"thisisnotafont" + bad_font_names = [ + not_a_font, + ",".join([not_a_font, not_a_font, not_a_font]), + [not_a_font, not_a_font, not_a_font], + (name for name in [not_a_font, not_a_font, not_a_font]), + not_a_font_b, + b",".join([not_a_font_b, not_a_font_b, not_a_font_b]), + [not_a_font_b, not_a_font_b, not_a_font_b], + [not_a_font, not_a_font_b, not_a_font], + ] + for font_name in bad_font_names: + self.assertIsNone(pygame_font.match_font(font_name), font_name) def test_match_font_bold(self): fonts = pygame_font.get_fonts() # Look for a bold font. - self.assertTrue(any(pygame_font.match_font(font, bold=True) - for font in fonts)) - + self.assertTrue(any(pygame_font.match_font(font, bold=True) for font in fonts)) def test_match_font_italic(self): fonts = pygame_font.get_fonts() # Look for an italic font. - self.assertTrue(any(pygame_font.match_font(font, italic=True) - for font in fonts)) - - def test_match_font_comma_separated(self): - fonts = pygame_font.get_fonts() - - # Check for not found. - self.assertTrue(pygame_font.match_font('thisisnotafont') is None) - - # Check comma separated list. - names = ','.join(['thisisnotafont', fonts[-1], 'anothernonfont']) - self.assertFalse(pygame_font.match_font(names) is None) - names = ','.join(['thisisnotafont1', 'thisisnotafont2', 'thisisnotafont3']) - self.assertTrue(pygame_font.match_font(names) is None) + self.assertTrue( + any(pygame_font.match_font(font, italic=True) for font in fonts) + ) + + def test_issue_742(self): + """that the font background does not crash.""" + surf = pygame.Surface((320, 240)) + font = pygame_font.Font(None, 24) + image = font.render("Test", 0, (255, 255, 255), (0, 0, 0)) + self.assertIsNone(image.get_colorkey()) + image.set_alpha(255) + surf.blit(image, (0, 0)) + + # not issue 742, but be sure to test that background color is + # correctly issued on this mode + self.assertEqual(surf.get_at((0, 0)), pygame.Color(0, 0, 0)) + + def test_issue_font_alphablit(self): + """Check that blitting anti-aliased text doesn't + change the background blue""" + pygame.display.set_mode((600, 400)) + + font = pygame_font.Font(None, 24) + + (color, text, center, pos) = ((160, 200, 250), "Music", (190, 170), "midright") + img1 = font.render(text, True, color) + + img = pygame.Surface(img1.get_size(), depth=32) + pre_blit_corner_pixel = img.get_at((0, 0)) + img.blit(img1, (0, 0)) + post_blit_corner_pixel = img.get_at((0, 0)) + + self.assertEqual(pre_blit_corner_pixel, post_blit_corner_pixel) + + def test_segfault_after_reinit(self): + """Reinitialization of font module should not cause + segmentation fault""" + import gc + + font = pygame_font.Font(None, 20) + pygame_font.quit() + pygame_font.init() + del font + gc.collect() def test_quit(self): pygame_font.quit() -@unittest.skipIf(IS_PYPY, 'pypy skip known failure') # TODO +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO class FontTest(unittest.TestCase): - def setUp(self): pygame_font.init() @@ -165,12 +242,12 @@ class FontTest(unittest.TestCase): self.assertTrue(font_surface) screen.blit(font_surface, font_rect, font_rect) pygame.display.update() - self.assertEqual(tuple(screen.get_at((0,0)))[:3], (255, 255, 255)) + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (255, 255, 255)) self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (255, 255, 255)) # If we don't have a real display, don't do this test. # Transparent background doesn't seem to work without a read video card. - if os.environ.get('SDL_VIDEODRIVER') != 'dummy': + if os.environ.get("SDL_VIDEODRIVER") != "dummy": screen.fill((10, 10, 10)) font_surface = f.render(" bar", True, (0, 0, 0), None) font_rect = font_surface.get_rect() @@ -178,7 +255,7 @@ class FontTest(unittest.TestCase): self.assertTrue(font_surface) screen.blit(font_surface, font_rect, font_rect) pygame.display.update() - self.assertEqual(tuple(screen.get_at((0,0)))[:3], (10, 10, 10)) + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10)) self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10)) screen.fill((10, 10, 10)) @@ -188,20 +265,21 @@ class FontTest(unittest.TestCase): self.assertTrue(font_surface) screen.blit(font_surface, font_rect, font_rect) pygame.display.update(rect) - self.assertEqual(tuple(screen.get_at((0,0)))[:3], (10, 10, 10)) + self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10)) self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10)) - -@unittest.skipIf(IS_PYPY, 'pypy skip known failure') # TODO -class FontTypeTest( unittest.TestCase ): - +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class FontTypeTest(unittest.TestCase): def setUp(self): pygame_font.init() def tearDown(self): pygame_font.quit() + def test_default_parameters(self): + f = pygame_font.Font() + def test_get_ascent(self): # Ckecking ascent would need a custom test font to do properly. f = pygame_font.Font(None, 20) @@ -239,16 +317,16 @@ class FontTypeTest( unittest.TestCase ): # Ensure bytes decoding works correctly. Can only compare results # with unicode for now. f = pygame_font.Font(None, 20) - um = f.metrics(as_unicode(".")) - bm = f.metrics(as_bytes(".")) + um = f.metrics(".") + bm = f.metrics(b".") self.assertEqual(len(um), 1) self.assertEqual(len(bm), 1) self.assertIsNotNone(um[0]) self.assertEqual(um, bm) - u = u"\u212A" - b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM + u = "\u212A" + b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM bm = f.metrics(b) self.assertEqual(len(bm), 2) @@ -262,28 +340,27 @@ class FontTypeTest( unittest.TestCase ): self.assertNotEqual(bm[0], um[0]) self.assertNotEqual(bm[1], um[0]) - if UCS_4: - u = u"\U00013000" - bm = f.metrics(u) + u = "\U00013000" + bm = f.metrics(u) - self.assertEqual(len(bm), 1) - self.assertIsNone(bm[0]) + self.assertEqual(len(bm), 1) + self.assertIsNone(bm[0]) - return # unfinished + return # unfinished # The documentation is useless here. How large a list? # How do list positions relate to character codes? # What about unicode characters? # __doc__ (as of 2008-08-02) for pygame_font.Font.metrics: - # Font.metrics(text): return list - # Gets the metrics for each character in the pased string. - # - # The list contains tuples for each character, which contain the - # minimum X offset, the maximum X offset, the minimum Y offset, the - # maximum Y offset and the advance offset (bearing plus width) of the - # character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, - # maxy, advance), ...] + # Font.metrics(text): return list + # Gets the metrics for each character in the passed string. + # + # The list contains tuples for each character, which contain the + # minimum X offset, the maximum X offset, the minimum Y offset, the + # maximum Y offset and the advance offset (bearing plus width) of the + # character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, + # maxy, advance), ...] self.fail() @@ -297,28 +374,26 @@ class FontTypeTest( unittest.TestCase ): s = f.render("xxx", False, [0, 0, 0]) s = f.render(" ", False, [0, 0, 0]) s = f.render(" ", False, [0, 0, 0], [255, 255, 255]) - # null text should be 1 pixel wide. + # null text should be 0 pixel wide. s = f.render("", False, [0, 0, 0], [255, 255, 255]) - self.assertEqual(s.get_size()[0], 1) - # None text should be 1 pixel wide. + self.assertEqual(s.get_size()[0], 0) + # None text should be 0 pixel wide. s = f.render(None, False, [0, 0, 0], [255, 255, 255]) - self.assertEqual(s.get_size()[0], 1) + self.assertEqual(s.get_size()[0], 0) # Non-text should raise a TypeError. - self.assertRaises(TypeError, f.render, - [], False, [0, 0, 0], [255, 255, 255]) - self.assertRaises(TypeError, f.render, - 1, False, [0, 0, 0], [255, 255, 255]) + self.assertRaises(TypeError, f.render, [], False, [0, 0, 0], [255, 255, 255]) + self.assertRaises(TypeError, f.render, 1, False, [0, 0, 0], [255, 255, 255]) # is background transparent for antialiasing? s = f.render(".", True, [255, 255, 255]) self.assertEqual(s.get_at((0, 0))[3], 0) # is Unicode and bytes encoding correct? # Cannot really test if the correct characters are rendered, but # at least can assert the encodings differ. - su = f.render(as_unicode("."), False, [0, 0, 0], [255, 255, 255]) - sb = f.render(as_bytes("."), False, [0, 0, 0], [255, 255, 255]) + su = f.render(".", False, [0, 0, 0], [255, 255, 255]) + sb = f.render(b".", False, [0, 0, 0], [255, 255, 255]) self.assertTrue(equal_images(su, sb)) - u = as_unicode(r"\u212A") - b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM + u = "\u212A" + b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM sb = f.render(b, False, [0, 0, 0], [255, 255, 255]) try: # FIXME why do we do this try/except ? su = f.render(u, False, [0, 0, 0], [255, 255, 255]) @@ -327,19 +402,20 @@ class FontTypeTest( unittest.TestCase ): else: self.assertFalse(equal_images(su, sb)) - # If the font module is SDL_ttf based, then it can only supports UCS-2; + # test for internal null bytes + self.assertRaises(ValueError, f.render, b"ab\x00cd", 0, [0, 0, 0]) + self.assertRaises(ValueError, f.render, "ab\x00cd", 0, [0, 0, 0]) + + def test_render_ucs2_ucs4(self): + """that it renders without raising if there is a new enough SDL_ttf.""" + f = pygame_font.Font(None, 20) + # If the font module is SDL_ttf < 2.0.15 based, then it only supports UCS-2 # it will raise an exception for an out-of-range UCS-4 code point. - if UCS_4 and not hasattr(f, 'ucs4'): - ucs_2 = as_unicode(r"\uFFEE") + if hasattr(pygame_font, "UCS4"): + ucs_2 = "\uFFEE" s = f.render(ucs_2, False, [0, 0, 0], [255, 255, 255]) - ucs_4 = as_unicode(r"\U00010000") - self.assertRaises(UnicodeError, f.render, - ucs_4, False, [0, 0, 0], [255, 255, 255]) - - b = as_bytes("ab\x00cd") - self.assertRaises(ValueError, f.render, b, 0, [0, 0, 0]) - u = as_unicode("ab\x00cd") - self.assertRaises(ValueError, f.render, b, 0, [0, 0, 0]) + ucs_4 = "\U00010000" + s = f.render(ucs_4, False, [0, 0, 0], [255, 255, 255]) def test_set_bold(self): f = pygame_font.Font(None, 20) @@ -365,9 +441,51 @@ class FontTypeTest( unittest.TestCase ): f.set_underline(False) self.assertFalse(f.get_underline()) + def test_set_strikethrough(self): + if pygame_font.__name__ != "pygame.ftfont": + f = pygame_font.Font(None, 20) + self.assertFalse(f.get_strikethrough()) + f.set_strikethrough(True) + self.assertTrue(f.get_strikethrough()) + f.set_strikethrough(False) + self.assertFalse(f.get_strikethrough()) + + def test_bold_attr(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.bold) + f.bold = True + self.assertTrue(f.bold) + f.bold = False + self.assertFalse(f.bold) + + def test_set_italic_property(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.italic) + f.italic = True + self.assertTrue(f.italic) + f.italic = False + self.assertFalse(f.italic) + + def test_set_underline_property(self): + f = pygame_font.Font(None, 20) + self.assertFalse(f.underline) + f.underline = True + self.assertTrue(f.underline) + f.underline = False + self.assertFalse(f.underline) + + def test_set_strikethrough_property(self): + if pygame_font.__name__ != "pygame.ftfont": + f = pygame_font.Font(None, 20) + self.assertFalse(f.strikethrough) + f.strikethrough = True + self.assertTrue(f.strikethrough) + f.strikethrough = False + self.assertFalse(f.strikethrough) + def test_size(self): f = pygame_font.Font(None, 20) - text = as_unicode("Xg") + text = "Xg" size = f.size(text) w, h = size s = f.render(text, False, (255, 255, 255)) @@ -378,54 +496,89 @@ class FontTypeTest( unittest.TestCase ): self.assertEqual(s.get_size(), size) self.assertEqual(f.size(btext), size) - text = as_unicode(r"\u212A") - btext = text.encode("UTF-16")[2:] # Keep the byte order consistent. + text = "\u212A" + btext = text.encode("UTF-16")[2:] # Keep the byte order consistent. bsize = f.size(btext) - try: # FIXME why do we do this try/except ? - size = f.size(text) - except pygame.error: - pass - else: - self.assertNotEqual(size, bsize) + size = f.size(text) + + self.assertNotEqual(size, bsize) def test_font_file_not_found(self): # A per BUG reported by Bo Jangeborg on pygame-user mailing list, # http://www.mail-archive.com/pygame-users@seul.org/msg11675.html pygame_font.init() - self.assertRaises(IOError, - pygame_font.Font, - unicode_('some-fictional-font.ttf'), 20) + self.assertRaises( + FileNotFoundError, pygame_font.Font, "some-fictional-font.ttf", 20 + ) def test_load_from_file(self): font_name = pygame_font.get_default_font() - font_path = os.path.join(os.path.split(pygame.__file__)[0], - pygame_font.get_default_font()) + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) f = pygame_font.Font(font_path, 20) + def test_load_from_file_default(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(font_path) + + def test_load_from_pathlib(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(pathlib.Path(font_path), 20) + f = pygame_font.Font(pathlib.Path(font_path)) + + def test_load_from_pathlib_default(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + f = pygame_font.Font(pathlib.Path(font_path)) + def test_load_from_file_obj(self): font_name = pygame_font.get_default_font() - font_path = os.path.join(os.path.split(pygame.__file__)[0], - pygame_font.get_default_font()) + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) with open(font_path, "rb") as f: font = pygame_font.Font(f, 20) + def test_load_from_file_obj_default(self): + font_name = pygame_font.get_default_font() + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) + with open(font_path, "rb") as f: + font = pygame_font.Font(f) + def test_load_default_font_filename(self): # In font_init, a special case is when the filename argument is # identical to the default font file name. f = pygame_font.Font(pygame_font.get_default_font(), 20) + def test_load_default_font_filename_default(self): + # In font_init, a special case is when the filename argument is + # identical to the default font file name. + f = pygame_font.Font(pygame_font.get_default_font()) + def _load_unicode(self, path): import shutil - fdir = unicode_(FONTDIR) + + fdir = str(FONTDIR) temp = os.path.join(fdir, path) - pgfont = os.path.join(fdir, u'test_sans.ttf') + pgfont = os.path.join(fdir, "test_sans.ttf") shutil.copy(pgfont, temp) try: - with open(temp, 'rb') as f: + with open(temp, "rb") as f: pass - except IOError: - raise unittest.SkipTest('the path cannot be opened') + except FileNotFoundError: + raise unittest.SkipTest("the path cannot be opened") try: pygame_font.Font(temp, 20) finally: @@ -433,29 +586,59 @@ class FontTypeTest( unittest.TestCase ): def test_load_from_file_unicode_0(self): """ASCII string as a unicode object""" - self._load_unicode(u'temp_file.ttf') + self._load_unicode("temp_file.ttf") def test_load_from_file_unicode_1(self): - self._load_unicode(u'你好.ttf') + self._load_unicode("你好.ttf") def test_load_from_file_bytes(self): - font_path = os.path.join(os.path.split(pygame.__file__)[0], - pygame_font.get_default_font()) + font_path = os.path.join( + os.path.split(pygame.__file__)[0], pygame_font.get_default_font() + ) filesystem_encoding = sys.getfilesystemencoding() + filesystem_errors = "replace" if sys.platform == "win32" else "surrogateescape" try: # FIXME why do we do this try/except ? - font_path = font_path.decode(filesystem_encoding, - filesystem_errors) + font_path = font_path.decode(filesystem_encoding, filesystem_errors) except AttributeError: pass - bfont_path = font_path.encode(filesystem_encoding, - filesystem_errors) + bfont_path = font_path.encode(filesystem_encoding, filesystem_errors) f = pygame_font.Font(bfont_path, 20) + def test_issue_3144(self): + fpath = os.path.join(FONTDIR, "PlayfairDisplaySemibold.ttf") + + # issue in SDL_ttf 2.0.18 DLL on Windows + # tested to make us aware of any regressions + for size in (60, 40, 10, 20, 70, 45, 50, 10): + font = pygame_font.Font(fpath, size) + font.render("WHERE", True, "black") + + def test_font_set_script(self): + if pygame_font.__name__ == "pygame.ftfont": + return # this ain't a pygame.ftfont thing! -@unittest.skipIf(IS_PYPY, 'pypy skip known failure') # TODO -class VisualTests( unittest.TestCase ): + font = pygame_font.Font(None, 16) - __tags__ = ['interactive'] + ttf_version = pygame_font.get_sdl_ttf_version() + if ttf_version >= (2, 20, 0): + self.assertRaises(TypeError, pygame.font.Font.set_script) + self.assertRaises(TypeError, pygame.font.Font.set_script, font) + self.assertRaises(TypeError, pygame.font.Font.set_script, "hey", "Deva") + self.assertRaises(TypeError, font.set_script, 1) + self.assertRaises(TypeError, font.set_script, ["D", "e", "v", "a"]) + + self.assertRaises(ValueError, font.set_script, "too long by far") + self.assertRaises(ValueError, font.set_script, "") + self.assertRaises(ValueError, font.set_script, "a") + + font.set_script("Deva") + else: + self.assertRaises(pygame.error, font.set_script, "Deva") + + +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class VisualTests(unittest.TestCase): + __tags__ = ["interactive"] screen = None aborted = False @@ -473,8 +656,14 @@ class VisualTests( unittest.TestCase ): pygame.quit() self.aborted = True - def query(self, - bold=False, italic=False, underline=False, antialiase=False): + def query( + self, + bold=False, + italic=False, + underline=False, + strikethrough=False, + antialiase=False, + ): if self.aborted: return False spacing = 10 @@ -484,7 +673,7 @@ class VisualTests( unittest.TestCase ): screen = self.screen screen.fill((255, 255, 255)) pygame.display.flip() - if not (bold or italic or underline or antialiase): + if not (bold or italic or underline or strikethrough or antialiase): text = "normal" else: modes = [] @@ -494,22 +683,28 @@ class VisualTests( unittest.TestCase ): modes.append("italic") if underline: modes.append("underlined") + if strikethrough: + modes.append("strikethrough") if antialiase: modes.append("antialiased") - text = "%s (y/n):" % ('-'.join(modes),) + text = f"{'-'.join(modes)} (y/n):" f.set_bold(bold) f.set_italic(italic) f.set_underline(underline) + if pygame_font.__name__ != "pygame.ftfont": + f.set_strikethrough(strikethrough) s = f.render(text, antialiase, (0, 0, 0)) screen.blit(s, (offset, y)) y += s.get_size()[1] + spacing f.set_bold(False) f.set_italic(False) f.set_underline(False) + if pygame_font.__name__ != "pygame.ftfont": + f.set_strikethrough(False) s = f.render("(some comparison text)", False, (0, 0, 0)) screen.blit(s, (offset, y)) pygame.display.flip() - while 1: + while True: for evt in pygame.event.get(): if evt.type == pygame.KEYDOWN: if evt.key == pygame.K_ESCAPE: @@ -532,6 +727,10 @@ class VisualTests( unittest.TestCase ): def test_underline(self): self.assertTrue(self.query(underline=True)) + def test_strikethrough(self): + if pygame_font.__name__ != "pygame.ftfont": + self.assertTrue(self.query(strikethrough=True)) + def test_antialiase(self): self.assertTrue(self.query(antialiase=True)) @@ -541,6 +740,10 @@ class VisualTests( unittest.TestCase ): def test_italic_underline(self): self.assertTrue(self.query(italic=True, underline=True)) + def test_bold_strikethrough(self): + if pygame_font.__name__ != "pygame.ftfont": + self.assertTrue(self.query(bold=True, strikethrough=True)) + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/freetype_tags.py b/venv/Lib/site-packages/pygame/tests/freetype_tags.py index 5c56fc3f9bb7e11a34e8324a37b39a59e87ff70c..d84cbb77a370675dc42775ac49237d0ec655777b 100644 --- a/venv/Lib/site-packages/pygame/tests/freetype_tags.py +++ b/venv/Lib/site-packages/pygame/tests/freetype_tags.py @@ -1,4 +1,4 @@ -__tags__ = ['development'] +__tags__ = ["development"] exclude = False @@ -8,5 +8,4 @@ except ImportError: exclude = True if exclude: - __tags__.extend(['ignore', 'subprocess_ignore']) - + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/venv/Lib/site-packages/pygame/tests/freetype_test.py b/venv/Lib/site-packages/pygame/tests/freetype_test.py index 05603ef8c2376f42a442295e0fe4140342fe154a..25551d8b8c73b35c578cbf00c38e97ed485518ae 100644 --- a/venv/Lib/site-packages/pygame/tests/freetype_test.py +++ b/venv/Lib/site-packages/pygame/tests/freetype_test.py @@ -1,15 +1,16 @@ import os -if os.environ.get('SDL_VIDEODRIVER') == 'dummy': - __tags__ = ('ignore', 'subprocess_ignore') + +if os.environ.get("SDL_VIDEODRIVER") == "dummy": + __tags__ = ("ignore", "subprocess_ignore") import unittest -import sys import ctypes import weakref import gc +import pathlib import platform -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() try: @@ -18,24 +19,26 @@ except NameError: pass import pygame + try: import pygame.freetype as ft except ImportError: ft = None -from pygame.compat import as_unicode, bytes_, unichr_, unicode_ -FONTDIR = os.path.join(os.path.dirname (os.path.abspath (__file__)), - 'fixtures', 'fonts') +FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + def nullfont(): """return an uninitialized font instance""" return ft.Font.__new__(ft.Font) + max_point_size_FX6 = 0x7FFFFFFF max_point_size = max_point_size_FX6 >> 6 max_point_size_f = max_point_size_FX6 * 0.015625 + def surf_same_image(a, b): """Return True if a's pixel buffer is identical to b's""" @@ -49,13 +52,12 @@ def surf_same_image(a, b): class FreeTypeFontTest(unittest.TestCase): - - _fixed_path = os.path.join(FONTDIR, 'test_fixed.otf') - _sans_path = os.path.join(FONTDIR, 'test_sans.ttf') - _mono_path = os.path.join(FONTDIR, 'PyGameMono.otf') - _bmp_8_75dpi_path = os.path.join(FONTDIR, 'PyGameMono-8.bdf') - _bmp_18_75dpi_path = os.path.join(FONTDIR, 'PyGameMono-18-75dpi.bdf') - _bmp_18_100dpi_path = os.path.join(FONTDIR, 'PyGameMono-18-100dpi.bdf') + _fixed_path = os.path.join(FONTDIR, "test_fixed.otf") + _sans_path = os.path.join(FONTDIR, "test_sans.ttf") + _mono_path = os.path.join(FONTDIR, "PyGameMono.otf") + _bmp_8_75dpi_path = os.path.join(FONTDIR, "PyGameMono-8.bdf") + _bmp_18_75dpi_path = os.path.join(FONTDIR, "PyGameMono-18-75dpi.bdf") + _bmp_18_100dpi_path = os.path.join(FONTDIR, "PyGameMono-18-100dpi.bdf") _TEST_FONTS = {} @classmethod @@ -67,32 +69,32 @@ class FreeTypeFontTest(unittest.TestCase): # Inconsolata is an open-source font designed by Raph Levien. # Licensed under the Open Font License. # http://www.levien.com/type/myfonts/inconsolata.html - cls._TEST_FONTS['fixed'] = ft.Font(cls._fixed_path) + cls._TEST_FONTS["fixed"] = ft.Font(cls._fixed_path) # Liberation Sans is an open-source font designed by Steve Matteson. # Licensed under the GNU GPL. # https://fedorahosted.org/liberation-fonts/ - cls._TEST_FONTS['sans'] = ft.Font(cls._sans_path) + cls._TEST_FONTS["sans"] = ft.Font(cls._sans_path) # A scalable mono test font made for pygame. It contains only # a few glyphs: '\0', 'A', 'B', 'C', and U+13079. # It also contains two bitmap sizes: 8.0 X 8.0 and 19.0 X 19.0. - cls._TEST_FONTS['mono'] = ft.Font(cls._mono_path) + cls._TEST_FONTS["mono"] = ft.Font(cls._mono_path) # A fixed size bitmap mono test font made for pygame. # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. # The size is 8.0 X 8.0. - cls._TEST_FONTS['bmp-8-75dpi'] = ft.Font(cls._bmp_8_75dpi_path) + cls._TEST_FONTS["bmp-8-75dpi"] = ft.Font(cls._bmp_8_75dpi_path) # A fixed size bitmap mono test font made for pygame. # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. # The size is 8.0 X 8.0. - cls._TEST_FONTS['bmp-18-75dpi'] = ft.Font(cls._bmp_18_75dpi_path) + cls._TEST_FONTS["bmp-18-75dpi"] = ft.Font(cls._bmp_18_75dpi_path) # A fixed size bitmap mono test font made for pygame. # It contains only a few glyphs: '\0', 'A', 'B', 'C', and U+13079. # The size is 8.0 X 8.0. - cls._TEST_FONTS['bmp-18-100dpi'] = ft.Font(cls._bmp_18_100dpi_path) + cls._TEST_FONTS["bmp-18-100dpi"] = ft.Font(cls._bmp_18_100dpi_path) @classmethod def tearDownClass(cls): @@ -103,13 +105,14 @@ class FreeTypeFontTest(unittest.TestCase): self.assertEqual(font.name, "FreeSans") def test_freetype_Font_init(self): + self.assertRaises( + FileNotFoundError, ft.Font, os.path.join(FONTDIR, "nonexistent.ttf") + ) - self.assertRaises(IOError, ft.Font, os.path.join (FONTDIR, 'nonexistant.ttf')) - - f = self._TEST_FONTS['sans'] + f = self._TEST_FONTS["sans"] self.assertIsInstance(f, ft.Font) - f = self._TEST_FONTS['fixed'] + f = self._TEST_FONTS["fixed"] self.assertIsInstance(f, ft.Font) # Test keyword arguments @@ -121,18 +124,18 @@ class FreeTypeFontTest(unittest.TestCase): self.assertEqual(f.resolution, 100) f = ft.Font(ucs4=True, file=None) self.assertTrue(f.ucs4) - self.assertRaises(OverflowError, ft.Font, file=None, - size=(max_point_size + 1)) + self.assertRaises(OverflowError, ft.Font, file=None, size=(max_point_size + 1)) self.assertRaises(OverflowError, ft.Font, file=None, size=-1) f = ft.Font(None, size=24) self.assertTrue(f.height > 0) - self.assertRaises(IOError, f.__init__, - os.path.join(FONTDIR, 'nonexistant.ttf')) + self.assertRaises( + FileNotFoundError, f.__init__, os.path.join(FONTDIR, "nonexistent.ttf") + ) # Test attribute preservation during reinitalization f = ft.Font(self._sans_path, size=24, ucs4=True) - self.assertEqual(f.name, 'Liberation Sans') + self.assertEqual(f.name, "Liberation Sans") self.assertTrue(f.scalable) self.assertFalse(f.fixed_width) self.assertTrue(f.antialiased) @@ -141,7 +144,7 @@ class FreeTypeFontTest(unittest.TestCase): f.antialiased = False f.oblique = True f.__init__(self._mono_path) - self.assertEqual(f.name, 'PyGameMono') + self.assertEqual(f.name, "PyGameMono") self.assertTrue(f.scalable) self.assertTrue(f.fixed_width) self.assertFalse(f.antialiased) @@ -161,7 +164,8 @@ class FreeTypeFontTest(unittest.TestCase): @unittest.skipIf(IS_PYPY, "PyPy doesn't use refcounting") def test_freetype_Font_dealloc(self): import sys - handle = open(self._sans_path, 'rb') + + handle = open(self._sans_path, "rb") def load_font(): tempFont = ft.Font(handle) @@ -174,39 +178,89 @@ class FreeTypeFontTest(unittest.TestCase): # Ensures file is closed even if test fails. handle.close() - def test_freetype_Font_scalable(self): + def test_freetype_Font_kerning(self): + """Ensures get/set works with the kerning property.""" + ft_font = self._TEST_FONTS["sans"] + + # Test default is disabled. + self.assertFalse(ft_font.kerning) + + # Test setting to True. + ft_font.kerning = True + + self.assertTrue(ft_font.kerning) + + # Test setting to False. + ft_font.kerning = False + + self.assertFalse(ft_font.kerning) + + def test_freetype_Font_kerning__enabled(self): + """Ensures exceptions are not raised when calling freetype methods + while kerning is enabled. + + Note: This does not test what changes occur to a rendered font by + having kerning enabled. + + Related to issue #367. + """ + surface = pygame.Surface((10, 10), 0, 32) + TEST_TEXT = "Freetype Font" + ft_font = self._TEST_FONTS["bmp-8-75dpi"] + + ft_font.kerning = True + + # Call different methods to ensure they don't raise an exception. + metrics = ft_font.get_metrics(TEST_TEXT) + self.assertIsInstance(metrics, list) + + rect = ft_font.get_rect(TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + font_surf, rect = ft_font.render(TEST_TEXT) + self.assertIsInstance(font_surf, pygame.Surface) + self.assertIsInstance(rect, pygame.Rect) - f = self._TEST_FONTS['sans'] + rect = ft_font.render_to(surface, (0, 0), TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + buf, size = ft_font.render_raw(TEST_TEXT) + self.assertIsInstance(buf, bytes) + self.assertIsInstance(size, tuple) + + rect = ft_font.render_raw_to(surface.get_view("2"), TEST_TEXT) + self.assertIsInstance(rect, pygame.Rect) + + def test_freetype_Font_scalable(self): + f = self._TEST_FONTS["sans"] self.assertTrue(f.scalable) - self.assertRaises(RuntimeError, lambda : nullfont().scalable) + self.assertRaises(RuntimeError, lambda: nullfont().scalable) def test_freetype_Font_fixed_width(self): - - f = self._TEST_FONTS['sans'] + f = self._TEST_FONTS["sans"] self.assertFalse(f.fixed_width) - f = self._TEST_FONTS['mono'] + f = self._TEST_FONTS["mono"] self.assertTrue(f.fixed_width) - self.assertRaises(RuntimeError, lambda : nullfont().fixed_width) + self.assertRaises(RuntimeError, lambda: nullfont().fixed_width) def test_freetype_Font_fixed_sizes(self): - - f = self._TEST_FONTS['sans'] + f = self._TEST_FONTS["sans"] self.assertEqual(f.fixed_sizes, 0) - f = self._TEST_FONTS['bmp-8-75dpi'] + f = self._TEST_FONTS["bmp-8-75dpi"] self.assertEqual(f.fixed_sizes, 1) - f = self._TEST_FONTS['mono'] + f = self._TEST_FONTS["mono"] self.assertEqual(f.fixed_sizes, 2) def test_freetype_Font_get_sizes(self): - f = self._TEST_FONTS['sans'] + f = self._TEST_FONTS["sans"] szlist = f.get_sizes() self.assertIsInstance(szlist, list) self.assertEqual(len(szlist), 0) - f = self._TEST_FONTS['bmp-8-75dpi'] + f = self._TEST_FONTS["bmp-8-75dpi"] szlist = f.get_sizes() self.assertIsInstance(szlist, list) self.assertEqual(len(szlist), 1) @@ -221,7 +275,7 @@ class FreeTypeFontTest(unittest.TestCase): self.assertIsInstance(size8[4], float) self.assertEqual(int(size8[4] * 64.0 + 0.5), 8 * 64) - f = self._TEST_FONTS['mono'] + f = self._TEST_FONTS["mono"] szlist = f.get_sizes() self.assertIsInstance(szlist, list) self.assertEqual(len(szlist), 2) @@ -237,40 +291,40 @@ class FreeTypeFontTest(unittest.TestCase): self.assertEqual(int(size19[4] * 64.0 + 0.5), 19 * 64) def test_freetype_Font_use_bitmap_strikes(self): - f = self._TEST_FONTS['mono'] + f = self._TEST_FONTS["mono"] try: # use_bitmap_strikes == True # self.assertTrue(f.use_bitmap_strikes) # bitmap compatible properties - s_strike, sz = f.render_raw('A', size=19) + s_strike, sz = f.render_raw("A", size=19) try: f.vertical = True - s_strike_vert, sz = f.render_raw('A', size=19) + s_strike_vert, sz = f.render_raw("A", size=19) finally: f.vertical = False try: f.wide = True - s_strike_wide, sz = f.render_raw('A', size=19) + s_strike_wide, sz = f.render_raw("A", size=19) finally: f.wide = False try: f.underline = True - s_strike_underline, sz = f.render_raw('A', size=19) + s_strike_underline, sz = f.render_raw("A", size=19) finally: f.underline = False # bitmap incompatible properties - s_strike_rot45, sz = f.render_raw('A', size=19, rotation=45) + s_strike_rot45, sz = f.render_raw("A", size=19, rotation=45) try: f.strong = True - s_strike_strong, sz = f.render_raw('A', size=19) + s_strike_strong, sz = f.render_raw("A", size=19) finally: f.strong = False try: f.oblique = True - s_strike_oblique, sz = f.render_raw('A', size=19) + s_strike_oblique, sz = f.render_raw("A", size=19) finally: f.oblique = False @@ -280,39 +334,39 @@ class FreeTypeFontTest(unittest.TestCase): self.assertFalse(f.use_bitmap_strikes) # bitmap compatible properties - s_outline, sz = f.render_raw('A', size=19) + s_outline, sz = f.render_raw("A", size=19) self.assertNotEqual(s_outline, s_strike) try: f.vertical = True - s_outline, sz = f.render_raw('A', size=19) + s_outline, sz = f.render_raw("A", size=19) self.assertNotEqual(s_outline, s_strike_vert) finally: f.vertical = False try: f.wide = True - s_outline, sz = f.render_raw('A', size=19) + s_outline, sz = f.render_raw("A", size=19) self.assertNotEqual(s_outline, s_strike_wide) finally: f.wide = False try: f.underline = True - s_outline, sz = f.render_raw('A', size=19) + s_outline, sz = f.render_raw("A", size=19) self.assertNotEqual(s_outline, s_strike_underline) finally: f.underline = False # bitmap incompatible properties - s_outline, sz = f.render_raw('A', size=19, rotation=45) + s_outline, sz = f.render_raw("A", size=19, rotation=45) self.assertEqual(s_outline, s_strike_rot45) try: f.strong = True - s_outline, sz = f.render_raw('A', size=19) + s_outline, sz = f.render_raw("A", size=19) self.assertEqual(s_outline, s_strike_strong) finally: f.strong = False try: f.oblique = True - s_outline, sz = f.render_raw('A', size=19) + s_outline, sz = f.render_raw("A", size=19) self.assertEqual(s_outline, s_strike_oblique) finally: f.oblique = False @@ -321,68 +375,80 @@ class FreeTypeFontTest(unittest.TestCase): def test_freetype_Font_bitmap_files(self): """Ensure bitmap file restrictions are caught""" - f = self._TEST_FONTS['bmp-8-75dpi'] + f = self._TEST_FONTS["bmp-8-75dpi"] f_null = nullfont() s = pygame.Surface((10, 10), 0, 32) - a = s.get_view('3') + a = s.get_view("3") exception = AttributeError - self.assertRaises(exception, setattr, f, 'strong', True) - self.assertRaises(exception, setattr, f, 'oblique', True) - self.assertRaises(exception, setattr, f, 'style', ft.STYLE_STRONG) - self.assertRaises(exception, setattr, f, 'style', ft.STYLE_OBLIQUE) + self.assertRaises(exception, setattr, f, "strong", True) + self.assertRaises(exception, setattr, f, "oblique", True) + self.assertRaises(exception, setattr, f, "style", ft.STYLE_STRONG) + self.assertRaises(exception, setattr, f, "style", ft.STYLE_OBLIQUE) exception = RuntimeError - self.assertRaises(exception, setattr, f_null, 'strong', True) - self.assertRaises(exception, setattr, f_null, 'oblique', True) - self.assertRaises(exception, setattr, f_null, 'style', ft.STYLE_STRONG) - self.assertRaises(exception, setattr, f_null, 'style', ft.STYLE_OBLIQUE) + self.assertRaises(exception, setattr, f_null, "strong", True) + self.assertRaises(exception, setattr, f_null, "oblique", True) + self.assertRaises(exception, setattr, f_null, "style", ft.STYLE_STRONG) + self.assertRaises(exception, setattr, f_null, "style", ft.STYLE_OBLIQUE) exception = ValueError - self.assertRaises(exception, f.render, - 'A', (0, 0, 0), size=8, rotation=1) - self.assertRaises(exception, f.render, - 'A', (0, 0, 0), size=8, style=ft.STYLE_OBLIQUE) - self.assertRaises(exception, f.render, - 'A', (0, 0, 0), size=8, style=ft.STYLE_STRONG) - self.assertRaises(exception, f.render_raw, 'A', size=8, rotation=1) - self.assertRaises(exception, f.render_raw, - 'A', size=8, style=ft.STYLE_OBLIQUE) - self.assertRaises(exception, f.render_raw, - 'A', size=8, style=ft.STYLE_STRONG) - self.assertRaises(exception, f.render_to, - s, (0, 0), 'A', (0, 0, 0), size=8, rotation=1) - self.assertRaises(exception, f.render_to, - s, (0, 0), 'A', (0, 0, 0), size=8, - style=ft.STYLE_OBLIQUE) - self.assertRaises(exception, f.render_to, - s, (0, 0), 'A', (0, 0, 0), size=8, - style=ft.STYLE_STRONG) - self.assertRaises(exception, f.render_raw_to, - a, 'A', size=8, rotation=1) - self.assertRaises(exception, f.render_raw_to, - a, 'A', size=8, style=ft.STYLE_OBLIQUE) - self.assertRaises(exception, f.render_raw_to, - a, 'A', size=8, style=ft.STYLE_STRONG) - self.assertRaises(exception, f.get_rect, 'A', size=8, rotation=1) - self.assertRaises(exception, f.get_rect, - 'A', size=8, style=ft.STYLE_OBLIQUE) - self.assertRaises(exception, f.get_rect, - 'A', size=8, style=ft.STYLE_STRONG) + self.assertRaises(exception, f.render, "A", (0, 0, 0), size=8, rotation=1) + self.assertRaises( + exception, f.render, "A", (0, 0, 0), size=8, style=ft.STYLE_OBLIQUE + ) + self.assertRaises( + exception, f.render, "A", (0, 0, 0), size=8, style=ft.STYLE_STRONG + ) + self.assertRaises(exception, f.render_raw, "A", size=8, rotation=1) + self.assertRaises(exception, f.render_raw, "A", size=8, style=ft.STYLE_OBLIQUE) + self.assertRaises(exception, f.render_raw, "A", size=8, style=ft.STYLE_STRONG) + self.assertRaises( + exception, f.render_to, s, (0, 0), "A", (0, 0, 0), size=8, rotation=1 + ) + self.assertRaises( + exception, + f.render_to, + s, + (0, 0), + "A", + (0, 0, 0), + size=8, + style=ft.STYLE_OBLIQUE, + ) + self.assertRaises( + exception, + f.render_to, + s, + (0, 0), + "A", + (0, 0, 0), + size=8, + style=ft.STYLE_STRONG, + ) + self.assertRaises(exception, f.render_raw_to, a, "A", size=8, rotation=1) + self.assertRaises( + exception, f.render_raw_to, a, "A", size=8, style=ft.STYLE_OBLIQUE + ) + self.assertRaises( + exception, f.render_raw_to, a, "A", size=8, style=ft.STYLE_STRONG + ) + self.assertRaises(exception, f.get_rect, "A", size=8, rotation=1) + self.assertRaises(exception, f.get_rect, "A", size=8, style=ft.STYLE_OBLIQUE) + self.assertRaises(exception, f.get_rect, "A", size=8, style=ft.STYLE_STRONG) # Unsupported point size exception = pygame.error - self.assertRaises(exception, f.get_rect, 'A', size=42) - self.assertRaises(exception, f.get_metrics, 'A', size=42) + self.assertRaises(exception, f.get_rect, "A", size=42) + self.assertRaises(exception, f.get_metrics, "A", size=42) self.assertRaises(exception, f.get_sized_ascender, 42) self.assertRaises(exception, f.get_sized_descender, 42) self.assertRaises(exception, f.get_sized_height, 42) self.assertRaises(exception, f.get_sized_glyph_height, 42) def test_freetype_Font_get_metrics(self): + font = self._TEST_FONTS["sans"] - font = self._TEST_FONTS['sans'] - - metrics = font.get_metrics('ABCD', size=24) - self.assertEqual(len(metrics), len('ABCD')) + metrics = font.get_metrics("ABCD", size=24) + self.assertEqual(len(metrics), len("ABCD")) self.assertIsInstance(metrics, list) for metrics_tuple in metrics: @@ -396,19 +462,17 @@ class FreeTypeFontTest(unittest.TestCase): self.assertIsInstance(m, float) # test for empty string - metrics = font.get_metrics('', size=24) + metrics = font.get_metrics("", size=24) self.assertEqual(metrics, []) # test for invalid string self.assertRaises(TypeError, font.get_metrics, 24, 24) - # raises exception when uninitalized - self.assertRaises(RuntimeError, nullfont().get_metrics, - 'a', size=24) + # raises exception when uninitialized + self.assertRaises(RuntimeError, nullfont().get_metrics, "a", size=24) def test_freetype_Font_get_rect(self): - - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] def test_rect(r): self.assertIsInstance(r, pygame.Rect) @@ -444,54 +508,49 @@ class FreeTypeFontTest(unittest.TestCase): # Rect size should change if UTF surrogate pairs are treated as # one code point or two. - ufont = self._TEST_FONTS['mono'] - rect_utf32 = ufont.get_rect(as_unicode(r'\U00013079'), size=24) - rect_utf16 = ufont.get_rect(as_unicode(r'\uD80C\uDC79'), size=24) - self.assertEqual(rect_utf16, rect_utf32); + ufont = self._TEST_FONTS["mono"] + rect_utf32 = ufont.get_rect("\U00013079", size=24) + rect_utf16 = ufont.get_rect("\uD80C\uDC79", size=24) + self.assertEqual(rect_utf16, rect_utf32) ufont.ucs4 = True try: - rect_utf16 = ufont.get_rect(as_unicode(r'\uD80C\uDC79'), size=24) + rect_utf16 = ufont.get_rect("\uD80C\uDC79", size=24) finally: ufont.ucs4 = False - self.assertNotEqual(rect_utf16, rect_utf32); + self.assertNotEqual(rect_utf16, rect_utf32) - self.assertRaises(RuntimeError, - nullfont().get_rect, 'a', size=24) + self.assertRaises(RuntimeError, nullfont().get_rect, "a", size=24) # text stretching - rect12 = font.get_rect('A', size=12.0) - rect24 = font.get_rect('A', size=24.0) - rect_x = font.get_rect('A', size=(24.0, 12.0)) + rect12 = font.get_rect("A", size=12.0) + rect24 = font.get_rect("A", size=24.0) + rect_x = font.get_rect("A", size=(24.0, 12.0)) self.assertEqual(rect_x.width, rect24.width) self.assertEqual(rect_x.height, rect12.height) - rect_y = font.get_rect('A', size=(12.0, 24.0)) + rect_y = font.get_rect("A", size=(12.0, 24.0)) self.assertEqual(rect_y.width, rect12.width) self.assertEqual(rect_y.height, rect24.height) def test_freetype_Font_height(self): - - f = self._TEST_FONTS['sans'] + f = self._TEST_FONTS["sans"] self.assertEqual(f.height, 2355) - f = self._TEST_FONTS['fixed'] + f = self._TEST_FONTS["fixed"] self.assertEqual(f.height, 1100) - self.assertRaises(RuntimeError, lambda : nullfont().height) - + self.assertRaises(RuntimeError, lambda: nullfont().height) def test_freetype_Font_name(self): + f = self._TEST_FONTS["sans"] + self.assertEqual(f.name, "Liberation Sans") - f = self._TEST_FONTS['sans'] - self.assertEqual(f.name, 'Liberation Sans') - - f = self._TEST_FONTS['fixed'] - self.assertEqual(f.name, 'Inconsolata') + f = self._TEST_FONTS["fixed"] + self.assertEqual(f.name, "Inconsolata") nf = nullfont() self.assertEqual(nf.name, repr(nf)) def test_freetype_Font_size(self): - f = ft.Font(None, size=12) self.assertEqual(f.size, 12) f.size = 22 @@ -504,9 +563,8 @@ class FreeTypeFontTest(unittest.TestCase): self.assertEqual(f.size, 6.5) f.size = max_point_size_f self.assertEqual(f.size, max_point_size_f) - self.assertRaises(OverflowError, setattr, f, 'size', -1) - self.assertRaises(OverflowError, setattr, f, 'size', - (max_point_size + 1)) + self.assertRaises(OverflowError, setattr, f, "size", -1) + self.assertRaises(OverflowError, setattr, f, "size", (max_point_size + 1)) f.size = 24.0, 0 size = f.size @@ -533,196 +591,238 @@ class FreeTypeFontTest(unittest.TestCase): size = f.size self.assertIsInstance(size, float) self.assertEqual(size, 0.0) - self.assertRaises(ValueError, setattr, f, 'size', (0, 24.0)) - self.assertRaises(TypeError, setattr, f, 'size', (24.0,)) - self.assertRaises(TypeError, setattr, f, 'size', (24.0, 0, 0)) - self.assertRaises(TypeError, setattr, f, 'size', (24.0j, 24.0)) - self.assertRaises(TypeError, setattr, f, 'size', (24.0, 24.0j)) - self.assertRaises(OverflowError, setattr, f, 'size', (-1, 16)) - self.assertRaises(OverflowError, setattr, f, 'size', - (max_point_size + 1, 16)) - self.assertRaises(OverflowError, setattr, f, 'size', (16, -1)) - self.assertRaises(OverflowError, setattr, f, 'size', - (16, max_point_size + 1)) + self.assertRaises(ValueError, setattr, f, "size", (0, 24.0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0,)) + self.assertRaises(TypeError, setattr, f, "size", (24.0, 0, 0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0j, 24.0)) + self.assertRaises(TypeError, setattr, f, "size", (24.0, 24.0j)) + self.assertRaises(OverflowError, setattr, f, "size", (-1, 16)) + self.assertRaises(OverflowError, setattr, f, "size", (max_point_size + 1, 16)) + self.assertRaises(OverflowError, setattr, f, "size", (16, -1)) + self.assertRaises(OverflowError, setattr, f, "size", (16, max_point_size + 1)) # bitmap files with identical point size but differing ppems. - f75 = self._TEST_FONTS['bmp-18-75dpi'] + f75 = self._TEST_FONTS["bmp-18-75dpi"] sizes = f75.get_sizes() self.assertEqual(len(sizes), 1) size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] self.assertEqual(size_pt, 18) self.assertEqual(x_ppem, 19.0) self.assertEqual(y_ppem, 19.0) - rect = f75.get_rect('A', size=18) - rect = f75.get_rect('A', size=19) - rect = f75.get_rect('A', size=(19.0, 19.0)) - self.assertRaises(pygame.error, f75.get_rect, 'A', size=17) - f100 = self._TEST_FONTS['bmp-18-100dpi'] + rect = f75.get_rect("A", size=18) + rect = f75.get_rect("A", size=19) + rect = f75.get_rect("A", size=(19.0, 19.0)) + self.assertRaises(pygame.error, f75.get_rect, "A", size=17) + f100 = self._TEST_FONTS["bmp-18-100dpi"] sizes = f100.get_sizes() self.assertEqual(len(sizes), 1) size_pt, width_px, height_px, x_ppem, y_ppem = sizes[0] self.assertEqual(size_pt, 18) self.assertEqual(x_ppem, 25.0) self.assertEqual(y_ppem, 25.0) - rect = f100.get_rect('A', size=18) - rect = f100.get_rect('A', size=25) - rect = f100.get_rect('A', size=(25.0, 25.0)) - self.assertRaises(pygame.error, f100.get_rect, 'A', size=17) + rect = f100.get_rect("A", size=18) + rect = f100.get_rect("A", size=25) + rect = f100.get_rect("A", size=(25.0, 25.0)) + self.assertRaises(pygame.error, f100.get_rect, "A", size=17) def test_freetype_Font_rotation(self): - - test_angles = [(30, 30), - (360, 0), (390, 30), - (720, 0), (764, 44), - (-30, 330), - (-360, 0), (-390, 330), - (-720, 0), (-764, 316)] + test_angles = [ + (30, 30), + (360, 0), + (390, 30), + (720, 0), + (764, 44), + (-30, 330), + (-360, 0), + (-390, 330), + (-720, 0), + (-764, 316), + ] f = ft.Font(None) self.assertEqual(f.rotation, 0) for r, r_reduced in test_angles: f.rotation = r - self.assertEqual(f.rotation, r_reduced, - "for angle %d: %d != %d" % - (r, f.rotation, r_reduced)) - self.assertRaises(TypeError, setattr, f, 'rotation', '12') + self.assertEqual( + f.rotation, + r_reduced, + "for angle %d: %d != %d" % (r, f.rotation, r_reduced), + ) + self.assertRaises(TypeError, setattr, f, "rotation", "12") def test_freetype_Font_render_to(self): # Rendering to an existing target surface is equivalent to # blitting a surface returned by Font.render with the target. - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] surf = pygame.Surface((800, 600)) color = pygame.Color(0, 0, 0) - rrect = font.render_to(surf, (32, 32), - 'FoobarBaz', color, None, size=24) + rrect = font.render_to(surf, (32, 32), "FoobarBaz", color, None, size=24) self.assertIsInstance(rrect, pygame.Rect) - self.assertEqual(rrect.top, rrect.height) - ## self.assertEqual(rrect.left, something or other) + self.assertEqual(rrect.topleft, (32, 32)) + self.assertNotEqual(rrect.bottomright, (32, 32)) rcopy = rrect.copy() rcopy.topleft = (32, 32) self.assertTrue(surf.get_rect().contains(rcopy)) rect = pygame.Rect(20, 20, 2, 2) - rrect = font.render_to(surf, rect, 'FoobarBax', color, None, size=24) - self.assertEqual(rrect.top, rrect.height) + rrect = font.render_to(surf, rect, "FoobarBax", color, None, size=24) + self.assertEqual(rect.topleft, rrect.topleft) self.assertNotEqual(rrect.size, rect.size) - rrect = font.render_to(surf, (20.1, 18.9), 'FoobarBax', - color, None, size=24) - ## self.assertEqual(tuple(rend[1].topleft), (20, 18)) + rrect = font.render_to(surf, (20.1, 18.9), "FoobarBax", color, None, size=24) - rrect = font.render_to(surf, rect, '', color, None, size=24) + rrect = font.render_to(surf, rect, "", color, None, size=24) self.assertFalse(rrect) self.assertEqual(rrect.height, font.get_sized_height(24)) # invalid surf test - self.assertRaises(TypeError, font.render_to, - "not a surface", "text", color) - self.assertRaises(TypeError, font.render_to, - pygame.Surface, "text", color) + self.assertRaises(TypeError, font.render_to, "not a surface", "text", color) + self.assertRaises(TypeError, font.render_to, pygame.Surface, "text", color) # invalid dest test - for dest in [None, 0, 'a', 'ab', - (), (1,), ('a', 2), (1, 'a'), (1+2j, 2), (1, 1+2j), - (1, int), (int, 1)]: - self.assertRaises(TypeError, font.render, - surf, dest, 'foobar', color, size=24) + for dest in [ + None, + 0, + "a", + "ab", + (), + (1,), + ("a", 2), + (1, "a"), + (1 + 2j, 2), + (1, 1 + 2j), + (1, int), + (int, 1), + ]: + self.assertRaises( + TypeError, font.render_to, surf, dest, "foobar", color, size=24 + ) # misc parameter test - self.assertRaises(ValueError, font.render_to, surf, (0, 0), - 'foobar', color) - self.assertRaises(TypeError, font.render_to, surf, (0, 0), - 'foobar', color, "", size=24) - self.assertRaises(ValueError, font.render_to, surf, (0, 0), - 'foobar', color, None, style=42, size=24) - self.assertRaises(TypeError, font.render_to, surf, (0, 0), - 'foobar', color, None, style=None, size=24) - self.assertRaises(ValueError, font.render_to, surf, (0, 0), - 'foobar', color, None, style=97, size=24) + self.assertRaises(ValueError, font.render_to, surf, (0, 0), "foobar", color) + self.assertRaises( + TypeError, font.render_to, surf, (0, 0), "foobar", color, 2.3, size=24 + ) + self.assertRaises( + ValueError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=42, + size=24, + ) + self.assertRaises( + TypeError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=None, + size=24, + ) + self.assertRaises( + ValueError, + font.render_to, + surf, + (0, 0), + "foobar", + color, + None, + style=97, + size=24, + ) def test_freetype_Font_render(self): - - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] surf = pygame.Surface((800, 600)) color = pygame.Color(0, 0, 0) - rend = font.render('FoobarBaz', pygame.Color(0, 0, 0), None, size=24) + rend = font.render("FoobarBaz", pygame.Color(0, 0, 0), None, size=24) self.assertIsInstance(rend, tuple) self.assertEqual(len(rend), 2) self.assertIsInstance(rend[0], pygame.Surface) self.assertIsInstance(rend[1], pygame.Rect) self.assertEqual(rend[0].get_rect().size, rend[1].size) - s, r = font.render('', pygame.Color(0, 0, 0), None, size=24) - self.assertEqual(r.width, 1) + s, r = font.render("", pygame.Color(0, 0, 0), None, size=24) + self.assertEqual(r.width, 0) self.assertEqual(r.height, font.get_sized_height(24)) self.assertEqual(s.get_size(), r.size) self.assertEqual(s.get_bitsize(), 32) # misc parameter test - self.assertRaises(ValueError, font.render, 'foobar', color) - self.assertRaises(TypeError, font.render, 'foobar', color, "", - size=24) - self.assertRaises(ValueError, font.render, 'foobar', color, None, - style=42, size=24) - self.assertRaises(TypeError, font.render, 'foobar', color, None, - style=None, size=24) - self.assertRaises(ValueError, font.render, 'foobar', color, None, - style=97, size=24) + self.assertRaises(ValueError, font.render, "foobar", color) + self.assertRaises(TypeError, font.render, "foobar", color, 2.3, size=24) + self.assertRaises( + ValueError, font.render, "foobar", color, None, style=42, size=24 + ) + self.assertRaises( + TypeError, font.render, "foobar", color, None, style=None, size=24 + ) + self.assertRaises( + ValueError, font.render, "foobar", color, None, style=97, size=24 + ) # valid surrogate pairs - font2 = self._TEST_FONTS['mono'] + font2 = self._TEST_FONTS["mono"] ucs4 = font2.ucs4 try: font2.ucs4 = False - rend1 = font2.render(as_unicode(r'\uD80C\uDC79'), color, size=24) - rend2 = font2.render(as_unicode(r'\U00013079'), color, size=24) + rend1 = font2.render("\uD80C\uDC79", color, size=24) + rend2 = font2.render("\U00013079", color, size=24) self.assertEqual(rend1[1], rend2[1]) font2.ucs4 = True - rend1 = font2.render(as_unicode(r'\uD80C\uDC79'), color, size=24) + rend1 = font2.render("\uD80C\uDC79", color, size=24) self.assertNotEqual(rend1[1], rend2[1]) finally: font2.ucs4 = ucs4 # malformed surrogate pairs - self.assertRaises(UnicodeEncodeError, font.render, - as_unicode(r'\uD80C'), color, size=24) - self.assertRaises(UnicodeEncodeError, font.render, - as_unicode(r'\uDCA7'), color, size=24) - self.assertRaises(UnicodeEncodeError, font.render, - as_unicode(r'\uD7FF\uDCA7'), color, size=24) - self.assertRaises(UnicodeEncodeError, font.render, - as_unicode(r'\uDC00\uDCA7'), color, size=24) - self.assertRaises(UnicodeEncodeError, font.render, - as_unicode(r'\uD80C\uDBFF'), color, size=24) - self.assertRaises(UnicodeEncodeError, font.render, - as_unicode(r'\uD80C\uE000'), color, size=24) - - # raises exception when uninitalized - self.assertRaises(RuntimeError, nullfont().render, - 'a', (0, 0, 0), size=24) - - # Confirm the correct glpyhs are returned for a couple of + self.assertRaises(UnicodeEncodeError, font.render, "\uD80C", color, size=24) + self.assertRaises(UnicodeEncodeError, font.render, "\uDCA7", color, size=24) + self.assertRaises( + UnicodeEncodeError, font.render, "\uD7FF\uDCA7", color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, "\uDC00\uDCA7", color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, "\uD80C\uDBFF", color, size=24 + ) + self.assertRaises( + UnicodeEncodeError, font.render, "\uD80C\uE000", color, size=24 + ) + + # raises exception when uninitialized + self.assertRaises(RuntimeError, nullfont().render, "a", (0, 0, 0), size=24) + + # Confirm the correct glyphs are returned for a couple of # unicode code points, 'A' and '\U00023079'. For each code point # the rendered glyph is compared with an image of glyph bitmap # as exported by FontForge. - path = os.path.join(FONTDIR, 'A_PyGameMono-8.png') + path = os.path.join(FONTDIR, "A_PyGameMono-8.png") A = pygame.image.load(path) - path = os.path.join(FONTDIR, 'u13079_PyGameMono-8.png') + path = os.path.join(FONTDIR, "u13079_PyGameMono-8.png") u13079 = pygame.image.load(path) - font = self._TEST_FONTS['mono'] + font = self._TEST_FONTS["mono"] font.ucs4 = False - A_rendered, r = font.render('A', bgcolor=pygame.Color('white'), size=8) - u13079_rendered, r = font.render(as_unicode(r'\U00013079'), - bgcolor=pygame.Color('white'), size=8) - - ## before comparing the surfaces, make sure they are the same - ## pixel format. Use 32-bit SRCALPHA to avoid row padding and - ## undefined bytes (the alpha byte will be set to 255.) + A_rendered, r = font.render("A", bgcolor=pygame.Color("white"), size=8) + u13079_rendered, r = font.render( + "\U00013079", bgcolor=pygame.Color("white"), size=8 + ) + + # before comparing the surfaces, make sure they are the same + # pixel format. Use 32-bit SRCALPHA to avoid row padding and + # undefined bytes (the alpha byte will be set to 255.) bitmap = pygame.Surface(A.get_size(), pygame.SRCALPHA, 32) bitmap.blit(A, (0, 0)) rendering = pygame.Surface(A_rendered.get_size(), pygame.SRCALPHA, 32) @@ -730,15 +830,14 @@ class FreeTypeFontTest(unittest.TestCase): self.assertTrue(surf_same_image(rendering, bitmap)) bitmap = pygame.Surface(u13079.get_size(), pygame.SRCALPHA, 32) bitmap.blit(u13079, (0, 0)) - rendering = pygame.Surface(u13079_rendered.get_size(), - pygame.SRCALPHA, 32) + rendering = pygame.Surface(u13079_rendered.get_size(), pygame.SRCALPHA, 32) rendering.blit(u13079_rendered, (0, 0)) self.assertTrue(surf_same_image(rendering, bitmap)) def test_freetype_Font_render_mono(self): - font = self._TEST_FONTS['sans'] - color = pygame.Color('black') - colorkey = pygame.Color('white') + font = self._TEST_FONTS["sans"] + color = pygame.Color("black") + colorkey = pygame.Color("white") text = "." save_antialiased = font.antialiased @@ -767,31 +866,33 @@ class FreeTypeFontTest(unittest.TestCase): finally: font.antialiased = save_antialiased - @unittest.skipIf(pygame.get_sdl_version()[0] == 2, "skipping due to blending issue (#864)") def test_freetype_Font_render_to_mono(self): # Blitting is done in two stages. First the target is alpha filled # with the background color, if any. Second, the foreground # color is alpha blitted to the background. - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] text = " ." rect = font.get_rect(text, size=24) size = rect.size fg = pygame.Surface((1, 1), pygame.SRCALPHA, 32) bg = pygame.Surface((1, 1), pygame.SRCALPHA, 32) surrogate = pygame.Surface((1, 1), pygame.SRCALPHA, 32) - surfaces = [pygame.Surface(size, 0, 8), - pygame.Surface(size, 0, 16), - pygame.Surface(size, pygame.SRCALPHA, 16), - pygame.Surface(size, 0, 24), - pygame.Surface(size, 0, 32), - pygame.Surface(size, pygame.SRCALPHA, 32)] + surfaces = [ + pygame.Surface(size, 0, 8), + pygame.Surface(size, 0, 16), + pygame.Surface(size, pygame.SRCALPHA, 16), + pygame.Surface(size, 0, 24), + pygame.Surface(size, 0, 32), + pygame.Surface(size, pygame.SRCALPHA, 32), + ] fg_colors = [ surfaces[0].get_palette_at(2), surfaces[1].unmap_rgb(surfaces[1].map_rgb((128, 64, 200))), surfaces[2].unmap_rgb(surfaces[2].map_rgb((99, 0, 100, 64))), (128, 97, 213), (128, 97, 213), - (128, 97, 213, 60)] + (128, 97, 213, 60), + ] fg_colors = [pygame.Color(*c) for c in fg_colors] self.assertEqual(len(surfaces), len(fg_colors)) # integrity check bg_colors = [ @@ -800,14 +901,15 @@ class FreeTypeFontTest(unittest.TestCase): surfaces[2].unmap_rgb(surfaces[2].map_rgb((55, 200, 0, 86))), (255, 120, 13), (255, 120, 13), - (255, 120, 13, 180)] + (255, 120, 13, 180), + ] bg_colors = [pygame.Color(*c) for c in bg_colors] self.assertEqual(len(surfaces), len(bg_colors)) # integrity check save_antialiased = font.antialiased font.antialiased = False try: - fill_color = pygame.Color('black') + fill_color = pygame.Color("black") for i, surf in enumerate(surfaces): surf.fill(fill_color) fg_color = fg_colors[i] @@ -815,18 +917,21 @@ class FreeTypeFontTest(unittest.TestCase): surf.blit(fg, (0, 0)) r_fg_color = surf.get_at((0, 0)) surf.set_at((0, 0), fill_color) - rrect = font.render_to(surf, (0, 0), text, fg_color, - size=24) + rrect = font.render_to(surf, (0, 0), text, fg_color, size=24) bottomleft = 0, rrect.height - 1 - self.assertEqual(surf.get_at(bottomleft), fill_color, - "Position: {}. Depth: {}." - " fg_color: {}.".format(bottomleft, - surf.get_bitsize(), fg_color)) + self.assertEqual( + surf.get_at(bottomleft), + fill_color, + "Position: {}. Depth: {}." + " fg_color: {}.".format(bottomleft, surf.get_bitsize(), fg_color), + ) bottomright = rrect.width - 1, rrect.height - 1 - self.assertEqual(surf.get_at(bottomright), r_fg_color, - "Position: {}. Depth: {}." - " fg_color: {}.".format(bottomright, - surf.get_bitsize(), fg_color)) + self.assertEqual( + surf.get_at(bottomright), + r_fg_color, + "Position: {}. Depth: {}." + " fg_color: {}.".format(bottomright, surf.get_bitsize(), fg_color), + ) for i, surf in enumerate(surfaces): surf.fill(fill_color) fg_color = fg_colors[i] @@ -856,8 +961,7 @@ class FreeTypeFontTest(unittest.TestCase): surf.blit(fg, (0, 0)) r_fg_color = surf.get_at((0, 0)) surf.set_at((0, 0), fill_color) - rrect = font.render_to(surf, (0, 0), text, fg_color, - bg_color, size=24) + rrect = font.render_to(surf, (0, 0), text, fg_color, bg_color, size=24) bottomleft = 0, rrect.height - 1 self.assertEqual(surf.get_at(bottomleft), r_bg_color) bottomright = rrect.width - 1, rrect.height - 1 @@ -866,8 +970,7 @@ class FreeTypeFontTest(unittest.TestCase): font.antialiased = save_antialiased def test_freetype_Font_render_raw(self): - - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] text = "abc" size = font.get_rect(text, size=24).size @@ -876,7 +979,7 @@ class FreeTypeFontTest(unittest.TestCase): self.assertEqual(len(rend), 2) r, s = rend - self.assertIsInstance(r, bytes_) + self.assertIsInstance(r, bytes) self.assertIsInstance(s, tuple) self.assertTrue(len(s), 2) @@ -886,51 +989,52 @@ class FreeTypeFontTest(unittest.TestCase): self.assertEqual(s, size) self.assertEqual(len(r), w * h) - r, (w, h) = font.render_raw('', size=24) + r, (w, h) = font.render_raw("", size=24) self.assertEqual(w, 0) self.assertEqual(h, font.height) self.assertEqual(len(r), 0) # bug with decenders: this would crash - rend = font.render_raw('render_raw', size=24) + rend = font.render_raw("render_raw", size=24) # bug with non-printable characters: this would cause a crash # because the text length was not adjusted for skipped characters. - text = unicode_("").join([unichr_(i) for i in range(31, 64)]) + text = "".join([chr(i) for i in range(31, 64)]) rend = font.render_raw(text, size=10) def test_freetype_Font_render_raw_to(self): - # This only checks that blits do not crash. It needs to check: # - int values # - invert option # - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] text = "abc" # No frills antialiased render to int1 (__render_glyph_INT) srect = font.get_rect(text, size=24) surf = pygame.Surface(srect.size, 0, 8) - rrect = font.render_raw_to(surf.get_view('2'), text, size=24) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) self.assertEqual(rrect, srect) for bpp in [24, 32]: surf = pygame.Surface(srect.size, 0, bpp) - rrect = font.render_raw_to(surf.get_view('r'), text, size=24) + rrect = font.render_raw_to(surf.get_view("r"), text, size=24) self.assertEqual(rrect, srect) # Underlining to int1 (__fill_glyph_INT) srect = font.get_rect(text, size=24, style=ft.STYLE_UNDERLINE) surf = pygame.Surface(srect.size, 0, 8) - rrect = font.render_raw_to(surf.get_view('2'), text, size=24, - style=ft.STYLE_UNDERLINE) + rrect = font.render_raw_to( + surf.get_view("2"), text, size=24, style=ft.STYLE_UNDERLINE + ) self.assertEqual(rrect, srect) for bpp in [24, 32]: surf = pygame.Surface(srect.size, 0, bpp) - rrect = font.render_raw_to(surf.get_view('r'), text, size=24, - style=ft.STYLE_UNDERLINE) + rrect = font.render_raw_to( + surf.get_view("r"), text, size=24, style=ft.STYLE_UNDERLINE + ) self.assertEqual(rrect, srect) # Unaliased (mono) rendering to int1 (__render_glyph_MONO_as_INT) @@ -938,12 +1042,12 @@ class FreeTypeFontTest(unittest.TestCase): try: srect = font.get_rect(text, size=24) surf = pygame.Surface(srect.size, 0, 8) - rrect = font.render_raw_to(surf.get_view('2'), text, size=24) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) self.assertEqual(rrect, srect) for bpp in [24, 32]: surf = pygame.Surface(srect.size, 0, bpp) - rrect = font.render_raw_to(surf.get_view('r'), text, size=24) + rrect = font.render_raw_to(surf.get_view("r"), text, size=24) self.assertEqual(rrect, srect) finally: font.antialiased = True @@ -954,7 +1058,7 @@ class FreeTypeFontTest(unittest.TestCase): for bpp in [16, 24, 32]: surf = pygame.Surface(srect.size, 0, bpp) - rrect = font.render_raw_to(surf.get_view('2'), text, size=24) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) self.assertEqual(rrect, srect) # Underline render to ints sized greater than 1 byte @@ -963,8 +1067,9 @@ class FreeTypeFontTest(unittest.TestCase): for bpp in [16, 24, 32]: surf = pygame.Surface(srect.size, 0, bpp) - rrect = font.render_raw_to(surf.get_view('2'), text, size=24, - style=ft.STYLE_UNDERLINE) + rrect = font.render_raw_to( + surf.get_view("2"), text, size=24, style=ft.STYLE_UNDERLINE + ) self.assertEqual(rrect, srect) # Unaliased (mono) rendering to ints greater than 1 byte @@ -975,17 +1080,37 @@ class FreeTypeFontTest(unittest.TestCase): for bpp in [16, 24, 32]: surf = pygame.Surface(srect.size, 0, bpp) - rrect = font.render_raw_to(surf.get_view('2'), - text, size=24) + rrect = font.render_raw_to(surf.get_view("2"), text, size=24) self.assertEqual(rrect, srect) finally: font.antialiased = True - def test_freetype_Font_text_is_None(self): + # Invalid dest parameter test. + srect = font.get_rect(text, size=24) + surf_buf = pygame.Surface(srect.size, 0, 32).get_view("2") + + for dest in [ + 0, + "a", + "ab", + (), + (1,), + ("a", 2), + (1, "a"), + (1 + 2j, 2), + (1, 1 + 2j), + (1, int), + (int, 1), + ]: + self.assertRaises( + TypeError, font.render_raw_to, surf_buf, text, dest, size=24 + ) + + def test_freetype_Font_text_is_None_with_arr(self): f = ft.Font(self._sans_path, 36) f.style = ft.STYLE_NORMAL f.rotation = 0 - text = 'ABCD' + text = "ABCD" # reference values get_rect = f.get_rect(text) @@ -1000,7 +1125,7 @@ class FreeTypeFontTest(unittest.TestCase): if IS_PYPY: return - arr = arrinter.Array(get_rect.size, 'u', 1) + arr = arrinter.Array(get_rect.size, "u", 1) render = f.render(text, (0, 0, 0)) render_to = f.render_to(render_to_surf, (0, 0), text, (0, 0, 0)) render_raw = f.render_raw(text) @@ -1025,7 +1150,7 @@ class FreeTypeFontTest(unittest.TestCase): f = ft.Font(self._sans_path, 36) f.style = ft.STYLE_NORMAL f.rotation = 0 - text = 'ABCD' + text = "ABCD" # reference values get_rect = f.get_rect(text) @@ -1055,11 +1180,11 @@ class FreeTypeFontTest(unittest.TestCase): def test_freetype_Font_fgcolor(self): f = ft.Font(self._bmp_8_75dpi_path) - notdef = '\0' # the PyGameMono .notdef glyph has a pixel at (0, 0) + notdef = "\0" # the PyGameMono .notdef glyph has a pixel at (0, 0) f.origin = False f.pad = False - black = pygame.Color('black') # initial color - green = pygame.Color('green') + black = pygame.Color("black") # initial color + green = pygame.Color("green") alpha128 = pygame.Color(10, 20, 30, 128) c = f.fgcolor @@ -1083,29 +1208,88 @@ class FreeTypeFontTest(unittest.TestCase): f.render_to(surf, (0, 0), None) self.assertEqual(surf.get_at((0, 0)), alpha128) - self.assertRaises(AttributeError, setattr, f, 'fgcolor', None) + self.assertRaises(AttributeError, setattr, f, "fgcolor", None) + + def test_freetype_Font_bgcolor(self): + f = ft.Font(None, 32) + zero = "0" # the default font 0 glyph does not have a pixel at (0, 0) + f.origin = False + f.pad = False + + transparent_black = pygame.Color(0, 0, 0, 0) # initial color + green = pygame.Color("green") + alpha128 = pygame.Color(10, 20, 30, 128) + + c = f.bgcolor + self.assertIsInstance(c, pygame.Color) + self.assertEqual(c, transparent_black) + + s, r = f.render(zero, pygame.Color(255, 255, 255)) + self.assertEqual(s.get_at((0, 0)), transparent_black) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + f.bgcolor = green + self.assertEqual(f.bgcolor, green) + + s, r = f.render(zero) + self.assertEqual(s.get_at((0, 0)), green) + + f.bgcolor = alpha128 + s, r = f.render(zero) + self.assertEqual(s.get_at((0, 0)), alpha128) + + surf = pygame.Surface(f.get_rect(zero).size, pygame.SRCALPHA, 32) + f.render_to(surf, (0, 0), None) + self.assertEqual(surf.get_at((0, 0)), alpha128) + + self.assertRaises(AttributeError, setattr, f, "bgcolor", None) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") + @unittest.skipIf(IS_PYPY, "pypy no likey") def test_newbuf(self): from pygame.tests.test_utils import buftools + Exporter = buftools.Exporter - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] srect = font.get_rect("Hi", size=12) - for format in ['b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', - 'x', '1x', '2x', '3x', '4x', '5x', '6x', '7x', - '8x', '9x', 'h', '=h', '@h', '!h', '1h', '=1h']: + for format in [ + "b", + "B", + "h", + "H", + "i", + "I", + "l", + "L", + "q", + "Q", + "x", + "1x", + "2x", + "3x", + "4x", + "5x", + "6x", + "7x", + "8x", + "9x", + "h", + "=h", + "@h", + "!h", + "1h", + "=1h", + ]: newbuf = Exporter(srect.size, format=format) rrect = font.render_raw_to(newbuf, "Hi", size=12) self.assertEqual(rrect, srect) # Some unsupported formats - for format in ['f', 'd', '2h', '?', 'hh']: + for format in ["f", "d", "2h", "?", "hh"]: newbuf = Exporter(srect.size, format=format, itemsize=4) - self.assertRaises(ValueError, font.render_raw_to, - newbuf, "Hi", size=12) + self.assertRaises(ValueError, font.render_raw_to, newbuf, "Hi", size=12) def test_freetype_Font_style(self): - - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] # make sure STYLE_NORMAL is the default value self.assertEqual(ft.STYLE_NORMAL, font.style) @@ -1120,16 +1304,15 @@ class FreeTypeFontTest(unittest.TestCase): with self.assertRaises(ValueError): font.style = 112 - # make assure no assignements happened + # make assure no assignments happened self.assertEqual(ft.STYLE_NORMAL, font.style) - # test assignement + # test assignment font.style = ft.STYLE_UNDERLINE self.assertEqual(ft.STYLE_UNDERLINE, font.style) # test complex styles - st = ( ft.STYLE_STRONG | ft.STYLE_UNDERLINE | - ft.STYLE_OBLIQUE ) + st = ft.STYLE_STRONG | ft.STYLE_UNDERLINE | ft.STYLE_OBLIQUE font.style = st self.assertEqual(st, font.style) @@ -1148,14 +1331,18 @@ class FreeTypeFontTest(unittest.TestCase): resolution = ft.get_default_resolution() new_font = ft.Font(self._sans_path, resolution=2 * resolution) self.assertEqual(new_font.resolution, 2 * resolution) - size_normal = self._TEST_FONTS['sans'].get_rect(text, size=24).size + size_normal = self._TEST_FONTS["sans"].get_rect(text, size=24).size size_scaled = new_font.get_rect(text, size=24).size size_by_2 = size_normal[0] * 2 - self.assertTrue(size_by_2 + 2 >= size_scaled[0] >= size_by_2 - 2, - "%i not equal %i" % (size_scaled[1], size_by_2)) + self.assertTrue( + size_by_2 + 2 >= size_scaled[0] >= size_by_2 - 2, + "%i not equal %i" % (size_scaled[1], size_by_2), + ) size_by_2 = size_normal[1] * 2 - self.assertTrue(size_by_2 + 2 >= size_scaled[1] >= size_by_2 - 2, - "%i not equal %i" % (size_scaled[1], size_by_2)) + self.assertTrue( + size_by_2 + 2 >= size_scaled[1] >= size_by_2 - 2, + "%i not equal %i" % (size_scaled[1], size_by_2), + ) new_resolution = resolution + 10 ft.set_default_resolution(new_resolution) try: @@ -1165,8 +1352,8 @@ class FreeTypeFontTest(unittest.TestCase): ft.set_default_resolution() def test_freetype_Font_path(self): - self.assertEqual(self._TEST_FONTS['sans'].path, self._sans_path) - self.assertRaises(AttributeError, getattr, nullfont(), 'path') + self.assertEqual(self._TEST_FONTS["sans"].path, self._sans_path) + self.assertRaises(AttributeError, getattr, nullfont(), "path") # This Font cache test is conditional on freetype being built by a debug # version of Python or with the C macro PGFT_DEBUG_CACHE defined. @@ -1175,11 +1362,11 @@ class FreeTypeFontTest(unittest.TestCase): glen = len(glyphs) other_glyphs = "123" oglen = len(other_glyphs) - uempty = unicode_("") -## many_glyphs = (uempty.join([unichr_(i) for i in range(32,127)] + -## [unichr_(i) for i in range(161,172)] + -## [unichr_(i) for i in range(174,239)])) - many_glyphs = uempty.join([unichr_(i) for i in range(32,127)]) + uempty = "" + ## many_glyphs = (uempty.join([chr(i) for i in range(32,127)] + + ## [chr(i) for i in range(161,172)] + + ## [chr(i) for i in range(174,239)])) + many_glyphs = uempty.join([chr(i) for i in range(32, 127)]) mglen = len(many_glyphs) count = 0 @@ -1239,16 +1426,18 @@ class FreeTypeFontTest(unittest.TestCase): f.render_raw(glyphs) f.strong = False ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats - self.assertEqual((ccount + cdelete_count, caccess, chit, cmiss), - (count, access, hit, miss)) + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) # Rotation does count += glen access += glen miss += glen f.render_raw(glyphs, rotation=10) ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats - self.assertEqual((ccount + cdelete_count, caccess, chit, cmiss), - (count, access, hit, miss)) + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) # aliased (mono) glyphs do count += oglen access += oglen @@ -1257,8 +1446,9 @@ class FreeTypeFontTest(unittest.TestCase): f.render_raw(other_glyphs) f.antialiased = True ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats - self.assertEqual((ccount + cdelete_count, caccess, chit, cmiss), - (count, access, hit, miss)) + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) # Trigger a cleanup for sure. count += 2 * mglen access += 2 * mglen @@ -1267,8 +1457,9 @@ class FreeTypeFontTest(unittest.TestCase): f.get_metrics(many_glyphs, size=10) ccount, cdelete_count, caccess, chit, cmiss = f._debug_cache_stats self.assertTrue(ccount < count) - self.assertEqual((ccount + cdelete_count, caccess, chit, cmiss), - (count, access, hit, miss)) + self.assertEqual( + (ccount + cdelete_count, caccess, chit, cmiss), (count, access, hit, miss) + ) try: ft.Font._debug_cache_stats @@ -1279,30 +1470,17 @@ class FreeTypeFontTest(unittest.TestCase): # To be consistent with pygame.font.Font, undefined codes # are rendered as the undefined character, and has metrics # of None. - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] - img, size1 = font.render(unichr_(1), (0, 0, 0), size=24) + img, size1 = font.render(chr(1), (0, 0, 0), size=24) img, size0 = font.render("", (0, 0, 0), size=24) - self.assertTrue(size1.width > size0.width ) + self.assertTrue(size1.width > size0.width) - metrics = font.get_metrics(unichr_(1) + unichr_(48), size=24) + metrics = font.get_metrics(chr(1) + chr(48), size=24) self.assertEqual(len(metrics), 2) self.assertIsNone(metrics[0]) self.assertIsInstance(metrics[1], tuple) - @unittest.skipIf(pygame.get_sdl_version()[0] == 2, "SDL2 surfaces are only limited by memory") - def test_issue_144(self): - """Issue #144: unable to render text""" - - # The bug came in two parts. The first was a convertion bug from - # FT_Fixed to integer in for an Intel x86_64 Pygame build. The second - # was to have the raised exception disappear before Font.render - # returned to Python level. - # - font = ft.Font(None, size=64) - s = 'M' * 100000 # Way too long for an SDL surface - self.assertRaises(pygame.error, font.render, s, (0, 0, 0)) - def test_issue_242(self): """Issue #242: get_rect() uses 0 as default style""" @@ -1321,13 +1499,13 @@ class FreeTypeFontTest(unittest.TestCase): # because _ftfont_getrect(), in _freetype.c, set the default # style to 0 rather than FT_STYLE_DEFAULT. # - font = self._TEST_FONTS['sans'] + font = self._TEST_FONTS["sans"] # Try wide style on a wide character. prev_style = font.wide font.wide = True try: - rect = font.get_rect('M', size=64) + rect = font.get_rect("M", size=64) surf, rrect = font.render(None, size=64) self.assertEqual(rect, rrect) finally: @@ -1337,7 +1515,7 @@ class FreeTypeFontTest(unittest.TestCase): prev_style = font.strong font.strong = True try: - rect = font.get_rect('Mm_', size=64) + rect = font.get_rect("Mm_", size=64) surf, rrect = font.render(None, size=64) self.assertEqual(rect, rrect) finally: @@ -1347,7 +1525,7 @@ class FreeTypeFontTest(unittest.TestCase): prev_style = font.oblique font.oblique = True try: - rect = font.get_rect('|', size=64) + rect = font.get_rect("|", size=64) surf, rrect = font.render(None, size=64) self.assertEqual(rect, rrect) finally: @@ -1357,7 +1535,7 @@ class FreeTypeFontTest(unittest.TestCase): prev_style = font.underline font.underline = True try: - rect = font.get_rect(' ', size=64) + rect = font.get_rect(" ", size=64) surf, rrect = font.render(None, size=64) self.assertEqual(rect, rrect) finally: @@ -1392,7 +1570,7 @@ class FreeTypeFontTest(unittest.TestCase): # Issue #243: For a string with trailing spaces, freetype ignores the # last space in boundary calculations # - font = self._TEST_FONTS['fixed'] + font = self._TEST_FONTS["fixed"] r1 = font.get_rect(" ", size=64) self.assertTrue(r1.width > 1) r2 = font.get_rect(" ", size=64) @@ -1400,12 +1578,13 @@ class FreeTypeFontTest(unittest.TestCase): def test_garbage_collection(self): """Check reference counting on returned new references""" + def ref_items(seq): return [weakref.ref(o) for o in seq] - font = self._TEST_FONTS['bmp-8-75dpi'] + font = self._TEST_FONTS["bmp-8-75dpi"] font.size = font.get_sizes()[0][0] - text = 'A' + text = "A" rect = font.get_rect(text) surf = pygame.Surface(rect.size, pygame.SRCALPHA, 32) refs = [] @@ -1428,22 +1607,20 @@ class FreeTypeFontTest(unittest.TestCase): except ImportError: pass else: - array = arrinter.Array(rect.size, 'u', 1) + array = arrinter.Array(rect.size, "u", 1) o = font.render_raw(text) self.assertEqual(getrefcount(o), 2) self.assertEqual(getrefcount(o[0]), 2) self.assertEqual(getrefcount(o[1]), 2) self.assertEqual(getrefcount(font.render_raw_to(array, text)), 1) - o = font.get_metrics('AB') + o = font.get_metrics("AB") self.assertEqual(getrefcount(o), 2) for i in range(len(o)): - self.assertEqual(getrefcount(o[i]), 2, - "refcount fail for item %d" % i) + self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i) o = font.get_sizes() self.assertEqual(getrefcount(o), 2) for i in range(len(o)): - self.assertEqual(getrefcount(o[i]), 2, - "refcount fail for item %d" % i) + self.assertEqual(getrefcount(o[i]), 2, "refcount fail for item %d" % i) def test_display_surface_quit(self): """Font.render_to() on a closed display surface""" @@ -1452,17 +1629,18 @@ class FreeTypeFontTest(unittest.TestCase): # and raise a exception if it is. This fixes a bug in Pygame revision # 0600ea4f1cfb and earlier where Pygame segfaults instead. null_surface = pygame.Surface.__new__(pygame.Surface) - f = self._TEST_FONTS['sans'] - self.assertRaises(pygame.error, f.render_to, - null_surface, (0, 0), "Crash!", size=12) + f = self._TEST_FONTS["sans"] + self.assertRaises( + pygame.error, f.render_to, null_surface, (0, 0), "Crash!", size=12 + ) def test_issue_565(self): """get_metrics supporting rotation/styles/size""" tests = [ - {'method': 'size', 'value': 36, 'msg': 'metrics same for size'}, - {'method': 'rotation', 'value': 90, 'msg': 'metrics same for rotation'}, - {'method': 'oblique', 'value': True, 'msg': 'metrics same for oblique'} + {"method": "size", "value": 36, "msg": "metrics same for size"}, + {"method": "rotation", "value": 90, "msg": "metrics same for rotation"}, + {"method": "oblique", "value": True, "msg": "metrics same for oblique"}, ] text = "|" @@ -1474,7 +1652,53 @@ class FreeTypeFontTest(unittest.TestCase): self.assertNotEqual(before, after, msg) for test in tests: - run_test(test['method'], test['value'], test['msg']) + run_test(test["method"], test["value"], test["msg"]) + + def test_freetype_SysFont_name(self): + """that SysFont accepts names of various types""" + fonts = pygame.font.get_fonts() + size = 12 + + # Check single name string: + font_name = ft.SysFont(fonts[0], size).name + self.assertFalse(font_name is None) + + # Check string of comma-separated names. + names = ",".join(fonts) + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + # Check list of names. + font_name_2 = ft.SysFont(fonts, size).name + self.assertEqual(font_name_2, font_name) + + # Check generator: + names = (name for name in fonts) + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + fonts_b = [f.encode() for f in fonts] + + # Check single name bytes. + font_name_2 = ft.SysFont(fonts_b[0], size).name + self.assertEqual(font_name_2, font_name) + + # Check comma-separated bytes. + names = b",".join(fonts_b) + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + # Check list of bytes. + font_name_2 = ft.SysFont(fonts_b, size).name + self.assertEqual(font_name_2, font_name) + + # Check mixed list of bytes and string. + names = [fonts[0], fonts_b[1], fonts[2], fonts_b[3]] + font_name_2 = ft.SysFont(names, size).name + self.assertEqual(font_name_2, font_name) + + def test_pathlib(self): + f = ft.Font(pathlib.Path(self._fixed_path), 20) class FreeTypeTest(unittest.TestCase): @@ -1492,7 +1716,7 @@ class FreeTypeTest(unittest.TestCase): new_resolution = resolution + 10 ft.set_default_resolution(new_resolution) self.assertEqual(ft.get_default_resolution(), new_resolution) - ft.init(resolution=resolution+20) + ft.init(resolution=resolution + 20) self.assertEqual(ft.get_default_resolution(), new_resolution) finally: ft.set_default_resolution() @@ -1549,6 +1773,24 @@ class FreeTypeTest(unittest.TestCase): ft.init(cache_size=new_cache_size) self.assertEqual(ft.get_cache_size(), new_cache_size) + def test_get_error(self): + """Ensures get_error() is initially empty (None).""" + error_msg = ft.get_error() + + self.assertIsNone(error_msg) + + def test_get_version(self): + # Test that get_version() can be called before init() + # Also tests get_version + ft.quit() + + # asserting not None just to have a test case + # there is no real fail condition other than + # raising an exception or a segfault, so a tuple of ints + # should be returned in all cases + self.assertIsNotNone(ft.get_version(linked=False)) + self.assertIsNotNone(ft.get_version(linked=True)) + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/ftfont_tags.py b/venv/Lib/site-packages/pygame/tests/ftfont_tags.py index 5e17673e9677f69790c64a4a0a47b2be9b0993f9..0d538f476bcd0d855e62df472d3ba12cecfb11df 100644 --- a/venv/Lib/site-packages/pygame/tests/ftfont_tags.py +++ b/venv/Lib/site-packages/pygame/tests/ftfont_tags.py @@ -1,4 +1,4 @@ -__tags__ = ['development'] +__tags__ = ["development"] exclude = False @@ -8,5 +8,4 @@ except ImportError: exclude = True if exclude: - __tags__.extend(['ignore', 'subprocess_ignore']) - + __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/venv/Lib/site-packages/pygame/tests/ftfont_test.py b/venv/Lib/site-packages/pygame/tests/ftfont_test.py index 0acd0ef91a43f991004ab91a778a288c73cfa615..cda708b1de899289aa2ef63d192a0357b6949c44 100644 --- a/venv/Lib/site-packages/pygame/tests/ftfont_test.py +++ b/venv/Lib/site-packages/pygame/tests/ftfont_test.py @@ -6,15 +6,12 @@ from pygame.tests import font_test import pygame.ftfont font_test.pygame_font = pygame.ftfont -# Disable UCS-4 specific tests as this "Font" type does accept UCS-4 codes. -font_test.UCS_4 = False for name in dir(font_test): obj = getattr(font_test, name) - if (isinstance(obj, type) and # conditional and - issubclass(obj, unittest.TestCase)): - new_name = 'Ft%s' % name - globals()[new_name] = type(new_name, (obj, ), {}) + if isinstance(obj, type) and issubclass(obj, unittest.TestCase): # conditional and + new_name = f"Ft{name}" + globals()[new_name] = type(new_name, (obj,), {}) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/gfxdraw_test.py b/venv/Lib/site-packages/pygame/tests/gfxdraw_test.py index a6e3390330b92df05a079ca4048e339621add4c6..33ee2c51ef30c86734bf4309ad575618510f873c 100644 --- a/venv/Lib/site-packages/pygame/tests/gfxdraw_test.py +++ b/venv/Lib/site-packages/pygame/tests/gfxdraw_test.py @@ -4,6 +4,7 @@ import pygame.gfxdraw from pygame.locals import * from pygame.tests.test_utils import SurfaceSubclass + def intensity(c, i): """Return color c changed by intensity i @@ -19,13 +20,14 @@ def intensity(c, i): # Darken return ((r * i) // 127, (g * i) // 127, (b * i) // 127) # Lighten - return (r + ((255 - r) * (255 - i)) // 127, - g + ((255 - g) * (255 - i)) // 127, - b + ((255 - b) * (255 - i)) // 127) - + return ( + r + ((255 - r) * (255 - i)) // 127, + g + ((255 - g) * (255 - i)) // 127, + b + ((255 - b) * (255 - i)) // 127, + ) -class GfxdrawDefaultTest( unittest.TestCase ): +class GfxdrawDefaultTest(unittest.TestCase): is_started = False foreground_color = (128, 64, 8) @@ -43,16 +45,26 @@ class GfxdrawDefaultTest( unittest.TestCase ): def check_at(self, surf, posn, color): sc = surf.get_at(posn) - fail_msg = ("%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % - (sc, color, posn, surf.get_bitsize(), surf.get_flags(), - surf.get_masks())) + fail_msg = "%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % ( + sc, + color, + posn, + surf.get_bitsize(), + surf.get_flags(), + surf.get_masks(), + ) self.assertEqual(sc, color, fail_msg) def check_not_at(self, surf, posn, color): sc = surf.get_at(posn) - fail_msg = ("%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % - (sc, color, posn, surf.get_bitsize(), surf.get_flags(), - surf.get_masks())) + fail_msg = "%s != %s at %s, bitsize: %i, flags: %i, masks: %s" % ( + sc, + color, + posn, + surf.get_bitsize(), + surf.get_flags(), + surf.get_masks(), + ) self.assertNotEqual(sc, color, fail_msg) @classmethod @@ -76,35 +88,37 @@ class GfxdrawDefaultTest( unittest.TestCase ): palette = self.default_palette if not self.is_started: # Create test surfaces - self.surfaces = [Surface(size, 0, 8), - Surface(size, SRCALPHA, 16), - Surface(size, SRCALPHA, 32)] + self.surfaces = [ + Surface(size, 0, 8), + Surface(size, SRCALPHA, 16), + Surface(size, SRCALPHA, 32), + ] self.surfaces[0].set_palette(palette) nonpalette_fmts = ( - #(8, (0xe0, 0x1c, 0x3, 0x0)), - (12, (0xf00, 0xf0, 0xf, 0x0)), - (15, (0x7c00, 0x3e0, 0x1f, 0x0)), - (15, (0x1f, 0x3e0, 0x7c00, 0x0)), - (16, (0xf00, 0xf0, 0xf, 0xf000)), - (16, (0xf000, 0xf00, 0xf0, 0xf)), - (16, (0xf, 0xf0, 0xf00, 0xf000)), - (16, (0xf0, 0xf00, 0xf000, 0xf)), - (16, (0x7c00, 0x3e0, 0x1f, 0x8000)), - (16, (0xf800, 0x7c0, 0x3e, 0x1)), - (16, (0x1f, 0x3e0, 0x7c00, 0x8000)), - (16, (0x3e, 0x7c0, 0xf800, 0x1)), - (16, (0xf800, 0x7e0, 0x1f, 0x0)), - (16, (0x1f, 0x7e0, 0xf800, 0x0)), - (24, (0xff, 0xff00, 0xff0000, 0x0)), - (24, (0xff0000, 0xff00, 0xff, 0x0)), - (32, (0xff0000, 0xff00, 0xff, 0x0)), - (32, (0xff000000, 0xff0000, 0xff00, 0x0)), - (32, (0xff, 0xff00, 0xff0000, 0x0)), - (32, (0xff00, 0xff0000, 0xff000000, 0x0)), - (32, (0xff0000, 0xff00, 0xff, 0xff000000)), - (32, (0xff000000, 0xff0000, 0xff00, 0xff)), - (32, (0xff, 0xff00, 0xff0000, 0xff000000)), - (32, (0xff00, 0xff0000, 0xff000000, 0xff)) + # (8, (0xe0, 0x1c, 0x3, 0x0)), + (12, (0xF00, 0xF0, 0xF, 0x0)), + (15, (0x7C00, 0x3E0, 0x1F, 0x0)), + (15, (0x1F, 0x3E0, 0x7C00, 0x0)), + (16, (0xF00, 0xF0, 0xF, 0xF000)), + (16, (0xF000, 0xF00, 0xF0, 0xF)), + (16, (0xF, 0xF0, 0xF00, 0xF000)), + (16, (0xF0, 0xF00, 0xF000, 0xF)), + (16, (0x7C00, 0x3E0, 0x1F, 0x8000)), + (16, (0xF800, 0x7C0, 0x3E, 0x1)), + (16, (0x1F, 0x3E0, 0x7C00, 0x8000)), + (16, (0x3E, 0x7C0, 0xF800, 0x1)), + (16, (0xF800, 0x7E0, 0x1F, 0x0)), + (16, (0x1F, 0x7E0, 0xF800, 0x0)), + (24, (0xFF, 0xFF00, 0xFF0000, 0x0)), + (24, (0xFF0000, 0xFF00, 0xFF, 0x0)), + (32, (0xFF0000, 0xFF00, 0xFF, 0x0)), + (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)), + (32, (0xFF, 0xFF00, 0xFF0000, 0x0)), + (32, (0xFF00, 0xFF0000, 0xFF000000, 0x0)), + (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)), + (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)), + (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)), + (32, (0xFF00, 0xFF0000, 0xFF000000, 0xFF)), ) for bitsize, masks in nonpalette_fmts: self.surfaces.append(Surface(size, 0, bitsize, masks)) @@ -114,8 +128,8 @@ class GfxdrawDefaultTest( unittest.TestCase ): def test_gfxdraw__subclassed_surface(self): """Ensure pygame.gfxdraw works on subclassed surfaces.""" surface = SurfaceSubclass((11, 13), SRCALPHA, 32) - surface.fill(pygame.Color('blue')) - expected_color = pygame.Color('red') + surface.fill(pygame.Color("blue")) + expected_color = pygame.Color("red") x, y = 1, 2 pygame.gfxdraw.pixel(surface, x, y, expected_color) @@ -145,9 +159,14 @@ class GfxdrawDefaultTest( unittest.TestCase ): stopx = 80 y = 50 fg_test_points = [(startx, y), (stopx, y), ((stopx - startx) // 2, y)] - bg_test_points = [(startx - 1, y), (stopx + 1, y), - (startx, y - 1), (startx, y + 1), - (stopx, y - 1), (stopx, y + 1)] + bg_test_points = [ + (startx - 1, y), + (stopx + 1, y), + (startx, y - 1), + (startx, y + 1), + (stopx, y - 1), + (stopx, y + 1), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -165,9 +184,14 @@ class GfxdrawDefaultTest( unittest.TestCase ): starty = 10 stopy = 80 fg_test_points = [(x, starty), (x, stopy), (x, (stopy - starty) // 2)] - bg_test_points = [(x, starty - 1), (x, stopy + 1), - (x - 1, starty), (x + 1, starty), - (x - 1, stopy), (x + 1, stopy)] + bg_test_points = [ + (x, starty - 1), + (x, stopy + 1), + (x - 1, starty), + (x + 1, starty), + (x - 1, stopy), + (x + 1, stopy), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -183,18 +207,22 @@ class GfxdrawDefaultTest( unittest.TestCase ): bg = self.background_color rect = pygame.Rect(10, 15, 55, 62) rect_tuple = tuple(rect) - fg_test_points = [rect.topleft, - (rect.right - 1, rect.top), - (rect.left, rect.bottom - 1), - (rect.right - 1, rect.bottom - 1)] - bg_test_points = [(rect.left - 1, rect.top - 1), - (rect.left + 1, rect.top + 1), - (rect.right, rect.top - 1), - (rect.right - 2, rect.top + 1), - (rect.left - 1, rect.bottom), - (rect.left + 1, rect.bottom - 2), - (rect.right, rect.bottom), - (rect.right - 2, rect.bottom - 2)] + fg_test_points = [ + rect.topleft, + (rect.right - 1, rect.top), + (rect.left, rect.bottom - 1), + (rect.right - 1, rect.bottom - 1), + ] + bg_test_points = [ + (rect.left - 1, rect.top - 1), + (rect.left + 1, rect.top + 1), + (rect.right, rect.top - 1), + (rect.right - 2, rect.top + 1), + (rect.left - 1, rect.bottom), + (rect.left + 1, rect.bottom - 2), + (rect.right, rect.bottom), + (rect.right - 2, rect.bottom - 2), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -216,18 +244,22 @@ class GfxdrawDefaultTest( unittest.TestCase ): bg = self.background_color rect = pygame.Rect(10, 15, 55, 62) rect_tuple = tuple(rect) - fg_test_points = [rect.topleft, - (rect.left + 1, rect.top + 1), - (rect.right - 1, rect.top), - (rect.right - 2, rect.top + 1), - (rect.left, rect.bottom - 1), - (rect.left + 1, rect.bottom - 2), - (rect.right - 1, rect.bottom - 1), - (rect.right - 2, rect.bottom - 2)] - bg_test_points = [(rect.left - 1, rect.top - 1), - (rect.right, rect.top - 1), - (rect.left - 1, rect.bottom), - (rect.right, rect.bottom)] + fg_test_points = [ + rect.topleft, + (rect.left + 1, rect.top + 1), + (rect.right - 1, rect.top), + (rect.right - 2, rect.top + 1), + (rect.left, rect.bottom - 1), + (rect.left + 1, rect.bottom - 2), + (rect.right - 1, rect.bottom - 1), + (rect.right - 2, rect.bottom - 2), + ] + bg_test_points = [ + (rect.left - 1, rect.top - 1), + (rect.right, rect.top - 1), + (rect.left - 1, rect.bottom), + (rect.right, rect.bottom), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -252,8 +284,14 @@ class GfxdrawDefaultTest( unittest.TestCase ): x2 = 92 y2 = 77 fg_test_points = [(x1, y1), (x2, y2)] - bg_test_points = [(x1 - 1, y1), (x1, y1 - 1), (x1 - 1, y1 - 1), - (x2 + 1, y2), (x2, y2 + 1), (x2 + 1, y2 + 1)] + bg_test_points = [ + (x1 - 1, y1), + (x1, y1 - 1), + (x1 - 1, y1 - 1), + (x2 + 1, y2), + (x2, y2 + 1), + (x2 + 1, y2 + 1), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -270,19 +308,18 @@ class GfxdrawDefaultTest( unittest.TestCase ): x = 45 y = 40 r = 30 - fg_test_points = [(x, y - r), - (x, y + r), - (x - r, y), - (x + r, y)] - bg_test_points = [(x, y), - (x, y - r + 1), - (x, y - r - 1), - (x, y + r + 1), - (x, y + r - 1), - (x - r - 1, y), - (x - r + 1, y), - (x + r + 1, y), - (x + r - 1, y)] + fg_test_points = [(x, y - r), (x, y + r), (x - r, y), (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r + 1), + (x, y - r - 1), + (x, y + r + 1), + (x, y + r - 1), + (x - r - 1, y), + (x - r + 1, y), + (x + r + 1, y), + (x + r - 1, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -300,17 +337,19 @@ class GfxdrawDefaultTest( unittest.TestCase ): y = 40 r = 30 start = 0 # +x direction, but not (x + r, y) (?) - end = 90 # -y direction, including (x, y + r) + end = 90 # -y direction, including (x, y + r) fg_test_points = [(x, y + r), (x + r, y + 1)] - bg_test_points = [(x, y), - (x, y - r), - (x - r, y), - (x, y + r + 1), - (x, y + r - 1), - (x - 1, y + r), - (x + r + 1, y), - (x + r - 1, y), - (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r), + (x - r, y), + (x, y + r + 1), + (x, y + r - 1), + (x - 1, y + r), + (x + r + 1, y), + (x + r - 1, y), + (x + r, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -327,19 +366,18 @@ class GfxdrawDefaultTest( unittest.TestCase ): x = 45 y = 40 r = 30 - fg_test_points = [(x, y - r), - (x, y + r), - (x - r, y), - (x + r, y)] - bg_test_points = [(x, y), - (x, y - r + 1), - (x, y - r - 1), - (x, y + r + 1), - (x, y + r - 1), - (x - r - 1, y), - (x - r + 1, y), - (x + r + 1, y), - (x + r - 1, y)] + fg_test_points = [(x, y - r), (x, y + r), (x - r, y), (x + r, y)] + bg_test_points = [ + (x, y), + (x, y - r + 1), + (x, y - r - 1), + (x, y + r + 1), + (x, y + r - 1), + (x - r - 1, y), + (x - r + 1, y), + (x + r + 1, y), + (x + r - 1, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -356,19 +394,23 @@ class GfxdrawDefaultTest( unittest.TestCase ): x = 45 y = 40 r = 30 - fg_test_points = [(x, y - r), - (x, y - r + 1), - (x, y + r), - (x, y + r - 1), - (x - r, y), - (x - r + 1, y), - (x + r, y), - (x + r - 1, y), - (x, y)] - bg_test_points = [(x, y - r - 1), - (x, y + r + 1), - (x - r - 1, y), - (x + r + 1, y)] + fg_test_points = [ + (x, y - r), + (x, y - r + 1), + (x, y + r), + (x, y + r - 1), + (x - r, y), + (x - r + 1, y), + (x + r, y), + (x + r - 1, y), + (x, y), + ] + bg_test_points = [ + (x, y - r - 1), + (x, y + r + 1), + (x - r - 1, y), + (x + r + 1, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -386,19 +428,18 @@ class GfxdrawDefaultTest( unittest.TestCase ): y = 40 rx = 30 ry = 35 - fg_test_points = [(x, y - ry), - (x, y + ry), - (x - rx, y), - (x + rx, y)] - bg_test_points = [(x, y), - (x, y - ry + 1), - (x, y - ry - 1), - (x, y + ry + 1), - (x, y + ry - 1), - (x - rx - 1, y), - (x - rx + 1, y), - (x + rx + 1, y), - (x + rx - 1, y)] + fg_test_points = [(x, y - ry), (x, y + ry), (x - rx, y), (x + rx, y)] + bg_test_points = [ + (x, y), + (x, y - ry + 1), + (x, y - ry - 1), + (x, y + ry + 1), + (x, y + ry - 1), + (x - rx - 1, y), + (x - rx + 1, y), + (x + rx + 1, y), + (x + rx - 1, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -416,19 +457,18 @@ class GfxdrawDefaultTest( unittest.TestCase ): y = 40 rx = 30 ry = 35 - fg_test_points = [(x, y - ry), - (x, y + ry), - (x - rx, y), - (x + rx, y)] - bg_test_points = [(x, y), - (x, y - ry + 1), - (x, y - ry - 1), - (x, y + ry + 1), - (x, y + ry - 1), - (x - rx - 1, y), - (x - rx + 1, y), - (x + rx + 1, y), - (x + rx - 1, y)] + fg_test_points = [(x, y - ry), (x, y + ry), (x - rx, y), (x + rx, y)] + bg_test_points = [ + (x, y), + (x, y - ry + 1), + (x, y - ry - 1), + (x, y + ry + 1), + (x, y + ry - 1), + (x - rx - 1, y), + (x - rx + 1, y), + (x + rx + 1, y), + (x + rx - 1, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -446,19 +486,23 @@ class GfxdrawDefaultTest( unittest.TestCase ): y = 40 rx = 30 ry = 35 - fg_test_points = [(x, y - ry), - (x, y - ry + 1), - (x, y + ry), - (x, y + ry - 1), - (x - rx, y), - (x - rx + 1, y), - (x + rx, y), - (x + rx - 1, y), - (x, y)] - bg_test_points = [(x, y - ry - 1), - (x, y + ry + 1), - (x - rx - 1, y), - (x + rx + 1, y)] + fg_test_points = [ + (x, y - ry), + (x, y - ry + 1), + (x, y + ry), + (x, y + ry - 1), + (x - rx, y), + (x - rx + 1, y), + (x + rx, y), + (x + rx - 1, y), + (x, y), + ] + bg_test_points = [ + (x, y - ry - 1), + (x, y + ry + 1), + (x - rx - 1, y), + (x + rx + 1, y), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -476,18 +520,17 @@ class GfxdrawDefaultTest( unittest.TestCase ): y = 40 r = 30 start = 0 # +x direction, including (x + r, y) - end = 90 # -y direction, but not (x, y + r) (?) - fg_test_points = [(x, y), - (x + 1, y), - (x, y + 1), - (x + r, y)] - bg_test_points = [(x - 1, y), - (x, y - 1), - (x - 1, y - 1), - (x + 1, y + 1), - (x + r + 1, y), - (x + r, y - 1), - (x, y + r + 1)] + end = 90 # -y direction, but not (x, y + r) (?) + fg_test_points = [(x, y), (x + 1, y), (x, y + 1), (x + r, y)] + bg_test_points = [ + (x - 1, y), + (x, y - 1), + (x - 1, y - 1), + (x + 1, y + 1), + (x + r + 1, y), + (x + r, y - 1), + (x, y + r + 1), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -508,10 +551,12 @@ class GfxdrawDefaultTest( unittest.TestCase ): x3 = 20 y3 = 60 fg_test_points = [(x1, y1), (x2, y2), (x3, y3)] - bg_test_points = [(x1 - 1, y1 - 1), - (x2 + 1, y2 + 1), - (x3 - 1, y3 + 1), - (x1 + 10, y1 + 30)] + bg_test_points = [ + (x1 - 1, y1 - 1), + (x2 + 1, y2 + 1), + (x3 - 1, y3 + 1), + (x1 + 10, y1 + 30), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -532,10 +577,12 @@ class GfxdrawDefaultTest( unittest.TestCase ): x3 = 20 y3 = 60 fg_test_points = [(x1, y1), (x2, y2), (x3, y3)] - bg_test_points = [(x1 - 1, y1 - 1), - (x2 + 1, y2 + 1), - (x3 - 1, y3 + 1), - (x1 + 10, y1 + 30)] + bg_test_points = [ + (x1 - 1, y1 - 1), + (x2 + 1, y2 + 1), + (x3 - 1, y3 + 1), + (x1 + 10, y1 + 30), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -545,7 +592,6 @@ class GfxdrawDefaultTest( unittest.TestCase ): for posn in bg_test_points: self.check_at(surf, posn, bg_adjusted) - @unittest.expectedFailure def test_aatrigon__with_horizontal_edge(self): """Ensure aatrigon draws horizontal edges correctly. @@ -556,8 +602,8 @@ class GfxdrawDefaultTest( unittest.TestCase ): Related to issue #622. """ - bg_color = pygame.Color('white') - line_color = pygame.Color('black') + bg_color = pygame.Color("white") + line_color = pygame.Color("black") width, height = 11, 10 expected_surface = pygame.Surface((width, height), 0, 32) expected_surface.fill(bg_color) @@ -569,8 +615,7 @@ class GfxdrawDefaultTest( unittest.TestCase ): x3, y3 = 0, 0 # The points in this order draw as expected. - pygame.gfxdraw.aatrigon(expected_surface, x1, y1, x2, y2, x3, y3, - line_color) + pygame.gfxdraw.aatrigon(expected_surface, x1, y1, x2, y2, x3, y3, line_color) # The points in reverse order fail to draw the horizontal edge along # the top. @@ -581,9 +626,11 @@ class GfxdrawDefaultTest( unittest.TestCase ): surface.lock() for x in range(width): for y in range(height): - self.assertEqual(expected_surface.get_at((x, y)), - surface.get_at((x, y)), - 'pos=({}, {})'.format(x, y)) + self.assertEqual( + expected_surface.get_at((x, y)), + surface.get_at((x, y)), + f"pos=({x}, {y})", + ) surface.unlock() expected_surface.unlock() @@ -598,11 +645,8 @@ class GfxdrawDefaultTest( unittest.TestCase ): y2 = 77 x3 = 20 y3 = 60 - fg_test_points = [(x1, y1), (x2, y2), (x3, y3), - (x1 + 10, y1 + 30)] - bg_test_points = [(x1 - 1, y1 - 1), - (x2 + 1, y2 + 1), - (x3 - 1, y3 + 1)] + fg_test_points = [(x1, y1), (x2, y2), (x3, y3), (x1 + 10, y1 + 30)] + bg_test_points = [(x1 - 1, y1 - 1), (x2 + 1, y2 + 1), (x3 - 1, y3 + 1)] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -617,25 +661,28 @@ class GfxdrawDefaultTest( unittest.TestCase ): fg = self.foreground_color bg = self.background_color points = [(10, 80), (10, 15), (92, 25), (92, 80)] - fg_test_points = (points + - [(points[0][0], points[0][1] - 1), - (points[0][0] + 1, points[0][1]), - (points[3][0] - 1, points[3][1]), - (points[3][0], points[3][1] - 1), - (points[2][0], points[2][1] + 1)]) - bg_test_points = [(points[0][0] - 1, points[0][1]), - (points[0][0], points[0][1] + 1), - (points[0][0] - 1, points[0][1] + 1), - (points[0][0] + 1, points[0][1] - 1), - (points[3][0] + 1, points[3][1]), - (points[3][0], points[3][1] + 1), - (points[3][0] + 1, points[3][1] + 1), - (points[3][0] - 1, points[3][1] - 1), - (points[2][0] + 1, points[2][1]), - (points[2][0] - 1, points[2][1] + 1), - (points[1][0] - 1, points[1][1]), - (points[1][0], points[1][1] - 1), - (points[1][0] - 1, points[1][1] - 1)] + fg_test_points = points + [ + (points[0][0], points[0][1] - 1), + (points[0][0] + 1, points[0][1]), + (points[3][0] - 1, points[3][1]), + (points[3][0], points[3][1] - 1), + (points[2][0], points[2][1] + 1), + ] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0] + 1, points[2][1]), + (points[2][0] - 1, points[2][1] + 1), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -651,19 +698,21 @@ class GfxdrawDefaultTest( unittest.TestCase ): bg = self.background_color points = [(10, 80), (10, 15), (92, 25), (92, 80)] fg_test_points = points - bg_test_points = [(points[0][0] - 1, points[0][1]), - (points[0][0], points[0][1] + 1), - (points[0][0] - 1, points[0][1] + 1), - (points[0][0] + 1, points[0][1] - 1), - (points[3][0] + 1, points[3][1]), - (points[3][0], points[3][1] + 1), - (points[3][0] + 1, points[3][1] + 1), - (points[3][0] - 1, points[3][1] - 1), - (points[2][0] + 1, points[2][1]), - (points[2][0] - 1, points[2][1] + 1), - (points[1][0] - 1, points[1][1]), - (points[1][0], points[1][1] - 1), - (points[1][0] - 1, points[1][1] - 1)] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0] + 1, points[2][1]), + (points[2][0] - 1, points[2][1] + 1), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -675,7 +724,6 @@ class GfxdrawDefaultTest( unittest.TestCase ): for posn in bg_test_points: self.check_at(surf, posn, bg_adjusted) - @unittest.expectedFailure def test_aapolygon__with_horizontal_edge(self): """Ensure aapolygon draws horizontal edges correctly. @@ -687,16 +735,15 @@ class GfxdrawDefaultTest( unittest.TestCase ): Related to issue #622. """ - bg_color = pygame.Color('white') - line_color = pygame.Color('black') + bg_color = pygame.Color("white") + line_color = pygame.Color("black") width, height = 11, 10 expected_surface = pygame.Surface((width, height), 0, 32) expected_surface.fill(bg_color) surface = pygame.Surface((width, height), 0, 32) surface.fill(bg_color) - points = ((0, 0), (0, height - 1), (width - 1, height - 1), - (width - 1, 0)) + points = ((0, 0), (0, height - 1), (width - 1, height - 1), (width - 1, 0)) # The points are used to draw the expected aapolygon using the line() # function. @@ -712,9 +759,11 @@ class GfxdrawDefaultTest( unittest.TestCase ): surface.lock() for x in range(width): for y in range(height): - self.assertEqual(expected_surface.get_at((x, y)), - surface.get_at((x, y)), - 'pos=({}, {})'.format(x, y)) + self.assertEqual( + expected_surface.get_at((x, y)), + surface.get_at((x, y)), + f"pos=({x}, {y})", + ) surface.unlock() expected_surface.unlock() @@ -724,25 +773,28 @@ class GfxdrawDefaultTest( unittest.TestCase ): fg = self.foreground_color bg = self.background_color points = [(10, 80), (10, 15), (92, 25), (92, 80)] - fg_test_points = (points + - [(points[0][0], points[0][1] - 1), - (points[0][0] + 1, points[0][1]), - (points[0][0] + 1, points[0][1] - 1), - (points[3][0] - 1, points[3][1]), - (points[3][0], points[3][1] - 1), - (points[3][0] - 1, points[3][1] - 1), - (points[2][0], points[2][1] + 1), - (points[2][0] - 1, points[2][1] + 1)]) - bg_test_points = [(points[0][0] - 1, points[0][1]), - (points[0][0], points[0][1] + 1), - (points[0][0] - 1, points[0][1] + 1), - (points[3][0] + 1, points[3][1]), - (points[3][0], points[3][1] + 1), - (points[3][0] + 1, points[3][1] + 1), - (points[2][0] + 1, points[2][1]), - (points[1][0] - 1, points[1][1]), - (points[1][0], points[1][1] - 1), - (points[1][0] - 1, points[1][1] - 1)] + fg_test_points = points + [ + (points[0][0], points[0][1] - 1), + (points[0][0] + 1, points[0][1]), + (points[0][0] + 1, points[0][1] - 1), + (points[3][0] - 1, points[3][1]), + (points[3][0], points[3][1] - 1), + (points[3][0] - 1, points[3][1] - 1), + (points[2][0], points[2][1] + 1), + (points[2][0] - 1, points[2][1] + 1), + ] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[2][0] + 1, points[2][1]), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -765,16 +817,18 @@ class GfxdrawDefaultTest( unittest.TestCase ): # Don't know how to really check this as boarder points may # or may not be included in the textured polygon. fg_test_points = [(points[1][0] + 30, points[1][1] + 40)] - bg_test_points = [(points[0][0] - 1, points[0][1]), - (points[0][0], points[0][1] + 1), - (points[0][0] - 1, points[0][1] + 1), - (points[3][0] + 1, points[3][1]), - (points[3][0], points[3][1] + 1), - (points[3][0] + 1, points[3][1] + 1), - (points[2][0] + 1, points[2][1]), - (points[1][0] - 1, points[1][1]), - (points[1][0], points[1][1] - 1), - (points[1][0] - 1, points[1][1] - 1)] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[0][0], points[0][1] + 1), + (points[0][0] - 1, points[0][1] + 1), + (points[3][0] + 1, points[3][1]), + (points[3][0], points[3][1] + 1), + (points[3][0] + 1, points[3][1] + 1), + (points[2][0] + 1, points[2][1]), + (points[1][0] - 1, points[1][1]), + (points[1][0], points[1][1] - 1), + (points[1][0] - 1, points[1][1] - 1), + ] for surf in self.surfaces[1:]: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -786,11 +840,15 @@ class GfxdrawDefaultTest( unittest.TestCase ): # Alpha blit to 8 bits-per-pixel surface forbidden. texture = pygame.Surface(self.default_size, SRCALPHA, 32) - self.assertRaises(ValueError, - pygame.gfxdraw.textured_polygon, - self.surfaces[0], - points, - texture, 0, 0) + self.assertRaises( + ValueError, + pygame.gfxdraw.textured_polygon, + self.surfaces[0], + points, + texture, + 0, + 0, + ) def test_bezier(self): """bezier(surface, points, steps, color): return None""" @@ -798,10 +856,12 @@ class GfxdrawDefaultTest( unittest.TestCase ): bg = self.background_color points = [(10, 50), (25, 15), (60, 80), (92, 30)] fg_test_points = [points[0], points[3]] - bg_test_points = [(points[0][0] - 1, points[0][1]), - (points[3][0] + 1, points[3][1]), - (points[1][0], points[1][1] + 3), - (points[2][0], points[2][1] - 3)] + bg_test_points = [ + (points[0][0] - 1, points[0][1]), + (points[3][0] + 1, points[3][1]), + (points[1][0], points[1][1] + 3), + (points[2][0], points[2][1] - 3), + ] for surf in self.surfaces: fg_adjusted = surf.unmap_rgb(surf.map_rgb(fg)) bg_adjusted = surf.unmap_rgb(surf.map_rgb(bg)) @@ -812,6 +872,5 @@ class GfxdrawDefaultTest( unittest.TestCase ): self.check_at(surf, posn, bg_adjusted) - -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py b/venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py index be4ee95d5ff0d254217661befaed48deddd9cede..2932f422b4860b124b9901ec20e64c81b0fbebc0 100644 --- a/venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py +++ b/venv/Lib/site-packages/pygame/tests/image__save_gl_surface_test.py @@ -6,8 +6,10 @@ import pygame from pygame.locals import * -@unittest.skipIf(os.environ.get('SDL_VIDEODRIVER') == 'dummy', - 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER') +@unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER', +) class GL_ImageSave(unittest.TestCase): def test_image_save_works_with_opengl_surfaces(self): """ @@ -15,7 +17,7 @@ class GL_ImageSave(unittest.TestCase): """ pygame.display.init() - screen = pygame.display.set_mode((640,480), OPENGL|DOUBLEBUF) + screen = pygame.display.set_mode((640, 480), OPENGL | DOUBLEBUF) pygame.display.flip() tmp_dir = test_utils.get_tmp_dir() @@ -40,5 +42,5 @@ class GL_ImageSave(unittest.TestCase): pygame.display.quit() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/image_tags.py b/venv/Lib/site-packages/pygame/tests/image_tags.py index 3f6c18199eea0248f0b48b26f0cf7fc97eba4e98..d847903d103e934ba7674ce7f33c8f6875d7ba82 100644 --- a/venv/Lib/site-packages/pygame/tests/image_tags.py +++ b/venv/Lib/site-packages/pygame/tests/image_tags.py @@ -2,6 +2,6 @@ __tags__ = [] import pygame import sys -if 'pygame.image' not in sys.modules: - __tags__.extend(('ignore', 'subprocess_ignore')) +if "pygame.image" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/venv/Lib/site-packages/pygame/tests/image_test.py b/venv/Lib/site-packages/pygame/tests/image_test.py index 9ec64a2a68e1f4ce29fe213bbcd112f6f2c9d7a8..f81674efa7b1666995a9abc22c7b8ef98efcd69c 100644 --- a/venv/Lib/site-packages/pygame/tests/image_test.py +++ b/venv/Lib/site-packages/pygame/tests/image_test.py @@ -1,58 +1,58 @@ -# -*- coding: utf-8 -*- - import array +import binascii +import io import os import tempfile import unittest import glob +import pathlib -from pygame.tests.test_utils import example_path, png +from pygame.tests.test_utils import example_path, png, tostring import pygame, pygame.image, pygame.pkgdata -from pygame.compat import xrange_, ord_, unicode_ +sdl_image_svg_jpeg_save_bug = False +_sdl_image_ver = pygame.image.get_sdl_image_version() +if _sdl_image_ver is not None: + sdl_image_svg_jpeg_save_bug = ( + _sdl_image_ver <= (2, 0, 5) and pygame.get_sdl_byteorder() == pygame.BIG_ENDIAN + ) -def test_magic(f, magic_hex): - """ tests a given file to see if the magic hex matches. - """ - data = f.read(len(magic_hex)) - if len(data) != len(magic_hex): +def test_magic(f, magic_hexes): + """Tests a given file to see if the magic hex matches.""" + data = f.read(len(magic_hexes)) + if len(data) != len(magic_hexes): return 0 - - for i in range(len(magic_hex)): - if magic_hex[i] != ord_(data[i]): + for i, magic_hex in enumerate(magic_hexes): + if magic_hex != data[i]: return 0 - return 1 -class ImageModuleTest( unittest.TestCase ): +class ImageModuleTest(unittest.TestCase): def testLoadIcon(self): - """ see if we can load the pygame icon. - """ + """see if we can load the pygame icon.""" f = pygame.pkgdata.getResource("pygame_icon.bmp") self.assertEqual(f.mode, "rb") surf = pygame.image.load_basic(f) - self.assertEqual(surf.get_at((0,0)),(5, 4, 5, 255)) - self.assertEqual(surf.get_height(),32) - self.assertEqual(surf.get_width(),32) + self.assertEqual(surf.get_at((0, 0)), (5, 4, 5, 255)) + self.assertEqual(surf.get_height(), 32) + self.assertEqual(surf.get_width(), 32) def testLoadPNG(self): - """ see if we can load a png with color values in the proper channels. - """ + """see if we can load a png with color values in the proper channels.""" # Create a PNG file with known colors reddish_pixel = (210, 0, 0, 255) greenish_pixel = (0, 220, 0, 255) bluish_pixel = (0, 0, 230, 255) greyish_pixel = (110, 120, 130, 140) - pixel_array = [reddish_pixel + greenish_pixel, - bluish_pixel + greyish_pixel] + pixel_array = [reddish_pixel + greenish_pixel, bluish_pixel + greyish_pixel] - f_descriptor, f_path = tempfile.mkstemp(suffix='.png') + f_descriptor, f_path = tempfile.mkstemp(suffix=".png") - with os.fdopen(f_descriptor, 'wb') as f: + with os.fdopen(f_descriptor, "wb") as f: w = png.Writer(2, 2, alpha=True) w.write(f, pixel_array) @@ -65,7 +65,7 @@ class ImageModuleTest( unittest.TestCase ): self.assertEqual(surf.get_at((1, 1)), greyish_pixel) # Read the PNG file obj. and verify that pygame interprets it correctly - with open(f_path, 'rb') as f: + with open(f_path, "rb") as f: surf = pygame.image.load(f) self.assertEqual(surf.get_at((0, 0)), reddish_pixel) @@ -76,22 +76,35 @@ class ImageModuleTest( unittest.TestCase ): os.remove(f_path) def testLoadJPG(self): - """ see if we can load a jpg. - """ - - f = example_path('data/alien1.jpg') # normalized - # f = os.path.join("examples", "data", "alien1.jpg") + """to see if we can load a jpg.""" + f = example_path("data/alien1.jpg") surf = pygame.image.load(f) with open(f, "rb") as f: surf = pygame.image.load(f) - # with open(os.path.join("examples", "data", "alien1.jpg"), "rb") as f: - # surf = pygame.image.load(open(os.path.join("examples", "data", - # "alien1.jpg"), "rb")) - + def testLoadBytesIO(self): + """to see if we can load images with BytesIO.""" + files = [ + "data/alien1.png", + "data/alien1.jpg", + "data/alien1.gif", + "data/asprite.bmp", + ] + + for fname in files: + with self.subTest(fname=fname): + with open(example_path(fname), "rb") as f: + img_bytes = f.read() + img_file = io.BytesIO(img_bytes) + image = pygame.image.load(img_file) + + @unittest.skipIf( + sdl_image_svg_jpeg_save_bug, + "SDL_image 2.0.5 and older has a big endian bug in jpeg saving", + ) def testSaveJPG(self): - """ JPG equivalent to issue #211 - color channel swapping + """JPG equivalent to issue #211 - color channel swapping Make sure the SDL surface color masks represent the rgb memory format required by the JPG library. The masks are machine endian dependent @@ -114,12 +127,16 @@ class ImageModuleTest( unittest.TestCase ): # # as (rect, color) pairs. def as_rect(square_x, square_y): - return Rect(square_x * square_len, square_y * square_len, - square_len, square_len) - squares = [(as_rect(0, 0), Color("red")), - (as_rect(1, 0), Color("green")), - (as_rect(0, 1), Color("blue")), - (as_rect(1, 1), Color(255, 128, 64))] + return Rect( + square_x * square_len, square_y * square_len, square_len, square_len + ) + + squares = [ + (as_rect(0, 0), Color("red")), + (as_rect(1, 0), Color("green")), + (as_rect(0, 1), Color("blue")), + (as_rect(1, 1), Color(255, 128, 64)), + ] # A surface format which is not directly usable with libjpeg. surf = pygame.Surface(sz, 0, 32) @@ -128,7 +145,7 @@ class ImageModuleTest( unittest.TestCase ): # Assume pygame.image.Load works correctly as it is handled by the # third party SDL_image library. - f_path = tempfile.mktemp(suffix='.jpg') + f_path = tempfile.mktemp(suffix=".jpg") pygame.image.save(surf, f_path) jpg_surf = pygame.image.load(f_path) @@ -136,14 +153,16 @@ class ImageModuleTest( unittest.TestCase ): def approx(c): mask = 0xFC return pygame.Color(c.r & mask, c.g & mask, c.b & mask) + offset = square_len // 2 for rect, color in squares: posn = rect.move((offset, offset)).topleft self.assertEqual(approx(jpg_surf.get_at(posn)), approx(color)) + os.remove(f_path) + def testSavePNG32(self): - """ see if we can save a png with color values in the proper channels. - """ + """see if we can save a png with color values in the proper channels.""" # Create a PNG file with known colors reddish_pixel = (215, 0, 0, 255) greenish_pixel = (0, 225, 0, 255) @@ -156,7 +175,7 @@ class ImageModuleTest( unittest.TestCase ): surf.set_at((0, 2), bluish_pixel) surf.set_at((0, 3), greyish_pixel) - f_path = tempfile.mktemp(suffix='.png') + f_path = tempfile.mktemp(suffix=".png") pygame.image.save(surf, f_path) try: @@ -178,8 +197,7 @@ class ImageModuleTest( unittest.TestCase ): os.remove(f_path) def testSavePNG24(self): - """ see if we can save a png with color values in the proper channels. - """ + """see if we can save a png with color values in the proper channels.""" # Create a PNG file with known colors reddish_pixel = (215, 0, 0) greenish_pixel = (0, 225, 0) @@ -192,7 +210,7 @@ class ImageModuleTest( unittest.TestCase ): surf.set_at((0, 2), bluish_pixel) surf.set_at((0, 3), greyish_pixel) - f_path = tempfile.mktemp(suffix='.png') + f_path = tempfile.mktemp(suffix=".png") pygame.image.save(surf, f_path) try: @@ -213,16 +231,81 @@ class ImageModuleTest( unittest.TestCase ): del reader os.remove(f_path) - def test_save(self): + def testSavePNG8(self): + """see if we can save an 8 bit png correctly""" + # Create an 8-bit PNG file with known colors + set_pixels = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (170, 146, 170)] + + size = (1, len(set_pixels)) + surf = pygame.Surface(size, depth=8) + for cnt, pix in enumerate(set_pixels): + surf.set_at((0, cnt), pix) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + width, height, pixels, _ = reader.asRGB8() + + self.assertEqual(size, (width, height)) + + # pixels is a generator + self.assertEqual(list(map(tuple, pixels)), set_pixels) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) + + def testSavePaletteAsPNG8(self): + """see if we can save a png with color values in the proper channels.""" + # Create a PNG file with known colors + pygame.display.init() + + reddish_pixel = (215, 0, 0) + greenish_pixel = (0, 225, 0) + bluish_pixel = (0, 0, 235) + greyish_pixel = (115, 125, 135) + + surf = pygame.Surface((1, 4), 0, 8) + surf.set_palette_at(0, reddish_pixel) + surf.set_palette_at(1, greenish_pixel) + surf.set_palette_at(2, bluish_pixel) + surf.set_palette_at(3, greyish_pixel) + + f_path = tempfile.mktemp(suffix=".png") + pygame.image.save(surf, f_path) + try: + # Read the PNG file and verify that pygame saved it correctly + reader = png.Reader(filename=f_path) + reader.read() + palette = reader.palette() + + # pixels is a generator + self.assertEqual(tuple(next(palette)), reddish_pixel) + self.assertEqual(tuple(next(palette)), greenish_pixel) + self.assertEqual(tuple(next(palette)), bluish_pixel) + self.assertEqual(tuple(next(palette)), greyish_pixel) + + finally: + # Ensures proper clean up. + if not reader.file.closed: + reader.file.close() + del reader + os.remove(f_path) - s = pygame.Surface((10,10)) - s.fill((23,23,23)) + def test_save(self): + s = pygame.Surface((10, 10)) + s.fill((23, 23, 23)) magic_hex = {} - magic_hex['jpg'] = [0xff, 0xd8, 0xff, 0xe0] - magic_hex['png'] = [0x89 ,0x50 ,0x4e ,0x47] + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] # magic_hex['tga'] = [0x0, 0x0, 0xa] - magic_hex['bmp'] = [0x42, 0x4d] - + magic_hex["bmp"] = [0x42, 0x4D] formats = ["jpg", "png", "bmp"] # uppercase too... JPG @@ -230,34 +313,114 @@ class ImageModuleTest( unittest.TestCase ): for fmt in formats: try: - temp_filename = "%s.%s" % ("tmpimg", fmt) + temp_filename = f"tmpimg.{fmt}" pygame.image.save(s, temp_filename) # Using 'with' ensures the file is closed even if test fails. with open(temp_filename, "rb") as handle: # Test the magic numbers at the start of the file to ensure # they are saved as the correct file type. - self.assertEqual((1, fmt), (test_magic(handle, - magic_hex[fmt.lower()]), fmt)) + self.assertEqual( + (1, fmt), (test_magic(handle, magic_hex[fmt.lower()]), fmt) + ) # load the file to make sure it was saved correctly. # Note load can load a jpg saved with a .png file name. s2 = pygame.image.load(temp_filename) - #compare contents, might only work reliably for png... + # compare contents, might only work reliably for png... # but because it's all one color it seems to work with jpg. - self.assertEqual(s2.get_at((0,0)), s.get_at((0,0))) + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) finally: - #clean up the temp file, comment out to leave tmp file after run. + # clean up the temp file, comment out to leave tmp file after run. os.remove(temp_filename) + def test_save_to_fileobject(self): + s = pygame.Surface((1, 1)) + s.fill((23, 23, 23)) + bytes_stream = io.BytesIO() + + pygame.image.save(s, bytes_stream) + bytes_stream.seek(0) + s2 = pygame.image.load(bytes_stream, "tga") + self.assertEqual(s.get_at((0, 0)), s2.get_at((0, 0))) + + def test_save_tga(self): + s = pygame.Surface((1, 1)) + s.fill((23, 23, 23)) + with tempfile.NamedTemporaryFile(suffix=".tga", delete=False) as f: + temp_filename = f.name + + try: + pygame.image.save(s, temp_filename) + s2 = pygame.image.load(temp_filename) + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) + finally: + # clean up the temp file, even if test fails + os.remove(temp_filename) + + def test_save_pathlib(self): + surf = pygame.Surface((1, 1)) + surf.fill((23, 23, 23)) + with tempfile.NamedTemporaryFile(suffix=".tga", delete=False) as f: + temp_filename = f.name + + path = pathlib.Path(temp_filename) + try: + pygame.image.save(surf, path) + s2 = pygame.image.load(path) + self.assertEqual(s2.get_at((0, 0)), surf.get_at((0, 0))) + finally: + os.remove(temp_filename) + + def test_save__to_fileobject_w_namehint_argument(self): + s = pygame.Surface((10, 10)) + s.fill((23, 23, 23)) + magic_hex = {} + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] + magic_hex["bmp"] = [0x42, 0x4D] + + formats = ["tga", "jpg", "bmp", "png"] + # uppercase too... JPG + formats = formats + [x.upper() for x in formats] + + SDL_Im_version = pygame.image.get_sdl_image_version() + # We assume here that minor version and patch level of SDL_Image + # never goes above 99 + isAtLeastSDL_image_2_0_2 = (SDL_Im_version is not None) and ( + SDL_Im_version[0] * 10000 + SDL_Im_version[1] * 100 + SDL_Im_version[2] + ) >= 20002 + for fmt in formats: + tmp_file, tmp_filename = tempfile.mkstemp(suffix=f".{fmt}") + if not isAtLeastSDL_image_2_0_2 and fmt.lower() == "jpg": + with os.fdopen(tmp_file, "wb") as handle: + with self.assertRaises(pygame.error): + pygame.image.save(s, handle, tmp_filename) + else: + with os.fdopen(tmp_file, "r+b") as handle: + pygame.image.save(s, handle, tmp_filename) + + if fmt.lower() in magic_hex: + # Test the magic numbers at the start of the file to + # ensure they are saved as the correct file type. + handle.seek(0) + self.assertEqual( + (1, fmt), (test_magic(handle, magic_hex[fmt.lower()]), fmt) + ) + # load the file to make sure it was saved correctly. + handle.flush() + handle.seek(0) + s2 = pygame.image.load(handle, tmp_filename) + self.assertEqual(s2.get_at((0, 0)), s.get_at((0, 0))) + os.remove(tmp_filename) + def test_save_colorkey(self): - """ make sure the color key is not changed when saving. - """ - s = pygame.Surface((10,10), pygame.SRCALPHA, 32) - s.fill((23,23,23)) - s.set_colorkey((0,0,0)) + """make sure the color key is not changed when saving.""" + s = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + s.fill((23, 23, 23)) + s.set_colorkey((0, 0, 0)) colorkey1 = s.get_colorkey() - p1 = s.get_at((0,0)) + p1 = s.get_at((0, 0)) temp_filename = "tmpimg.png" try: @@ -269,12 +432,13 @@ class ImageModuleTest( unittest.TestCase ): colorkey2 = s.get_colorkey() # check that the pixel and the colorkey is correct. self.assertEqual(colorkey1, colorkey2) - self.assertEqual(p1, s2.get_at((0,0))) + self.assertEqual(p1, s2.get_at((0, 0))) def test_load_unicode_path(self): import shutil - orig = unicode_(example_path("data/asprite.bmp")) - temp = os.path.join(unicode_(example_path('data')), u'你好.bmp') + + orig = example_path("data/asprite.bmp") + temp = os.path.join(example_path("data"), "你好.bmp") shutil.copy(orig, temp) try: im = pygame.image.load(temp) @@ -284,11 +448,11 @@ class ImageModuleTest( unittest.TestCase ): def _unicode_save(self, temp_file): im = pygame.Surface((10, 10), 0, 32) try: - with open(temp_file, 'w') as f: + with open(temp_file, "w") as f: pass os.remove(temp_file) - except IOError: - raise unittest.SkipTest('the path cannot be opened') + except OSError: + raise unittest.SkipTest("the path cannot be opened") self.assertFalse(os.path.exists(temp_file)) @@ -299,56 +463,75 @@ class ImageModuleTest( unittest.TestCase ): finally: try: os.remove(temp_file) - except EnvironmentError: + except OSError: pass def test_save_unicode_path(self): """save unicode object with non-ASCII chars""" - self._unicode_save(u"你好.bmp") + self._unicode_save("你好.bmp") def assertPremultipliedAreEqual(self, string1, string2, source_string): self.assertEqual(len(string1), len(string2)) block_size = 20 if string1 != string2: - for block_start in xrange_(0, len(string1), block_size): + for block_start in range(0, len(string1), block_size): block_end = min(block_start + block_size, len(string1)) block1 = string1[block_start:block_end] block2 = string2[block_start:block_end] if block1 != block2: source_block = source_string[block_start:block_end] - msg = "string difference in %d to %d of %d:\n%s\n%s\nsource:\n%s" % (block_start, block_end, len(string1), block1.encode("hex"), block2.encode("hex"), source_block.encode("hex")) + msg = ( + "string difference in %d to %d of %d:\n%s\n%s\nsource:\n%s" + % ( + block_start, + block_end, + len(string1), + binascii.hexlify(block1), + binascii.hexlify(block2), + binascii.hexlify(source_block), + ) + ) self.fail(msg) def test_to_string__premultiplied(self): - """ test to make sure we can export a surface to a premultiplied alpha string - """ + """test to make sure we can export a surface to a premultiplied alpha string""" def convertRGBAtoPremultiplied(surface_to_modify): - for x in xrange_(surface_to_modify.get_width()): - for y in xrange_(surface_to_modify.get_height()): + for x in range(surface_to_modify.get_width()): + for y in range(surface_to_modify.get_height()): color = surface_to_modify.get_at((x, y)) - premult_color = (color[0]*color[3]/255, - color[1]*color[3]/255, - color[2]*color[3]/255, - color[3]) + premult_color = ( + color[0] * color[3] / 255, + color[1] * color[3] / 255, + color[2] * color[3] / 255, + color[3], + ) surface_to_modify.set_at((x, y), premult_color) test_surface = pygame.Surface((256, 256), pygame.SRCALPHA, 32) - for x in xrange_(test_surface.get_width()): - for y in xrange_(test_surface.get_height()): - i = x + y*test_surface.get_width() - test_surface.set_at((x,y), ((i*7) % 256, (i*13) % 256, (i*27) % 256, y)) + for x in range(test_surface.get_width()): + for y in range(test_surface.get_height()): + i = x + y * test_surface.get_width() + test_surface.set_at( + (x, y), ((i * 7) % 256, (i * 13) % 256, (i * 27) % 256, y) + ) premultiplied_copy = test_surface.copy() convertRGBAtoPremultiplied(premultiplied_copy) - self.assertPremultipliedAreEqual(pygame.image.tostring(test_surface, "RGBA_PREMULT"), - pygame.image.tostring(premultiplied_copy, "RGBA"), - pygame.image.tostring(test_surface, "RGBA")) - self.assertPremultipliedAreEqual(pygame.image.tostring(test_surface, "ARGB_PREMULT"), - pygame.image.tostring(premultiplied_copy, "ARGB"), - pygame.image.tostring(test_surface, "ARGB")) + self.assertPremultipliedAreEqual( + pygame.image.tostring(test_surface, "RGBA_PREMULT"), + pygame.image.tostring(premultiplied_copy, "RGBA"), + pygame.image.tostring(test_surface, "RGBA"), + ) + self.assertPremultipliedAreEqual( + pygame.image.tostring(test_surface, "ARGB_PREMULT"), + pygame.image.tostring(premultiplied_copy, "ARGB"), + pygame.image.tostring(test_surface, "ARGB"), + ) no_alpha_surface = pygame.Surface((256, 256), 0, 24) - self.assertRaises(ValueError, pygame.image.tostring, no_alpha_surface, "RGBA_PREMULT") + self.assertRaises( + ValueError, pygame.image.tostring, no_alpha_surface, "RGBA_PREMULT" + ) # Custom assert method to check for identical surfaces. def _assertSurfaceEqual(self, surf_a, surf_b, msg=None): @@ -369,143 +552,699 @@ class ImageModuleTest( unittest.TestCase ): # Making the method lookups local for a possible speed up. surf_a_get_at = surf_a.get_at surf_b_get_at = surf_b.get_at - for y in xrange_(a_height): - for x in xrange_(a_width): - self.assertEqual(surf_a_get_at((x, y)), surf_b_get_at((x, y)), - msg) + for y in range(a_height): + for x in range(a_width): + self.assertEqual( + surf_a_get_at((x, y)), + surf_b_get_at((x, y)), + "%s (pixel: %d, %d)" % (msg, x, y), + ) def test_fromstring__and_tostring(self): """Ensure methods tostring() and fromstring() are symmetric.""" - #################################################################### - def RotateRGBAtoARGB(str_buf): - byte_buf = array.array("B", str_buf) - num_quads = len(byte_buf)//4 - for i in xrange_(num_quads): - alpha = byte_buf[i*4 + 3] - byte_buf[i*4 + 3] = byte_buf[i*4 + 2] - byte_buf[i*4 + 2] = byte_buf[i*4 + 1] - byte_buf[i*4 + 1] = byte_buf[i*4 + 0] - byte_buf[i*4 + 0] = alpha - return byte_buf.tostring() + import itertools - #################################################################### - def RotateARGBtoRGBA(str_buf): + fmts = ("RGBA", "ARGB", "BGRA") + fmt_permutations = itertools.permutations(fmts, 2) + fmt_combinations = itertools.combinations(fmts, 2) + + def convert(fmt1, fmt2, str_buf): + pos_fmt1 = {k: v for v, k in enumerate(fmt1)} + pos_fmt2 = {k: v for v, k in enumerate(fmt2)} byte_buf = array.array("B", str_buf) - num_quads = len(byte_buf)//4 - for i in xrange_(num_quads): - alpha = byte_buf[i*4 + 0] - byte_buf[i*4 + 0] = byte_buf[i*4 + 1] - byte_buf[i*4 + 1] = byte_buf[i*4 + 2] - byte_buf[i*4 + 2] = byte_buf[i*4 + 3] - byte_buf[i*4 + 3] = alpha - return byte_buf.tostring() + num_quads = len(byte_buf) // 4 + for i in range(num_quads): + i4 = i * 4 + R = byte_buf[i4 + pos_fmt1["R"]] + G = byte_buf[i4 + pos_fmt1["G"]] + B = byte_buf[i4 + pos_fmt1["B"]] + A = byte_buf[i4 + pos_fmt1["A"]] + byte_buf[i4 + pos_fmt2["R"]] = R + byte_buf[i4 + pos_fmt2["G"]] = G + byte_buf[i4 + pos_fmt2["B"]] = B + byte_buf[i4 + pos_fmt2["A"]] = A + return tostring(byte_buf) #################################################################### - test_surface = pygame.Surface((64, 256), flags=pygame.SRCALPHA, - depth=32) - for i in xrange_(256): - for j in xrange_(16): - intensity = j*16 + 15 + test_surface = pygame.Surface((64, 256), flags=pygame.SRCALPHA, depth=32) + for i in range(256): + for j in range(16): + intensity = j * 16 + 15 test_surface.set_at((j + 0, i), (intensity, i, i, i)) test_surface.set_at((j + 16, i), (i, intensity, i, i)) test_surface.set_at((j + 32, i), (i, i, intensity, i)) test_surface.set_at((j + 32, i), (i, i, i, intensity)) - self._assertSurfaceEqual(test_surface, test_surface, - 'failing with identical surfaces') - - rgba_buf = pygame.image.tostring(test_surface, "RGBA") - rgba_buf = RotateARGBtoRGBA(RotateRGBAtoARGB(rgba_buf)) - test_rotate_functions = pygame.image.fromstring( - rgba_buf, test_surface.get_size(), "RGBA") - - self._assertSurfaceEqual(test_surface, test_rotate_functions, - 'rotate functions are not symmetric') - - rgba_buf = pygame.image.tostring(test_surface, "RGBA") - argb_buf = RotateRGBAtoARGB(rgba_buf) - test_from_argb_string = pygame.image.fromstring( - argb_buf, test_surface.get_size(), "ARGB") - - self._assertSurfaceEqual(test_surface, test_from_argb_string, - '"RGBA" rotated to "ARGB" failed') - - argb_buf = pygame.image.tostring(test_surface, "ARGB") - rgba_buf = RotateARGBtoRGBA(argb_buf) - test_to_argb_string = pygame.image.fromstring( - rgba_buf, test_surface.get_size(), "RGBA") - - self._assertSurfaceEqual(test_surface, test_to_argb_string, - '"ARGB" rotated to "RGBA" failed') - - for fmt in ('ARGB', 'RGBA'): - fmt_buf = pygame.image.tostring(test_surface, fmt) + self._assertSurfaceEqual( + test_surface, test_surface, "failing with identical surfaces" + ) + + for pair in fmt_combinations: + fmt1_buf = pygame.image.tostring(test_surface, pair[0]) + fmt1_convert_buf = convert( + pair[1], pair[0], convert(pair[0], pair[1], fmt1_buf) + ) + test_convert_two_way = pygame.image.fromstring( + fmt1_convert_buf, test_surface.get_size(), pair[0] + ) + + self._assertSurfaceEqual( + test_surface, + test_convert_two_way, + f"converting {pair[0]} to {pair[1]} and back is not symmetric", + ) + + for pair in fmt_permutations: + fmt1_buf = pygame.image.tostring(test_surface, pair[0]) + fmt2_convert_buf = convert(pair[0], pair[1], fmt1_buf) + test_convert_one_way = pygame.image.fromstring( + fmt2_convert_buf, test_surface.get_size(), pair[1] + ) + + self._assertSurfaceEqual( + test_surface, + test_convert_one_way, + f"converting {pair[0]} to {pair[1]} failed", + ) + + for fmt in fmts: + test_buf = pygame.image.tostring(test_surface, fmt) test_to_from_fmt_string = pygame.image.fromstring( - fmt_buf, test_surface.get_size(), fmt) - - self._assertSurfaceEqual(test_surface, test_to_from_fmt_string, - 'tostring/fromstring functions are not ' - 'symmetric with "{}" format'.format(fmt)) - - def todo_test_frombuffer(self): - - # __doc__ (as of 2008-08-02) for pygame.image.frombuffer: + test_buf, test_surface.get_size(), fmt + ) + + self._assertSurfaceEqual( + test_surface, + test_to_from_fmt_string, + "tostring/fromstring functions are not " + f"symmetric with '{fmt}' format", + ) + + def test_tostring_depth_24(self): + test_surface = pygame.Surface((64, 256), depth=24) + for i in range(256): + for j in range(16): + intensity = j * 16 + 15 + test_surface.set_at((j + 0, i), (intensity, i, i, i)) + test_surface.set_at((j + 16, i), (i, intensity, i, i)) + test_surface.set_at((j + 32, i), (i, i, intensity, i)) + test_surface.set_at((j + 32, i), (i, i, i, intensity)) - # pygame.image.frombuffer(string, size, format): return Surface - # create a new Surface that shares data inside a string buffer - # - # Create a new Surface that shares pixel data directly from the string - # buffer. This method takes the same arguments as - # pygame.image.fromstring(), but is unable to vertically flip the - # source data. - # - # This will run much faster than pygame.image.fromstring, since no - # pixel data must be allocated and copied. + fmt = "RGB" + fmt_buf = pygame.image.tostring(test_surface, fmt) + test_to_from_fmt_string = pygame.image.fromstring( + fmt_buf, test_surface.get_size(), fmt + ) + + self._assertSurfaceEqual( + test_surface, + test_to_from_fmt_string, + f'tostring/fromstring functions are not symmetric with "{fmt}" format', + ) + + def test_frombuffer_8bit(self): + """test reading pixel data from a bytes buffer""" + pygame.display.init() + eight_bit_palette_buffer = bytearray( + [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3] + ) + + eight_bit_surf = pygame.image.frombuffer(eight_bit_palette_buffer, (4, 4), "P") + eight_bit_surf.set_palette( + [(255, 10, 20), (255, 255, 255), (0, 0, 0), (50, 200, 20)] + ) + self.assertEqual(eight_bit_surf.get_at((0, 0)), pygame.Color(255, 10, 20)) + self.assertEqual(eight_bit_surf.get_at((1, 1)), pygame.Color(255, 255, 255)) + self.assertEqual(eight_bit_surf.get_at((2, 2)), pygame.Color(0, 0, 0)) + self.assertEqual(eight_bit_surf.get_at((3, 3)), pygame.Color(50, 200, 20)) + + def test_frombuffer_RGB(self): + rgb_buffer = bytearray( + [ + 255, + 10, + 20, + 255, + 10, + 20, + 255, + 10, + 20, + 255, + 10, + 20, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 50, + 200, + 20, + 50, + 200, + 20, + 50, + 200, + 20, + 50, + 200, + 20, + ] + ) + + rgb_surf = pygame.image.frombuffer(rgb_buffer, (4, 4), "RGB") + self.assertEqual(rgb_surf.get_at((0, 0)), pygame.Color(255, 10, 20)) + self.assertEqual(rgb_surf.get_at((1, 1)), pygame.Color(255, 255, 255)) + self.assertEqual(rgb_surf.get_at((2, 2)), pygame.Color(0, 0, 0)) + self.assertEqual(rgb_surf.get_at((3, 3)), pygame.Color(50, 200, 20)) + + def test_frombuffer_BGR(self): + bgr_buffer = bytearray( + [ + 20, + 10, + 255, + 20, + 10, + 255, + 20, + 10, + 255, + 20, + 10, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20, + 200, + 50, + 20, + 200, + 50, + 20, + 200, + 50, + 20, + 200, + 50, + ] + ) + + bgr_surf = pygame.image.frombuffer(bgr_buffer, (4, 4), "BGR") + self.assertEqual(bgr_surf.get_at((0, 0)), pygame.Color(255, 10, 20)) + self.assertEqual(bgr_surf.get_at((1, 1)), pygame.Color(255, 255, 255)) + self.assertEqual(bgr_surf.get_at((2, 2)), pygame.Color(0, 0, 0)) + self.assertEqual(bgr_surf.get_at((3, 3)), pygame.Color(50, 200, 20)) + + def test_frombuffer_BGRA(self): + bgra_buffer = bytearray( + [ + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + ] + ) + + bgra_surf = pygame.image.frombuffer(bgra_buffer, (4, 4), "BGRA") + self.assertEqual(bgra_surf.get_at((0, 0)), pygame.Color(20, 10, 255, 200)) + self.assertEqual(bgra_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 127)) + self.assertEqual(bgra_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 79)) + self.assertEqual(bgra_surf.get_at((3, 3)), pygame.Color(20, 200, 50, 255)) + + def test_frombuffer_RGBX(self): + rgbx_buffer = bytearray( + [ + 255, + 10, + 20, + 255, + 255, + 10, + 20, + 255, + 255, + 10, + 20, + 255, + 255, + 10, + 20, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 255, + 0, + 0, + 0, + 255, + 0, + 0, + 0, + 255, + 0, + 0, + 0, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + ] + ) + + rgbx_surf = pygame.image.frombuffer(rgbx_buffer, (4, 4), "RGBX") + self.assertEqual(rgbx_surf.get_at((0, 0)), pygame.Color(255, 10, 20, 255)) + self.assertEqual(rgbx_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 255)) + self.assertEqual(rgbx_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 255)) + self.assertEqual(rgbx_surf.get_at((3, 3)), pygame.Color(50, 200, 20, 255)) + + def test_frombuffer_RGBA(self): + rgba_buffer = bytearray( + [ + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + ] + ) + + rgba_surf = pygame.image.frombuffer(rgba_buffer, (4, 4), "RGBA") + self.assertEqual(rgba_surf.get_at((0, 0)), pygame.Color(255, 10, 20, 200)) + self.assertEqual(rgba_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 127)) + self.assertEqual(rgba_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 79)) + self.assertEqual(rgba_surf.get_at((3, 3)), pygame.Color(50, 200, 20, 255)) + + def test_frombuffer_ARGB(self): + argb_buffer = bytearray( + [ + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 200, + 255, + 10, + 20, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 127, + 255, + 255, + 255, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 79, + 0, + 0, + 0, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + 255, + 50, + 200, + 20, + ] + ) + + argb_surf = pygame.image.frombuffer(argb_buffer, (4, 4), "ARGB") + self.assertEqual(argb_surf.get_at((0, 0)), pygame.Color(255, 10, 20, 200)) + self.assertEqual(argb_surf.get_at((1, 1)), pygame.Color(255, 255, 255, 127)) + self.assertEqual(argb_surf.get_at((2, 2)), pygame.Color(0, 0, 0, 79)) + self.assertEqual(argb_surf.get_at((3, 3)), pygame.Color(50, 200, 20, 255)) + + def test_get_extended(self): + # Create a png file and try to load it. If it cannot, get_extended() should return False + raw_image = [] + raw_image.append((200, 200, 200, 255, 100, 100, 100, 255)) + + f_descriptor, f_path = tempfile.mkstemp(suffix=".png") + + with os.fdopen(f_descriptor, "wb") as file: + w = png.Writer(2, 1, alpha=True) + w.write(file, raw_image) - self.fail() + try: + surf = pygame.image.load(f_path) + loaded = True + except pygame.error: + loaded = False - def todo_test_get_extended(self): + self.assertEqual(pygame.image.get_extended(), loaded) + os.remove(f_path) - # __doc__ (as of 2008-08-02) for pygame.image.get_extended: + def test_get_sdl_image_version(self): + # If get_extended() returns False then get_sdl_image_version() should + # return None + if not pygame.image.get_extended(): + self.assertIsNone(pygame.image.get_sdl_image_version()) + else: + expected_length = 3 + expected_type = tuple + expected_item_type = int - # pygame.image.get_extended(): return bool - # test if extended image formats can be loaded - # - # If pygame is built with extended image formats this function will - # return True. It is still not possible to determine which formats - # will be available, but generally you will be able to load them all. + version = pygame.image.get_sdl_image_version() - self.fail() + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) - def todo_test_load_basic(self): + for item in version: + self.assertIsInstance(item, expected_item_type) - # __doc__ (as of 2008-08-02) for pygame.image.load_basic: + def test_load_basic(self): + """to see if we can load bmp from files and/or file-like objects in memory""" - # pygame.image.load(filename): return Surface - # pygame.image.load(fileobj, namehint=): return Surface - # load new image from a file + # pygame.image.load(filename): return Surface - self.fail() + # test loading from a file + s = pygame.image.load_basic(example_path("data/asprite.bmp")) + self.assertEqual(s.get_at((0, 0)), (255, 255, 255, 255)) - def todo_test_load_extended(self): + # test loading from io.BufferedReader + f = pygame.pkgdata.getResource("pygame_icon.bmp") + self.assertEqual(f.mode, "rb") - # __doc__ (as of 2008-08-02) for pygame.image.load_extended: + surf = pygame.image.load_basic(f) - # pygame module for image transfer + self.assertEqual(surf.get_at((0, 0)), (5, 4, 5, 255)) + self.assertEqual(surf.get_height(), 32) + self.assertEqual(surf.get_width(), 32) - self.fail() + f.close() - def todo_test_save_extended(self): + def test_load_extended(self): + """can load different format images. - # __doc__ (as of 2008-08-02) for pygame.image.save_extended: + We test loading the following file types: + bmp, png, jpg, gif (non-animated), pcx, tga (uncompressed), tif, xpm, ppm, pgm + Following file types are tested when using SDL 2 + svg, pnm, webp + All the loaded images are smaller than 32 x 32 pixels. + """ - # pygame module for image transfer + filename_expected_color = [ + ("asprite.bmp", (255, 255, 255, 255)), + ("laplacian.png", (10, 10, 70, 255)), + ("red.jpg", (254, 0, 0, 255)), + ("blue.gif", (0, 0, 255, 255)), + ("green.pcx", (0, 255, 0, 255)), + ("yellow.tga", (255, 255, 0, 255)), + ("turquoise.tif", (0, 255, 255, 255)), + ("purple.xpm", (255, 0, 255, 255)), + ("black.ppm", (0, 0, 0, 255)), + ("grey.pgm", (120, 120, 120, 255)), + ("teal.svg", (0, 128, 128, 255)), + ("crimson.pnm", (220, 20, 60, 255)), + ("scarlet.webp", (252, 14, 53, 255)), + ] + + for filename, expected_color in filename_expected_color: + if filename.endswith("svg") and sdl_image_svg_jpeg_save_bug: + # SDL_image 2.0.5 and older has an svg loading bug on big + # endian platforms + continue + + with self.subTest( + f'Test loading a {filename.split(".")[-1]}', + filename="examples/data/" + filename, + expected_color=expected_color, + ): + surf = pygame.image.load_extended(example_path("data/" + filename)) + self.assertEqual(surf.get_at((0, 0)), expected_color) + + def test_load_pathlib(self): + """works loading using a Path argument.""" + path = pathlib.Path(example_path("data/asprite.bmp")) + surf = pygame.image.load_extended(path) + self.assertEqual(surf.get_at((0, 0)), (255, 255, 255, 255)) + + def test_save_extended(self): + surf = pygame.Surface((5, 5)) + surf.fill((23, 23, 23)) + + passing_formats = ["jpg", "png"] + passing_formats += [fmt.upper() for fmt in passing_formats] - self.fail() + magic_hex = {} + magic_hex["jpg"] = [0xFF, 0xD8, 0xFF, 0xE0] + magic_hex["png"] = [0x89, 0x50, 0x4E, 0x47] + + failing_formats = ["bmp", "tga"] + failing_formats += [fmt.upper() for fmt in failing_formats] + + # check that .jpg and .png save + for fmt in passing_formats: + temp_file_name = f"temp_file.{fmt}" + # save image as .jpg and .png + pygame.image.save_extended(surf, temp_file_name) + with open(temp_file_name, "rb") as file: + # Test the magic numbers at the start of the file to ensure + # they are saved as the correct file type. + self.assertEqual(1, (test_magic(file, magic_hex[fmt.lower()]))) + # load the file to make sure it was saved correctly + loaded_file = pygame.image.load(temp_file_name) + self.assertEqual(loaded_file.get_at((0, 0)), surf.get_at((0, 0))) + # clean up the temp file + os.remove(temp_file_name) + # check that .bmp and .tga do not save + for fmt in failing_formats: + self.assertRaises( + pygame.error, pygame.image.save_extended, surf, f"temp_file.{fmt}" + ) def threads_load(self, images): import pygame.threads + for i in range(10): surfs = pygame.threads.tmap(pygame.image.load, images) for s in surfs: @@ -523,5 +1262,10 @@ class ImageModuleTest( unittest.TestCase ): def test_load_gif_threads(self): self.threads_load(glob.glob(example_path("data/*.gif"))) -if __name__ == '__main__': + def test_from_to_bytes_exists(self): + getattr(pygame.image, "frombytes") + getattr(pygame.image, "tobytes") + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/imageext_tags.py b/venv/Lib/site-packages/pygame/tests/imageext_tags.py index 60df1da312051ba6d796b1ff7140912a63d88cc9..25cff743808701fcba6f771f3838c33bd50c9b94 100644 --- a/venv/Lib/site-packages/pygame/tests/imageext_tags.py +++ b/venv/Lib/site-packages/pygame/tests/imageext_tags.py @@ -2,7 +2,6 @@ __tags__ = [] import pygame import sys -if 'pygame.imageext' not in sys.modules: - __tags__.extend(('ignore', 'subprocess_ignore')) - +if "pygame.imageext" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/venv/Lib/site-packages/pygame/tests/imageext_test.py b/venv/Lib/site-packages/pygame/tests/imageext_test.py index 530dc12335527d5d226e085c7609f7ddf6d6c39a..c5ce75916fc8ab0968fe866a6cda8850fe87c61a 100644 --- a/venv/Lib/site-packages/pygame/tests/imageext_test.py +++ b/venv/Lib/site-packages/pygame/tests/imageext_test.py @@ -1,4 +1,3 @@ -# -*- coding: utf8 -*- import os import os.path import sys @@ -6,10 +5,12 @@ import unittest from pygame.tests.test_utils import example_path import pygame, pygame.image, pygame.pkgdata -from pygame.compat import as_unicode, unicode_ -imageext = sys.modules['pygame.imageext'] -class ImageextModuleTest( unittest.TestCase ): + +imageext = sys.modules["pygame.imageext"] + + +class ImageextModuleTest(unittest.TestCase): # Most of the testing is done indirectly through image_test.py # This just confirms file path encoding and error handling. def test_save_non_string_file(self): @@ -17,17 +18,17 @@ class ImageextModuleTest( unittest.TestCase ): self.assertRaises(TypeError, imageext.save_extended, im, []) def test_load_non_string_file(self): - self.assertRaises(pygame.error, imageext.load_extended, []) + self.assertRaises(TypeError, imageext.load_extended, []) @unittest.skip("SDL silently removes invalid characters") def test_save_bad_filename(self): im = pygame.Surface((10, 10), 0, 32) - u = u"a\x00b\x00c.png" + u = "a\x00b\x00c.png" self.assertRaises(pygame.error, imageext.save_extended, im, u) @unittest.skip("SDL silently removes invalid characters") def test_load_bad_filename(self): - u = u"a\x00b\x00c.png" + u = "a\x00b\x00c.png" self.assertRaises(pygame.error, imageext.load_extended, u) def test_save_unknown_extension(self): @@ -37,21 +38,22 @@ class ImageextModuleTest( unittest.TestCase ): def test_load_unknown_extension(self): s = "foo.bar" - self.assertRaises(pygame.error, imageext.load_extended, s) + self.assertRaises(FileNotFoundError, imageext.load_extended, s) def test_load_unknown_file(self): s = "nonexistent.png" - self.assertRaises(pygame.error, imageext.load_extended, s) + self.assertRaises(FileNotFoundError, imageext.load_extended, s) def test_load_unicode_path_0(self): - u = unicode_(example_path("data/alien1.png")) + u = example_path("data/alien1.png") im = imageext.load_extended(u) def test_load_unicode_path_1(self): """non-ASCII unicode""" import shutil - orig = unicode_(example_path("data/alien1.png")) - temp = os.path.join(unicode_(example_path('data')), u'你好.png') + + orig = example_path("data/alien1.png") + temp = os.path.join(example_path("data"), "你好.png") shutil.copy(orig, temp) try: im = imageext.load_extended(temp) @@ -61,11 +63,11 @@ class ImageextModuleTest( unittest.TestCase ): def _unicode_save(self, temp_file): im = pygame.Surface((10, 10), 0, 32) try: - with open(temp_file, 'w') as f: + with open(temp_file, "w") as f: pass os.remove(temp_file) - except IOError: - raise unittest.SkipTest('the path cannot be opened') + except OSError: + raise unittest.SkipTest("the path cannot be opened") self.assertFalse(os.path.exists(temp_file)) @@ -76,15 +78,16 @@ class ImageextModuleTest( unittest.TestCase ): finally: try: os.remove(temp_file) - except EnvironmentError: + except OSError: pass def test_save_unicode_path_0(self): """unicode object with ASCII chars""" - self._unicode_save(u"temp_file.png") + self._unicode_save("temp_file.png") def test_save_unicode_path_1(self): - self._unicode_save(u"你好.png") + self._unicode_save("你好.png") + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/joystick_test.py b/venv/Lib/site-packages/pygame/tests/joystick_test.py index 7d5b3285bf632a6392a0dd6a6591bfb90cb5a51b..47ce3f84922c134afe545e2eb664a1228a542aff 100644 --- a/venv/Lib/site-packages/pygame/tests/joystick_test.py +++ b/venv/Lib/site-packages/pygame/tests/joystick_test.py @@ -1,91 +1,166 @@ import unittest +from pygame.tests.test_utils import question, prompt + +import pygame +import pygame._sdl2.controller + class JoystickTypeTest(unittest.TestCase): def todo_test_Joystick(self): - # __doc__ (as of 2008-08-02) for pygame.joystick.Joystick: - # pygame.joystick.Joystick(id): return Joystick - # create a new Joystick object - # - # Create a new joystick to access a physical device. The id argument - # must be a value from 0 to pygame.joystick.get_count()-1. - # - # To access most of the Joystick methods, you'll need to init() the - # Joystick. This is separate from making sure the joystick module is - # initialized. When multiple Joysticks objects are created for the - # same physical joystick device (i.e., they have the same ID number), - # the state and values for those Joystick objects will be shared. - # - # The Joystick object allows you to get information about the types of - # controls on a joystick device. Once the device is initialized the - # Pygame event queue will start receiving events about its input. - # - # You can call the Joystick.get_name() and Joystick.get_id() functions - # without initializing the Joystick object. - # - - self.fail() - -class JoytickModuleTest(unittest.TestCase): - def todo_test_get_count(self): - - # __doc__ (as of 2008-08-02) for pygame.joystick.get_count: - - # pygame.joystick.get_count(): return count - # number of joysticks on the system - # - # Return the number of joystick devices on the system. The count will - # be 0 if there are no joysticks on the system. - # - # When you create Joystick objects using Joystick(id), you pass an - # integer that must be lower than this count. - # - - self.fail() - - def todo_test_get_init(self): - - # __doc__ (as of 2008-08-02) for pygame.joystick.get_init: - - # pygame.joystick.get_init(): return bool - # true if the joystick module is initialized - # - # Test if the pygame.joystick.init() function has been called. - - self.fail() - - def todo_test_init(self): - - # __doc__ (as of 2008-08-02) for pygame.joystick.init: - - # pygame.joystick.init(): return None - # initialize the joystick module - # - # This function is called automatically by pygame.init(). - # It initializes the joystick module. This will scan the system for - # all joystick devices. The module must be initialized before any - # other functions will work. - # - # It is safe to call this function more than once. + # pygame.joystick.Joystick(id): return Joystick + # create a new Joystick object + # + # Create a new joystick to access a physical device. The id argument + # must be a value from 0 to pygame.joystick.get_count()-1. + # + # To access most of the Joystick methods, you'll need to init() the + # Joystick. This is separate from making sure the joystick module is + # initialized. When multiple Joysticks objects are created for the + # same physical joystick device (i.e., they have the same ID number), + # the state and values for those Joystick objects will be shared. + # + # The Joystick object allows you to get information about the types of + # controls on a joystick device. Once the device is initialized the + # Pygame event queue will start receiving events about its input. + # + # You can call the Joystick.get_name() and Joystick.get_id() functions + # without initializing the Joystick object. + # self.fail() - def todo_test_quit(self): - # __doc__ (as of 2008-08-02) for pygame.joystick.quit: +class JoystickModuleTest(unittest.TestCase): + def test_get_init(self): + # Check that get_init() matches what is actually happening + def error_check_get_init(): + try: + pygame.joystick.get_count() + except pygame.error: + return False + return True + + # Start uninitialised + self.assertEqual(pygame.joystick.get_init(), False) + + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # True + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + pygame.joystick.init() + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # True + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + for i in range(100): + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # True + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + for i in range(100): + pygame.joystick.quit() + self.assertEqual(pygame.joystick.get_init(), error_check_get_init()) # False + + def test_init(self): + """ + This unit test is for joystick.init() + It was written to help reduce maintenance costs + and to help test against changes to the code or + different platforms. + """ + pygame.quit() + # test that pygame.init automatically calls joystick.init + pygame.init() + self.assertEqual(pygame.joystick.get_init(), True) + + # Controller module interferes with the joystick module. + pygame._sdl2.controller.quit() + + # test that get_count doesn't work w/o joystick init + # this is done before and after an init to test + # that init activates the joystick functions + pygame.joystick.quit() + with self.assertRaises(pygame.error): + pygame.joystick.get_count() + + # test explicit call(s) to joystick.init. + # Also test that get_count works once init is called + iterations = 20 + for i in range(iterations): + pygame.joystick.init() + self.assertEqual(pygame.joystick.get_init(), True) + self.assertIsNotNone(pygame.joystick.get_count()) + + def test_quit(self): + """Test if joystick.quit works.""" + + pygame.joystick.init() + + self.assertIsNotNone(pygame.joystick.get_count()) # Is not None before quit + + pygame.joystick.quit() + + with self.assertRaises(pygame.error): # Raises error if quit worked + pygame.joystick.get_count() + + def test_get_count(self): + # Test that get_count correctly returns a non-negative number of joysticks + pygame.joystick.init() + + try: + count = pygame.joystick.get_count() + self.assertGreaterEqual( + count, 0, ("joystick.get_count() must " "return a value >= 0") + ) + finally: + pygame.joystick.quit() + + +class JoystickInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + def test_get_count_interactive(self): + # Test get_count correctly identifies number of connected joysticks + prompt( + "Please connect any joysticks/controllers now before starting the " + "joystick.get_count() test." + ) + + pygame.joystick.init() + # pygame.joystick.get_count(): return count + # number of joysticks on the system, 0 means no joysticks connected + count = pygame.joystick.get_count() + + response = question( + "NOTE: Having Steam open may add an extra virtual controller for " + "each joystick/controller physically plugged in.\n" + f"joystick.get_count() thinks there is [{count}] joystick(s)/controller(s)" + "connected to this system. Is this correct?" + ) + + self.assertTrue(response) + + # When you create Joystick objects using Joystick(id), you pass an + # integer that must be lower than this count. + # Test Joystick(id) for each connected joystick + if count != 0: + for x in range(count): + pygame.joystick.Joystick(x) + with self.assertRaises(pygame.error): + pygame.joystick.Joystick(count) + + pygame.joystick.quit() - # pygame.joystick.quit(): return None - # uninitialize the joystick module - # - # Uninitialize the joystick module. After you call this any existing - # joystick objects will no longer work. - # - # It is safe to call this function more than once. - - self.fail() ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/key_test.py b/venv/Lib/site-packages/pygame/tests/key_test.py index 0163c7d35b11d784ceba6ab2b3df4d8f56bbf1c9..1899c73f8de6107db487b69dc74cc9454c0699b6 100644 --- a/venv/Lib/site-packages/pygame/tests/key_test.py +++ b/venv/Lib/site-packages/pygame/tests/key_test.py @@ -1,7 +1,169 @@ +import os +import time import unittest + import pygame import pygame.key +# keys that are not tested for const-name match +SKIPPED_KEYS = {"K_UNKNOWN"} + +# This is the expected compat output +KEY_NAME_COMPAT = { + "K_0": "0", + "K_1": "1", + "K_2": "2", + "K_3": "3", + "K_4": "4", + "K_5": "5", + "K_6": "6", + "K_7": "7", + "K_8": "8", + "K_9": "9", + "K_AC_BACK": "AC Back", + "K_AMPERSAND": "&", + "K_ASTERISK": "*", + "K_AT": "@", + "K_BACKQUOTE": "`", + "K_BACKSLASH": "\\", + "K_BACKSPACE": "backspace", + "K_BREAK": "break", + "K_CAPSLOCK": "caps lock", + "K_CARET": "^", + "K_CLEAR": "clear", + "K_COLON": ":", + "K_COMMA": ",", + "K_CURRENCYSUBUNIT": "CurrencySubUnit", + "K_CURRENCYUNIT": "euro", + "K_DELETE": "delete", + "K_DOLLAR": "$", + "K_DOWN": "down", + "K_END": "end", + "K_EQUALS": "=", + "K_ESCAPE": "escape", + "K_EURO": "euro", + "K_EXCLAIM": "!", + "K_F1": "f1", + "K_F10": "f10", + "K_F11": "f11", + "K_F12": "f12", + "K_F13": "f13", + "K_F14": "f14", + "K_F15": "f15", + "K_F2": "f2", + "K_F3": "f3", + "K_F4": "f4", + "K_F5": "f5", + "K_F6": "f6", + "K_F7": "f7", + "K_F8": "f8", + "K_F9": "f9", + "K_GREATER": ">", + "K_HASH": "#", + "K_HELP": "help", + "K_HOME": "home", + "K_INSERT": "insert", + "K_KP0": "[0]", + "K_KP1": "[1]", + "K_KP2": "[2]", + "K_KP3": "[3]", + "K_KP4": "[4]", + "K_KP5": "[5]", + "K_KP6": "[6]", + "K_KP7": "[7]", + "K_KP8": "[8]", + "K_KP9": "[9]", + "K_KP_0": "[0]", + "K_KP_1": "[1]", + "K_KP_2": "[2]", + "K_KP_3": "[3]", + "K_KP_4": "[4]", + "K_KP_5": "[5]", + "K_KP_6": "[6]", + "K_KP_7": "[7]", + "K_KP_8": "[8]", + "K_KP_9": "[9]", + "K_KP_DIVIDE": "[/]", + "K_KP_ENTER": "enter", + "K_KP_EQUALS": "equals", + "K_KP_MINUS": "[-]", + "K_KP_MULTIPLY": "[*]", + "K_KP_PERIOD": "[.]", + "K_KP_PLUS": "[+]", + "K_LALT": "left alt", + "K_LCTRL": "left ctrl", + "K_LEFT": "left", + "K_LEFTBRACKET": "[", + "K_LEFTPAREN": "(", + "K_LESS": "<", + "K_LGUI": "left meta", + "K_LMETA": "left meta", + "K_LSHIFT": "left shift", + "K_LSUPER": "left meta", + "K_MENU": "menu", + "K_MINUS": "-", + "K_MODE": "alt gr", + "K_NUMLOCK": "numlock", + "K_NUMLOCKCLEAR": "numlock", + "K_PAGEDOWN": "page down", + "K_PAGEUP": "page up", + "K_PAUSE": "break", + "K_PERCENT": "%", + "K_PERIOD": ".", + "K_PLUS": "+", + "K_POWER": "power", + "K_PRINT": "print screen", + "K_PRINTSCREEN": "print screen", + "K_QUESTION": "?", + "K_QUOTE": "'", + "K_QUOTEDBL": '"', + "K_RALT": "right alt", + "K_RCTRL": "right ctrl", + "K_RETURN": "return", + "K_RGUI": "right meta", + "K_RIGHT": "right", + "K_RIGHTBRACKET": "]", + "K_RIGHTPAREN": ")", + "K_RMETA": "right meta", + "K_RSHIFT": "right shift", + "K_RSUPER": "right meta", + "K_SCROLLLOCK": "scroll lock", + "K_SCROLLOCK": "scroll lock", + "K_SEMICOLON": ";", + "K_SLASH": "/", + "K_SPACE": "space", + "K_SYSREQ": "sys req", + "K_TAB": "tab", + "K_UNDERSCORE": "_", + "K_UP": "up", + "K_a": "a", + "K_b": "b", + "K_c": "c", + "K_d": "d", + "K_e": "e", + "K_f": "f", + "K_g": "g", + "K_h": "h", + "K_i": "i", + "K_j": "j", + "K_k": "k", + "K_l": "l", + "K_m": "m", + "K_n": "n", + "K_o": "o", + "K_p": "p", + "K_q": "q", + "K_r": "r", + "K_s": "s", + "K_t": "t", + "K_u": "u", + "K_v": "v", + "K_w": "w", + "K_x": "x", + "K_y": "y", + "K_z": "z", +} + class KeyModuleTest(unittest.TestCase): @classmethod @@ -12,38 +174,114 @@ class KeyModuleTest(unittest.TestCase): def tearDownClass(cls): pygame.quit() - def setUp(cls): + def setUp(self): # This makes sure pygame is always initialized before each test (in # case a test calls pygame.quit()). if not pygame.get_init(): pygame.init() + if not pygame.display.get_init(): + pygame.display.init() def test_import(self): - 'does it import' + """does it import?""" import pygame.key - def todo_test_get_focused(self): - - # __doc__ (as of 2008-08-02) for pygame.key.get_focused: - - # pygame.key.get_focused(): return bool - # true if the display is receiving keyboard input from the system - # - # This is true when the display window has keyboard focus from the - # system. If the display needs to ensure it does not lose keyboard - # focus, it can use pygame.event.set_grab() to grab all input. - # - - self.fail() + # fixme: test_get_focused failing systematically in some linux + # fixme: test_get_focused failing on SDL 2.0.18 on Windows + @unittest.skip("flaky test, and broken on 2.0.18 windows") + def test_get_focused(self): + # This test fails in SDL2 in some linux + # This test was skipped in SDL1. + focused = pygame.key.get_focused() + self.assertFalse(focused) # No window to focus + self.assertIsInstance(focused, int) + # Dummy video driver never gets keyboard focus. + if os.environ.get("SDL_VIDEODRIVER") != "dummy": + # Positive test, fullscreen with events grabbed + display_sizes = pygame.display.list_modes() + if display_sizes == -1: + display_sizes = [(500, 500)] + pygame.display.set_mode(size=display_sizes[-1], flags=pygame.FULLSCREEN) + pygame.event.set_grab(True) + # Pump event queue to get window focus on macos + pygame.event.pump() + focused = pygame.key.get_focused() + self.assertIsInstance(focused, int) + self.assertTrue(focused) + # Now test negative, iconify takes away focus + pygame.event.clear() + # TODO: iconify test fails in windows + if os.name != "nt": + pygame.display.iconify() + # Apparent need to pump event queue in order to make sure iconify + # happens. See display_test.py's test_get_active_iconify + for _ in range(50): + time.sleep(0.01) + pygame.event.pump() + self.assertFalse(pygame.key.get_focused()) + # Test if focus is returned when iconify is gone + pygame.display.set_mode(size=display_sizes[-1], flags=pygame.FULLSCREEN) + for i in range(50): + time.sleep(0.01) + pygame.event.pump() + self.assertTrue(pygame.key.get_focused()) + # Test if a quit display raises an error: + pygame.display.quit() + with self.assertRaises(pygame.error) as cm: + pygame.key.get_focused() def test_get_pressed(self): states = pygame.key.get_pressed() self.assertEqual(states[pygame.K_RIGHT], 0) - def test_name(self): - self.assertEqual(pygame.key.name(pygame.K_RETURN), "return") - self.assertEqual(pygame.key.name(pygame.K_0), "0") - self.assertEqual(pygame.key.name(pygame.K_SPACE), "space") + # def test_get_pressed_not_iter(self): + # states = pygame.key.get_pressed() + # with self.assertRaises(TypeError): + # next(states) + # with self.assertRaises(TypeError): + # for k in states: + # pass + + def test_name_and_key_code(self): + for const_name in dir(pygame): + if not const_name.startswith("K_") or const_name in SKIPPED_KEYS: + continue + + try: + expected_str_name = KEY_NAME_COMPAT[const_name] + except KeyError: + self.fail( + "If you are seeing this error in a test run, you probably added a " + "new pygame key constant, but forgot to update key_test unitests" + ) + + const_val = getattr(pygame, const_name) + + # with these tests below, we also make sure that key.name and key.key_code + # can work together and handle each others outputs + + # test positional args + self.assertEqual(pygame.key.name(const_val), expected_str_name) + # test kwarg + self.assertEqual(pygame.key.name(key=const_val), expected_str_name) + + # test positional args + self.assertEqual(pygame.key.key_code(expected_str_name), const_val) + # test kwarg + self.assertEqual(pygame.key.key_code(name=expected_str_name), const_val) + + alt_name = pygame.key.name(const_val, use_compat=False) + self.assertIsInstance(alt_name, str) + + # This is a test for an implementation detail of name with use_compat=False + # If this test breaks in the future for any key, it is safe to put skips on + # failing keys (the implementation detail is documented as being unreliable) + self.assertEqual(pygame.key.key_code(alt_name), const_val) + + self.assertRaises(TypeError, pygame.key.name, "fizzbuzz") + self.assertRaises(TypeError, pygame.key.key_code, pygame.K_a) + + self.assertRaises(ValueError, pygame.key.key_code, "fizzbuzz") def test_set_and_get_mods(self): pygame.key.set_mods(pygame.KMOD_CTRL) @@ -64,5 +302,5 @@ class KeyModuleTest(unittest.TestCase): self.assertEqual(pygame.key.get_repeat(), (0, 0)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/mask_test.py b/venv/Lib/site-packages/pygame/tests/mask_test.py index f7a508ca3eaea5087ca3daf64c0fed1fe3687ebc..bd7daf5fa5d2094ae6b953c99c5c0f4dd6829bfe 100644 --- a/venv/Lib/site-packages/pygame/tests/mask_test.py +++ b/venv/Lib/site-packages/pygame/tests/mask_test.py @@ -1,64 +1,175 @@ from collections import OrderedDict +import copy +import platform import random import unittest +import sys import pygame -import pygame.mask from pygame.locals import * +from pygame.math import Vector2 -def random_mask(size = (100,100)): +IS_PYPY = "PyPy" == platform.python_implementation() + + +def random_mask(size=(100, 100)): """random_mask(size=(100,100)): return Mask Create a mask of the given size, with roughly half the bits set at random.""" m = pygame.Mask(size) for i in range(size[0] * size[1] // 2): - x, y = random.randint(0,size[0] - 1), random.randint(0, size[1] - 1) - m.set_at((x,y)) + x, y = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1) + m.set_at((x, y)) return m -def maskFromSurface(surface, threshold = 127): + +def maskFromSurface(surface, threshold=127): mask = pygame.Mask(surface.get_size()) key = surface.get_colorkey() if key: for y in range(surface.get_height()): for x in range(surface.get_width()): - if surface.get_at((x+0.1,y+0.1)) != key: - mask.set_at((x,y),1) + if surface.get_at((x + 0.1, y + 0.1)) != key: + mask.set_at((x, y), 1) else: for y in range(surface.get_height()): - for x in range (surface.get_width()): - if surface.get_at((x,y))[3] > threshold: - mask.set_at((x,y),1) + for x in range(surface.get_width()): + if surface.get_at((x, y))[3] > threshold: + mask.set_at((x, y), 1) return mask -class MaskTypeTest(unittest.TestCase): - ORIGIN_OFFSETS = ((0, 0), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), - (-1, -1), (-1, 0), (-1, 1)) +def create_bounding_rect(points): + """Creates a bounding rect from the given points.""" + xmin = xmax = points[0][0] + ymin = ymax = points[0][1] + + for x, y in points[1:]: + xmin = min(x, xmin) + xmax = max(x, xmax) + ymin = min(y, ymin) + ymax = max(y, ymax) + + return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1)) + + +def zero_size_pairs(width, height): + """Creates a generator which yields pairs of sizes. + + For each pair of sizes at least one of the sizes will have a 0 in it. + """ + sizes = ((width, height), (width, 0), (0, height), (0, 0)) + + return ((a, b) for a in sizes for b in sizes if 0 in a or 0 in b) + + +def corners(mask): + """Returns a tuple with the corner positions of the given mask. + + Clockwise from the top left corner. + """ + width, height = mask.get_size() + return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)) + + +def off_corners(rect): + """Returns a tuple with the positions off of the corners of the given rect. - def _assertMaskEqual(self, m1, m2, msg=None): - # Checks to see if the 2 given masks are equal. - m1_count = m1.count() + Clockwise from the top left corner. + """ + return ( + (rect.left - 1, rect.top), + (rect.left - 1, rect.top - 1), + (rect.left, rect.top - 1), + (rect.right - 1, rect.top - 1), + (rect.right, rect.top - 1), + (rect.right, rect.top), + (rect.right, rect.bottom - 1), + (rect.right, rect.bottom), + (rect.right - 1, rect.bottom), + (rect.left, rect.bottom), + (rect.left - 1, rect.bottom), + (rect.left - 1, rect.bottom - 1), + ) - self.assertEqual(m1.get_size(), m2.get_size(), msg=msg) - self.assertEqual(m1_count, m2.count(), msg=msg) - self.assertEqual(m1_count, m1.overlap_area(m2, (0, 0)), msg=msg) - # This can be used to help debug exact locations. - ##for i in range(m1.get_size()[0]): - ## for j in range(m1.get_size()[1]): - ## self.assertEqual(m1.get_at((i, j)), m2.get_at((i, j))) +def assertSurfaceFilled(testcase, surface, expected_color, area_rect=None): + """Checks to see if the given surface is filled with the given color. + If an area_rect is provided, only check that area of the surface. + """ + if area_rect is None: + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + else: + area_rect.normalize() + area_rect = area_rect.clip(surface.get_rect()) + x_range = range(area_rect.left, area_rect.right) + y_range = range(area_rect.top, area_rect.bottom) + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in y_range for x in x_range): + testcase.assertEqual(surface.get_at(pos), expected_color, pos) + surface.unlock() + + +def assertSurfaceFilledIgnoreArea(testcase, surface, expected_color, ignore_rect): + """Checks if the surface is filled with the given color. The + ignore_rect area is not checked. + """ + x_range = range(surface.get_width()) + y_range = range(surface.get_height()) + ignore_rect.normalize() + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in y_range for x in x_range): + if not ignore_rect.collidepoint(pos): + testcase.assertEqual(surface.get_at(pos), expected_color, pos) + surface.unlock() + + +def assertMaskEqual(testcase, m1, m2, msg=None): + """Checks to see if the 2 given masks are equal.""" + m1_count = m1.count() + + testcase.assertEqual(m1.get_size(), m2.get_size(), msg=msg) + testcase.assertEqual(m1_count, m2.count(), msg=msg) + testcase.assertEqual(m1_count, m1.overlap_area(m2, (0, 0)), msg=msg) + + # This can be used to help debug exact locations. + ##for i in range(m1.get_size()[0]): + ## for j in range(m1.get_size()[1]): + ## testcase.assertEqual(m1.get_at((i, j)), m2.get_at((i, j))) + + +# @unittest.skipIf(IS_PYPY, "pypy has lots of mask failures") # TODO +class MaskTypeTest(unittest.TestCase): + ORIGIN_OFFSETS = ( + (0, 0), + (0, 1), + (1, 1), + (1, 0), + (1, -1), + (0, -1), + (-1, -1), + (-1, 0), + (-1, 1), + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_mask(self): """Ensure masks are created correctly without fill parameter.""" expected_count = 0 expected_size = (11, 23) + mask1 = pygame.mask.Mask(expected_size) mask2 = pygame.mask.Mask(size=expected_size) + self.assertIsInstance(mask1, pygame.mask.Mask) self.assertEqual(mask1.count(), expected_count) self.assertEqual(mask1.get_size(), expected_size) + self.assertIsInstance(mask2, pygame.mask.Mask) self.assertEqual(mask2.count(), expected_count) self.assertEqual(mask2.get_size(), expected_size) @@ -72,27 +183,50 @@ class MaskTypeTest(unittest.TestCase): """Ensure masks are created correctly using the fill keyword.""" width, height = 37, 47 expected_size = (width, height) - fill_counts = {True : width * height, False : 0 } + fill_counts = {True: width * height, False: 0} for fill, expected_count in fill_counts.items(): - msg = 'fill={}'.format(fill) + msg = f"fill={fill}" mask = pygame.mask.Mask(expected_size, fill=fill) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertEqual(mask.count(), expected_count, msg) self.assertEqual(mask.get_size(), expected_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_mask__fill_kwarg_bit_boundaries(self): + """Ensures masks are created correctly using the fill keyword + over a range of sizes. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(1, 4): + for width in range(1, 66): + expected_count = width * height + expected_size = (width, height) + msg = f"size={expected_size}" + + mask = pygame.mask.Mask(expected_size, fill=True) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + def test_mask__fill_arg(self): """Ensure masks are created correctly using a fill arg.""" width, height = 59, 71 expected_size = (width, height) - fill_counts = {True : width * height, False : 0 } + fill_counts = {True: width * height, False: 0} for fill, expected_count in fill_counts.items(): - msg = 'fill={}'.format(fill) + msg = f"fill={fill}" mask = pygame.mask.Mask(expected_size, fill) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertEqual(mask.count(), expected_count, msg) self.assertEqual(mask.get_size(), expected_size, msg) @@ -100,19 +234,85 @@ class MaskTypeTest(unittest.TestCase): """Ensure masks are created correctly using the size keyword.""" width, height = 73, 83 expected_size = (width, height) - fill_counts = {True : width * height, False : 0 } + fill_counts = {True: width * height, False: 0} for fill, expected_count in fill_counts.items(): - msg = 'fill={}'.format(fill) + msg = f"fill={fill}" mask1 = pygame.mask.Mask(fill=fill, size=expected_size) mask2 = pygame.mask.Mask(size=expected_size, fill=fill) + self.assertIsInstance(mask1, pygame.mask.Mask, msg) + self.assertIsInstance(mask2, pygame.mask.Mask, msg) self.assertEqual(mask1.count(), expected_count, msg) self.assertEqual(mask2.count(), expected_count, msg) self.assertEqual(mask1.get_size(), expected_size, msg) self.assertEqual(mask2.get_size(), expected_size, msg) + def test_copy(self): + """Ensures copy works correctly with some bits set and unset.""" + # Test different widths and heights. + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height)) + + # Create a checkerboard pattern of set/unset bits. + for x in range(width): + for y in range(x & 1, height, 2): + mask.set_at((x, y)) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__full(self): + """Ensures copy works correctly on a filled masked.""" + # Test different widths and heights. + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__empty(self): + """Ensures copy works correctly on an empty mask.""" + for width in (31, 32, 33, 63, 64, 65): + for height in (31, 32, 33, 63, 64, 65): + mask = pygame.mask.Mask((width, height)) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_copy__independent(self): + """Ensures copy makes an independent copy of the mask.""" + mask_set_pos = (64, 1) + mask_copy_set_pos = (64, 2) + mask = pygame.mask.Mask((65, 3)) + + # Test both the copy() and __copy__() methods. + mask_copies = (mask.copy(), copy.copy(mask)) + mask.set_at(mask_set_pos) + + for mask_copy in mask_copies: + mask_copy.set_at(mask_copy_set_pos) + + self.assertIsNot(mask_copy, mask) + self.assertNotEqual( + mask_copy.get_at(mask_set_pos), mask.get_at(mask_set_pos) + ) + self.assertNotEqual( + mask_copy.get_at(mask_copy_set_pos), mask.get_at(mask_copy_set_pos) + ) + def test_get_size(self): """Ensure a mask's size is correctly retrieved.""" expected_size = (93, 101) @@ -120,6 +320,136 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask.get_size(), expected_size) + def test_get_rect(self): + """Ensures get_rect works correctly.""" + expected_rect = pygame.Rect((0, 0), (11, 13)) + + # Test on full and empty masks. + for fill in (True, False): + mask = pygame.mask.Mask(expected_rect.size, fill=fill) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_get_rect__one_kwarg(self): + """Ensures get_rect supports a single rect attribute kwarg. + + Tests all the rect attributes. + """ + # Rect attributes that take a single value. + RECT_SINGLE_VALUE_ATTRIBUTES = ( + "x", + "y", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + "width", + "height", + "w", + "h", + ) + + # Rect attributes that take 2 values. + RECT_DOUBLE_VALUE_ATTRIBUTES = ( + "topleft", + "bottomleft", + "topright", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "center", + "size", + ) + + # Testing ints/floats and tuples/lists/Vector2s. + # {attribute_names : attribute_values} + rect_attributes = { + RECT_SINGLE_VALUE_ATTRIBUTES: (3, 5.1), + RECT_DOUBLE_VALUE_ATTRIBUTES: ((1, 2.2), [2.3, 3], Vector2(0, 1)), + } + + size = (7, 3) + mask = pygame.mask.Mask(size) + + for attributes, values in rect_attributes.items(): + for attribute in attributes: + for value in values: + expected_rect = pygame.Rect((0, 0), size) + setattr(expected_rect, attribute, value) + + rect = mask.get_rect(**{attribute: value}) + + self.assertEqual(rect, expected_rect) + + def test_get_rect__multiple_kwargs(self): + """Ensures get_rect supports multiple rect attribute kwargs.""" + mask = pygame.mask.Mask((5, 4)) + expected_rect = pygame.Rect((0, 0), (0, 0)) + kwargs = {"x": 7.1, "top": -1, "size": Vector2(2, 3.2)} + + for attrib, value in kwargs.items(): + setattr(expected_rect, attrib, value) + + rect = mask.get_rect(**kwargs) + + self.assertEqual(rect, expected_rect) + + def test_get_rect__no_arg_support(self): + """Ensures get_rect only supports kwargs.""" + mask = pygame.mask.Mask((4, 5)) + + with self.assertRaises(TypeError): + rect = mask.get_rect(3) + + with self.assertRaises(TypeError): + rect = mask.get_rect((1, 2)) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_get_rect__invalid_kwarg_name(self): + """Ensures get_rect detects invalid kwargs.""" + mask = pygame.mask.Mask((1, 2)) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(righte=11) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(toplef=(1, 1)) + + with self.assertRaises(AttributeError): + rect = mask.get_rect(move=(3, 2)) + + def test_get_rect__invalid_kwarg_format(self): + """Ensures get_rect detects invalid kwarg formats.""" + mask = pygame.mask.Mask((3, 11)) + + with self.assertRaises(TypeError): + rect = mask.get_rect(right="1") # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(bottom=(1,)) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(centerx=(1, 1)) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(midleft=(1, "1")) # Wrong type. + + with self.assertRaises(TypeError): + rect = mask.get_rect(topright=(1,)) # Too few. + + with self.assertRaises(TypeError): + rect = mask.get_rect(bottomleft=(1, 2, 3)) # Too many. + + with self.assertRaises(TypeError): + rect = mask.get_rect(midbottom=1) # Wrong type. + def test_get_at(self): """Ensure individual mask bits are correctly retrieved.""" width, height = 5, 7 @@ -131,9 +461,9 @@ class MaskTypeTest(unittest.TestCase): # Check twice to make sure bits aren't toggled. self.assertEqual(mask0.get_at(pos), mask0_expected_bit) - self.assertEqual(mask0.get_at(pos), mask0_expected_bit) - self.assertEqual(mask1.get_at(pos), mask1_expected_bit) - self.assertEqual(mask1.get_at(pos), mask1_expected_bit) + self.assertEqual(mask0.get_at(pos=pos), mask0_expected_bit) + self.assertEqual(mask1.get_at(Vector2(pos)), mask1_expected_bit) + self.assertEqual(mask1.get_at(pos=Vector2(pos)), mask1_expected_bit) def test_get_at__out_of_bounds(self): """Ensure get_at() checks bounds.""" @@ -163,7 +493,7 @@ class MaskTypeTest(unittest.TestCase): pos = (width - 1, height - 1) mask0.set_at(pos, expected_bit) # set 0 to 1 - mask1.set_at(pos, expected_bit) # set 1 to 1 + mask1.set_at(pos=Vector2(pos), value=expected_bit) # set 1 to 1 self.assertEqual(mask0.get_at(pos), expected_bit) self.assertEqual(mask0.count(), mask0_expected_count) @@ -235,7 +565,7 @@ class MaskTypeTest(unittest.TestCase): expected_size = (4, 4) offset = (0, 0) expected_default = None - expected_overlaps = {(True, True) : offset} + expected_overlaps = {(True, True): offset} for fill2 in (True, False): mask2 = pygame.mask.Mask(expected_size, fill=fill2) @@ -243,7 +573,7 @@ class MaskTypeTest(unittest.TestCase): for fill1 in (True, False): key = (fill1, fill2) - msg = 'key={}'.format(key) + msg = f"key={key}" mask1 = pygame.mask.Mask(expected_size, fill=fill1) mask1_count = mask1.count() expected_pos = expected_overlaps.get(key, expected_default) @@ -268,10 +598,10 @@ class MaskTypeTest(unittest.TestCase): mask2_size = mask2.get_size() for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" expected_pos = (max(offset[0], 0), max(offset[1], 0)) - overlap_pos = mask1.overlap(mask2, offset) + overlap_pos = mask1.overlap(other=mask2, offset=offset) self.assertEqual(overlap_pos, expected_pos, msg) @@ -295,7 +625,7 @@ class MaskTypeTest(unittest.TestCase): mask2_size = mask2.get_size() for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" x, y = offset expected_y = max(y, 0) if 0 == y: @@ -305,7 +635,7 @@ class MaskTypeTest(unittest.TestCase): else: expected_x = max(x, 1) - overlap_pos = mask1.overlap(mask2, offset) + overlap_pos = mask1.overlap(mask2, Vector2(offset)) self.assertEqual(overlap_pos, (expected_x, expected_y), msg) @@ -332,7 +662,7 @@ class MaskTypeTest(unittest.TestCase): mask2_count = 1 for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" overlap_pos = mask1.overlap(mask2, offset) @@ -355,13 +685,15 @@ class MaskTypeTest(unittest.TestCase): mask2_size = mask2.get_size() # Check the 4 boundaries. - offsets = ((mask1_size[0], 0), # off right - (0, mask1_size[1]), # off bottom - (-mask2_size[0], 0), # off left - (0, -mask2_size[1])) # off top + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top for offset in offsets: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" overlap_pos = mask1.overlap(mask2, offset) @@ -373,6 +705,37 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), mask1_size, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap__bit_boundaries(self): + """Ensures overlap handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + expected_pos = (max(offset[0], 0), max(offset[1], 0)) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertEqual(overlap_pos, expected_pos, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_overlap__invalid_mask_arg(self): """Ensure overlap handles invalid mask arguments correctly.""" size = (5, 3) @@ -386,13 +749,14 @@ class MaskTypeTest(unittest.TestCase): def test_overlap__invalid_offset_arg(self): """Ensure overlap handles invalid offset arguments correctly.""" size = (2, 7) - offset = '(0, 0)' + offset = "(0, 0)" mask1 = pygame.mask.Mask(size) mask2 = pygame.mask.Mask(size) with self.assertRaises(TypeError): overlap_pos = mask1.overlap(mask2, offset) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_overlap_area(self): """Ensure the overlap_area is correctly calculated. @@ -405,7 +769,7 @@ class MaskTypeTest(unittest.TestCase): expected_size = width, height = (4, 4) offset = (0, 0) expected_default = 0 - expected_counts = {(True, True) : width * height} + expected_counts = {(True, True): width * height} for fill2 in (True, False): mask2 = pygame.mask.Mask(expected_size, fill=fill2) @@ -413,7 +777,7 @@ class MaskTypeTest(unittest.TestCase): for fill1 in (True, False): key = (fill1, fill2) - msg = 'key={}'.format(key) + msg = f"key={key}" mask1 = pygame.mask.Mask(expected_size, fill=fill1) mask1_count = mask1.count() expected_count = expected_counts.get(key, expected_default) @@ -428,6 +792,7 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), expected_size, msg) self.assertEqual(mask2.get_size(), expected_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_overlap_area__offset(self): """Ensure an offset overlap_area is correctly calculated.""" mask1 = pygame.mask.Mask((65, 3), fill=True) @@ -438,16 +803,16 @@ class MaskTypeTest(unittest.TestCase): mask2_size = mask2.get_size() # Using rects to help determine the overlapping area. - rect1 = pygame.Rect((0, 0), mask1_size) - rect2 = pygame.Rect((0, 0), mask2_size) + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" rect2.topleft = offset overlap_rect = rect1.clip(rect2) expected_count = overlap_rect.w * overlap_rect.h - overlap_count = mask1.overlap_area(mask2, offset) + overlap_count = mask1.overlap_area(other=mask2, offset=offset) self.assertEqual(overlap_count, expected_count, msg) @@ -468,15 +833,17 @@ class MaskTypeTest(unittest.TestCase): expected_count = 0 # Check the 4 boundaries. - offsets = ((mask1_size[0], 0), # off right - (0, mask1_size[1]), # off bottom - (-mask2_size[0], 0), # off left - (0, -mask2_size[1])) # off top + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top for offset in offsets: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" - overlap_count = mask1.overlap_area(mask2, offset) + overlap_count = mask1.overlap_area(mask2, Vector2(offset)) self.assertEqual(overlap_count, expected_count, msg) @@ -486,6 +853,42 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), mask1_size, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_area__bit_boundaries(self): + """Ensures overlap_area handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_overlap_count = overlap_rect.w * overlap_rect.h + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_overlap_count, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + def test_overlap_area__invalid_mask_arg(self): """Ensure overlap_area handles invalid mask arguments correctly.""" size = (3, 5) @@ -499,7 +902,7 @@ class MaskTypeTest(unittest.TestCase): def test_overlap_area__invalid_offset_arg(self): """Ensure overlap_area handles invalid offset arguments correctly.""" size = (7, 2) - offset = '(0, 0)' + offset = "(0, 0)" mask1 = pygame.mask.Mask(size) mask2 = pygame.mask.Mask(size) @@ -518,8 +921,7 @@ class MaskTypeTest(unittest.TestCase): expected_size = (4, 4) offset = (0, 0) expected_default = pygame.mask.Mask(expected_size) - expected_masks = { - (True, True) : pygame.mask.Mask(expected_size, fill=True)} + expected_masks = {(True, True): pygame.mask.Mask(expected_size, fill=True)} for fill2 in (True, False): mask2 = pygame.mask.Mask(expected_size, fill=fill2) @@ -527,14 +929,15 @@ class MaskTypeTest(unittest.TestCase): for fill1 in (True, False): key = (fill1, fill2) - msg = 'key={}'.format(key) + msg = f"key={key}" mask1 = pygame.mask.Mask(expected_size, fill=fill1) mask1_count = mask1.count() expected_mask = expected_masks.get(key, expected_default) - overlap_mask = mask1.overlap_mask(mask2, offset) + overlap_mask = mask1.overlap_mask(other=mask2, offset=offset) - self._assertMaskEqual(overlap_mask, expected_mask, msg) + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) # Ensure mask1/mask2 unchanged. self.assertEqual(mask1.count(), mask1_count, msg) @@ -542,6 +945,7 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), expected_size, msg) self.assertEqual(mask2.get_size(), expected_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_overlap_mask__bits_set(self): """Ensure overlap_mask's mask has correct bits set.""" mask1 = pygame.mask.Mask((50, 50), fill=True) @@ -555,13 +959,11 @@ class MaskTypeTest(unittest.TestCase): for i in range(50): for j in range(10): - self.assertEqual(mask3.get_at((i, j)), 1, - '({}, {})'.format(i, j)) + self.assertEqual(mask3.get_at((i, j)), 1, f"({i}, {j})") for i in range(50): for j in range(11, 50): - self.assertEqual(mask3.get_at((i, j)), 0, - '({}, {})'.format(i, j)) + self.assertEqual(mask3.get_at((i, j)), 0, f"({i}, {j})") # Ensure mask1/mask2 unchanged. self.assertEqual(mask1.count(), mask1_count) @@ -575,30 +977,70 @@ class MaskTypeTest(unittest.TestCase): mask2 = pygame.mask.Mask((66, 4), fill=True) mask1_count = mask1.count() mask2_count = mask2.count() - expected_size = mask1.get_size() + mask1_size = mask1.get_size() mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1_size) # Using rects to help determine the overlapping area. - rect1 = pygame.Rect((0, 0), expected_size) - rect2 = pygame.Rect((0, 0), mask2_size) + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" rect2.topleft = offset overlap_rect = rect1.clip(rect2) - expected_count = overlap_rect.w * overlap_rect.h + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) overlap_mask = mask1.overlap_mask(mask2, offset) - self.assertEqual(overlap_mask.count(), expected_count, msg) - self.assertEqual(overlap_mask.get_size(), expected_size, msg) + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) # Ensure mask1/mask2 unchanged. self.assertEqual(mask1.count(), mask1_count, msg) self.assertEqual(mask2.count(), mask2_count, msg) - self.assertEqual(mask1.get_size(), expected_size, msg) + self.assertEqual(mask1.get_size(), mask1_size, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_mask__specific_offsets(self): + """Ensure an offset overlap_mask's mask is correctly calculated. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling overlap_mask() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5), fill=True) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = f"offset={offset}" + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + def test_overlap_mask__offset_boundary(self): """Ensures overlap_mask handles offsets and boundaries correctly.""" mask1 = pygame.mask.Mask((9, 3), fill=True) @@ -611,16 +1053,19 @@ class MaskTypeTest(unittest.TestCase): expected_size = mask1_size # Check the 4 boundaries. - offsets = ((mask1_size[0], 0), # off right - (0, mask1_size[1]), # off bottom - (-mask2_size[0], 0), # off left - (0, -mask2_size[1])) # off top + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top for offset in offsets: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" overlap_mask = mask1.overlap_mask(mask2, offset) + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) self.assertEqual(overlap_mask.count(), expected_count, msg) self.assertEqual(overlap_mask.get_size(), expected_size, msg) @@ -630,6 +1075,47 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), mask1_size, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_overlap_mask__bit_boundaries(self): + """Ensures overlap_mask handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size, fill=True) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + expected_mask.draw( + pygame.Mask(overlap_rect.size, fill=True), overlap_rect.topleft + ) + + overlap_mask = mask1.overlap_mask(mask2, offset) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + assertMaskEqual(self, overlap_mask, expected_mask, msg) + + # Ensure mask1/mask2 unchanged. + self.assertEqual(mask1.count(), mask_count, msg) + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask1.get_size(), mask_size, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + def test_overlap_mask__invalid_mask_arg(self): """Ensure overlap_mask handles invalid mask arguments correctly.""" size = (3, 2) @@ -643,32 +1129,31 @@ class MaskTypeTest(unittest.TestCase): def test_overlap_mask__invalid_offset_arg(self): """Ensure overlap_mask handles invalid offset arguments correctly.""" size = (5, 2) - offset = '(0, 0)' + offset = "(0, 0)" mask1 = pygame.mask.Mask(size) mask2 = pygame.mask.Mask(size) with self.assertRaises(TypeError): overlap_mask = mask1.overlap_mask(mask2, offset) - def test_mask_access( self ): - """ do the set_at, and get_at parts work correctly? - """ - m = pygame.Mask((10,10)) - m.set_at((0,0), 1) - self.assertEqual(m.get_at((0,0)), 1) - m.set_at((9,0), 1) - self.assertEqual(m.get_at((9,0)), 1) + def test_mask_access(self): + """do the set_at, and get_at parts work correctly?""" + m = pygame.Mask((10, 10)) + m.set_at((0, 0), 1) + self.assertEqual(m.get_at((0, 0)), 1) + m.set_at((9, 0), 1) + self.assertEqual(m.get_at((9, 0)), 1) - #s = pygame.Surface((10,10)) - #s.set_at((1,0), (0, 0, 1, 255)) - #self.assertEqual(s.get_at((1,0)), (0, 0, 1, 255)) - #s.set_at((-1,0), (0, 0, 1, 255)) + # s = pygame.Surface((10,10)) + # s.set_at((1,0), (0, 0, 1, 255)) + # self.assertEqual(s.get_at((1,0)), (0, 0, 1, 255)) + # s.set_at((-1,0), (0, 0, 1, 255)) # out of bounds, should get IndexError - self.assertRaises(IndexError, lambda : m.get_at((-1,0)) ) - self.assertRaises(IndexError, lambda : m.set_at((-1,0), 1) ) - self.assertRaises(IndexError, lambda : m.set_at((10,0), 1) ) - self.assertRaises(IndexError, lambda : m.set_at((0,10), 1) ) + self.assertRaises(IndexError, lambda: m.get_at((-1, 0))) + self.assertRaises(IndexError, lambda: m.set_at((-1, 0), 1)) + self.assertRaises(IndexError, lambda: m.set_at((10, 0), 1)) + self.assertRaises(IndexError, lambda: m.set_at((0, 10), 1)) def test_fill(self): """Ensure a mask can be filled.""" @@ -682,6 +1167,25 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_fill__bit_boundaries(self): + """Ensures masks of different sizes are filled correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height)) + expected_count = width * height + + mask.fill() + + self.assertEqual( + mask.count(), expected_count, f"size=({width}, {height})" + ) + def test_clear(self): """Ensure a mask can be cleared.""" expected_count = 0 @@ -693,6 +1197,26 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) + def test_clear__bit_boundaries(self): + """Ensures masks of different sizes are cleared correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + expected_count = 0 + + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=True) + + mask.clear() + + self.assertEqual( + mask.count(), expected_count, f"size=({width}, {height})" + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_invert(self): """Ensure a mask can be inverted.""" side = 73 @@ -719,7 +1243,7 @@ class MaskTypeTest(unittest.TestCase): for i in range(side): pos = (i, i) - msg = 'pos={}'.format(pos) + msg = f"pos={pos}" self.assertEqual(mask1.get_at(pos), 0, msg) self.assertEqual(mask2.get_at(pos), 1, msg) @@ -747,6 +1271,29 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_invert__bit_boundaries(self): + """Ensures masks of different sizes are inverted correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for fill in (True, False): + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=fill) + expected_count = 0 if fill else width * height + + mask.invert() + + self.assertEqual( + mask.count(), + expected_count, + f"fill={fill}, size=({width}, {height})", + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_scale(self): """Ensure a mask can be scaled.""" width, height = 43, 61 @@ -762,18 +1309,17 @@ class MaskTypeTest(unittest.TestCase): for new_h in range(height - 10, height + 10): expected_size = (new_w, new_h) expected_count = new_w * new_h if fill else 0 - msg = 'size={}'.format(expected_size) + msg = f"size={expected_size}" - mask = original_mask.scale(expected_size) + mask = original_mask.scale(scale=expected_size) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertEqual(mask.count(), expected_count, msg) self.assertEqual(mask.get_size(), expected_size) # Ensure the original mask is unchanged. - self.assertEqual(original_mask.count(), original_count, - msg) - self.assertEqual(original_mask.get_size(), original_size, - msg) + self.assertEqual(original_mask.count(), original_count, msg) + self.assertEqual(original_mask.get_size(), original_size, msg) def test_scale__negative_size(self): """Ensure scale handles negative sizes correctly.""" @@ -783,7 +1329,7 @@ class MaskTypeTest(unittest.TestCase): mask.scale((-1, -1)) with self.assertRaises(ValueError): - mask.scale((-1, 10)) + mask.scale(Vector2(-1, 10)) with self.assertRaises(ValueError): mask.scale((10, -1)) @@ -800,7 +1346,7 @@ class MaskTypeTest(unittest.TestCase): expected_size = (4, 4) offset = (0, 0) expected_default = pygame.mask.Mask(expected_size, fill=True) - expected_masks = {(False, False) : pygame.mask.Mask(expected_size)} + expected_masks = {(False, False): pygame.mask.Mask(expected_size)} for fill2 in (True, False): mask2 = pygame.mask.Mask(expected_size, fill=fill2) @@ -808,13 +1354,13 @@ class MaskTypeTest(unittest.TestCase): for fill1 in (True, False): key = (fill1, fill2) - msg = 'key={}'.format(key) + msg = f"key={key}" mask1 = pygame.mask.Mask(expected_size, fill=fill1) expected_mask = expected_masks.get(key, expected_default) mask1.draw(mask2, offset) - self._assertMaskEqual(mask1, expected_mask, msg) + assertMaskEqual(self, mask1, expected_mask, msg) # Ensure mask2 unchanged. self.assertEqual(mask2.count(), mask2_count, msg) @@ -825,29 +1371,72 @@ class MaskTypeTest(unittest.TestCase): mask1 = pygame.mask.Mask((65, 3)) mask2 = pygame.mask.Mask((66, 4), fill=True) mask2_count = mask2.count() - mask1_size = mask1.get_size() mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1.get_size()) # Using rects to help determine the overlapping area. - rect1 = pygame.Rect((0, 0), mask1_size) - rect2 = pygame.Rect((0, 0), mask2_size) + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" rect2.topleft = offset overlap_rect = rect1.clip(rect2) - expected_count = overlap_rect.w * overlap_rect.h + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the draw() + # method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) mask1.clear() # Ensure it's empty for testing each offset. - mask1.draw(mask2, offset) + mask1.draw(other=mask2, offset=offset) - self.assertEqual(mask1.count(), expected_count, msg) - self.assertEqual(mask1.get_size(), mask1_size, msg) + assertMaskEqual(self, mask1, expected_mask, msg) # Ensure mask2 unchanged. self.assertEqual(mask2.count(), mask2_count, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + def test_draw__specific_offsets(self): + """Ensure an offset mask can be drawn onto another mask. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling draw() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5)) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = f"offset={offset}" + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the draw() + # method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for testing each offset. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + def test_draw__offset_boundary(self): """Ensures draw handles offsets and boundaries correctly.""" mask1 = pygame.mask.Mask((13, 5)) @@ -858,13 +1447,15 @@ class MaskTypeTest(unittest.TestCase): mask2_size = mask2.get_size() # Check the 4 boundaries. - offsets = ((mask1_size[0], 0), # off right - (0, mask1_size[1]), # off bottom - (-mask2_size[0], 0), # off left - (0, -mask2_size[1])) # off top + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top for offset in offsets: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" mask1.draw(mask2, offset) @@ -874,6 +1465,49 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), mask1_size, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_draw__bit_boundaries(self): + """Ensures draw handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.clear() + + # Normally draw() could be used to set these bits, but the + # draw() method is being tested here, so a loop is used + # instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y)) + mask1.clear() # Ensure it's empty for each test. + + mask1.draw(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + def test_draw__invalid_mask_arg(self): """Ensure draw handles invalid mask arguments correctly.""" size = (7, 3) @@ -887,7 +1521,7 @@ class MaskTypeTest(unittest.TestCase): def test_draw__invalid_offset_arg(self): """Ensure draw handles invalid offset arguments correctly.""" size = (5, 7) - offset = '(0, 0)' + offset = "(0, 0)" mask1 = pygame.mask.Mask(size) mask2 = pygame.mask.Mask(size) @@ -906,8 +1540,7 @@ class MaskTypeTest(unittest.TestCase): expected_size = (4, 4) offset = (0, 0) expected_default = pygame.mask.Mask(expected_size) - expected_masks = { - (True, False) : pygame.mask.Mask(expected_size, fill=True)} + expected_masks = {(True, False): pygame.mask.Mask(expected_size, fill=True)} for fill2 in (True, False): mask2 = pygame.mask.Mask(expected_size, fill=fill2) @@ -915,13 +1548,13 @@ class MaskTypeTest(unittest.TestCase): for fill1 in (True, False): key = (fill1, fill2) - msg = 'key={}'.format(key) + msg = f"key={key}" mask1 = pygame.mask.Mask(expected_size, fill=fill1) expected_mask = expected_masks.get(key, expected_default) mask1.erase(mask2, offset) - self._assertMaskEqual(mask1, expected_mask, msg) + assertMaskEqual(self, mask1, expected_mask, msg) # Ensure mask2 unchanged. self.assertEqual(mask2.count(), mask2_count, msg) @@ -932,30 +1565,72 @@ class MaskTypeTest(unittest.TestCase): mask1 = pygame.mask.Mask((65, 3)) mask2 = pygame.mask.Mask((66, 4), fill=True) mask2_count = mask2.count() - mask1_size = mask1.get_size() mask2_size = mask2.get_size() + expected_mask = pygame.Mask(mask1.get_size()) # Using rects to help determine the overlapping area. - rect1 = pygame.Rect((0, 0), mask1_size) - rect2 = pygame.Rect((0, 0), mask2_size) - rect1_area = rect1.w * rect1.h + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() for offset in self.ORIGIN_OFFSETS: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" rect2.topleft = offset overlap_rect = rect1.clip(rect2) - expected_count = rect1_area - (overlap_rect.w * overlap_rect.h) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but the + # erase() method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) mask1.fill() # Ensure it's filled for testing each offset. - mask1.erase(mask2, offset) + mask1.erase(other=mask2, offset=offset) - self.assertEqual(mask1.count(), expected_count, msg) - self.assertEqual(mask1.get_size(), mask1_size, msg) + assertMaskEqual(self, mask1, expected_mask, msg) # Ensure mask2 unchanged. self.assertEqual(mask2.count(), mask2_count, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + def test_erase__specific_offsets(self): + """Ensure an offset mask can erase another mask. + + Testing the specific case of: + -both masks are wider than 32 bits + -a positive offset is used + -the mask calling erase() is wider than the mask passed in + """ + mask1 = pygame.mask.Mask((65, 5)) + mask2 = pygame.mask.Mask((33, 3), fill=True) + expected_mask = pygame.Mask(mask1.get_size()) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # This rect's corners are used to move rect2 around the inside of + # rect1. + corner_rect = rect1.inflate(-2, -2) + + for corner in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(rect2, corner, getattr(corner_rect, corner)) + offset = rect2.topleft + msg = f"offset={offset}" + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but the + # erase() method is being tested here, so a loop is used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for testing each offset. + + mask1.erase(mask2, Vector2(offset)) + + assertMaskEqual(self, mask1, expected_mask, msg) + def test_erase__offset_boundary(self): """Ensures erase handles offsets and boundaries correctly.""" mask1 = pygame.mask.Mask((7, 11), fill=True) @@ -966,13 +1641,15 @@ class MaskTypeTest(unittest.TestCase): mask2_size = mask2.get_size() # Check the 4 boundaries. - offsets = ((mask1_size[0], 0), # off right - (0, mask1_size[1]), # off bottom - (-mask2_size[0], 0), # off left - (0, -mask2_size[1])) # off top + offsets = ( + (mask1_size[0], 0), # off right + (0, mask1_size[1]), # off bottom + (-mask2_size[0], 0), # off left + (0, -mask2_size[1]), + ) # off top for offset in offsets: - msg = 'offset={}'.format(offset) + msg = f"offset={offset}" mask1.erase(mask2, offset) @@ -982,6 +1659,49 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask1.get_size(), mask1_size, msg) self.assertEqual(mask2.get_size(), mask2_size, msg) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_erase__bit_boundaries(self): + """Ensures erase handles masks of different sizes correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for height in range(2, 4): + for width in range(2, 66): + mask_size = (width, height) + mask_count = width * height + mask1 = pygame.mask.Mask(mask_size) + mask2 = pygame.mask.Mask(mask_size, fill=True) + expected_mask = pygame.Mask(mask_size) + + # Using rects to help determine the overlapping area. + rect1 = mask1.get_rect() + rect2 = mask2.get_rect() + + # Testing masks offset from each other. + for offset in self.ORIGIN_OFFSETS: + msg = f"size={mask_size}, offset={offset}" + rect2.topleft = offset + overlap_rect = rect1.clip(rect2) + expected_mask.fill() + + # Normally erase() could be used to clear these bits, but + # the erase() method is being tested here, so a loop is + # used instead. + for x in range(overlap_rect.left, overlap_rect.right): + for y in range(overlap_rect.top, overlap_rect.bottom): + expected_mask.set_at((x, y), 0) + mask1.fill() # Ensure it's filled for each test. + + mask1.erase(mask2, offset) + + assertMaskEqual(self, mask1, expected_mask, msg) + + # Ensure mask2 unchanged. + self.assertEqual(mask2.count(), mask_count, msg) + self.assertEqual(mask2.get_size(), mask_size, msg) + def test_erase__invalid_mask_arg(self): """Ensure erase handles invalid mask arguments correctly.""" size = (3, 7) @@ -995,7 +1715,7 @@ class MaskTypeTest(unittest.TestCase): def test_erase__invalid_offset_arg(self): """Ensure erase handles invalid offset arguments correctly.""" size = (7, 5) - offset = '(0, 0)' + offset = "(0, 0)" mask1 = pygame.mask.Mask(size) mask2 = pygame.mask.Mask(size) @@ -1018,6 +1738,37 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(count, expected_count) self.assertEqual(mask.get_size(), expected_size) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_count__bit_boundaries(self): + """Ensures the set bits of different sized masks are counted correctly. + + Tests masks of different sizes, including: + -masks 31 to 33 bits wide (32 bit boundaries) + -masks 63 to 65 bits wide (64 bit boundaries) + """ + for fill in (True, False): + for height in range(1, 4): + for width in range(1, 66): + mask = pygame.mask.Mask((width, height), fill=fill) + expected_count = width * height if fill else 0 + + # Test toggling each bit. + for pos in ((x, y) for y in range(height) for x in range(width)): + if fill: + mask.set_at(pos, 0) + expected_count -= 1 + else: + mask.set_at(pos, 1) + expected_count += 1 + + count = mask.count() + + self.assertEqual( + count, + expected_count, + f"fill={fill}, size=({width}, {height}), pos={pos}", + ) + def test_count__full_mask(self): """Ensure a full mask's set bits are correctly counted.""" width, height = 17, 97 @@ -1041,9 +1792,14 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(count, expected_count) self.assertEqual(mask.get_size(), expected_size) - def todo_test_centroid(self): - """Ensure a mask's centroid is correctly calculated.""" - self.fail() + def test_centroid(self): + """Ensure a filled mask's centroid is correctly calculated.""" + mask = pygame.mask.Mask((5, 7), fill=True) + expected_centroid = mask.get_rect().center + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) def test_centroid__empty_mask(self): """Ensure an empty mask's centroid is correctly calculated.""" @@ -1056,9 +1812,126 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(centroid, expected_centroid) self.assertEqual(mask.get_size(), expected_size) - def todo_test_angle(self): + def test_centroid__single_row(self): + """Ensure a mask's centroid is correctly calculated + when setting points along a single row.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + for y in range(height): + mask.clear() # Clear for each row. + + for x in range(width): + mask.set_at((x, y)) + expected_centroid = (x // 2, y) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_rows(self): + """Ensure a mask's centroid is correctly calculated + when setting points along two rows.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + # The first row is tested with each of the other rows. + for y in range(1, height): + mask.clear() # Clear for each set of rows. + + for x in range(width): + mask.set_at((x, 0)) + mask.set_at((x, y)) + expected_centroid = (x // 2, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__single_column(self): + """Ensure a mask's centroid is correctly calculated + when setting points along a single column.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + for x in range(width): + mask.clear() # Clear for each column. + + for y in range(height): + mask.set_at((x, y)) + expected_centroid = (x, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_columns(self): + """Ensure a mask's centroid is correctly calculated + when setting points along two columns.""" + width, height = (5, 7) + mask = pygame.mask.Mask((width, height)) + + # The first column is tested with each of the other columns. + for x in range(1, width): + mask.clear() # Clear for each set of columns. + + for y in range(height): + mask.set_at((0, y)) + mask.set_at((x, y)) + expected_centroid = (x // 2, y // 2) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__all_corners(self): + """Ensure a mask's centroid is correctly calculated + when its corners are set.""" + mask = pygame.mask.Mask((5, 7)) + expected_centroid = mask.get_rect().center + + for corner in corners(mask): + mask.set_at(corner) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_centroid__two_corners(self): + """Ensure a mask's centroid is correctly calculated + when only two corners are set.""" + mask = pygame.mask.Mask((5, 7)) + mask_rect = mask.get_rect() + mask_corners = corners(mask) + + for i, corner1 in enumerate(mask_corners): + for corner2 in mask_corners[i + 1 :]: + mask.clear() # Clear for each pair of corners. + mask.set_at(corner1) + mask.set_at(corner2) + + if corner1[0] == corner2[0]: + expected_centroid = (corner1[0], abs(corner1[1] - corner2[1]) // 2) + elif corner1[1] == corner2[1]: + expected_centroid = (abs(corner1[0] - corner2[0]) // 2, corner1[1]) + else: + expected_centroid = mask_rect.center + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_angle(self): """Ensure a mask's orientation angle is correctly calculated.""" - self.fail() + expected_angle = -45.0 + expected_size = (100, 100) + surface = pygame.Surface(expected_size) + mask = pygame.mask.from_surface(surface) + + angle = mask.angle() # Returns the orientation of the pixels + + self.assertIsInstance(angle, float) + self.assertEqual(angle, expected_angle) def test_angle__empty_mask(self): """Ensure an empty mask's angle is correctly calculated.""" @@ -1073,105 +1946,126 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(mask.get_size(), expected_size) def test_drawing(self): - """ Test fill, clear, invert, draw, erase - """ - m = pygame.Mask((100,100)) + """Test fill, clear, invert, draw, erase""" + m = pygame.Mask((100, 100)) self.assertEqual(m.count(), 0) m.fill() self.assertEqual(m.count(), 10000) m2 = pygame.Mask((10, 10), fill=True) - m.erase(m2, (50,50)) + m.erase(m2, (50, 50)) self.assertEqual(m.count(), 9900) m.invert() self.assertEqual(m.count(), 100) - m.draw(m2, (0,0)) + m.draw(m2, (0, 0)) self.assertEqual(m.count(), 200) m.clear() self.assertEqual(m.count(), 0) def test_outline(self): - """ - """ + """ """ - m = pygame.Mask((20,20)) + m = pygame.Mask((20, 20)) self.assertEqual(m.outline(), []) - m.set_at((10,10), 1) - self.assertEqual(m.outline(), [(10,10)]) + m.set_at((10, 10), 1) + self.assertEqual(m.outline(), [(10, 10)]) - m.set_at((10,12), 1) - self.assertEqual(m.outline(10), [(10,10)]) + m.set_at((10, 12), 1) + self.assertEqual(m.outline(10), [(10, 10)]) - m.set_at((11,11), 1) - self.assertEqual(m.outline(), [(10,10), (11,11), (10,12), (11,11), (10,10)]) - self.assertEqual(m.outline(2), [(10,10), (10,12), (10,10)]) + m.set_at((11, 11), 1) + self.assertEqual( + m.outline(), [(10, 10), (11, 11), (10, 12), (11, 11), (10, 10)] + ) + self.assertEqual(m.outline(every=2), [(10, 10), (10, 12), (10, 10)]) - #TODO: Test more corner case outlines. + # TODO: Test more corner case outlines. def test_convolve__size(self): - sizes = [(1,1), (31,31), (32,32), (100,100)] + sizes = [(1, 1), (31, 31), (32, 32), (100, 100)] for s1 in sizes: m1 = pygame.Mask(s1) for s2 in sizes: m2 = pygame.Mask(s2) o = m1.convolve(m2) - for i in (0,1): - self.assertEqual(o.get_size()[i], - m1.get_size()[i] + m2.get_size()[i] - 1) + + self.assertIsInstance(o, pygame.mask.Mask) + + for i in (0, 1): + self.assertEqual( + o.get_size()[i], m1.get_size()[i] + m2.get_size()[i] - 1 + ) def test_convolve__point_identities(self): """Convolving with a single point is the identity, while convolving a point with something flips it.""" - m = random_mask((100,100)) - k = pygame.Mask((1,1)) - k.set_at((0,0)) + m = random_mask((100, 100)) + k = pygame.Mask((1, 1)) + k.set_at((0, 0)) + + convolve_mask = m.convolve(k) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + assertMaskEqual(self, m, convolve_mask) - self._assertMaskEqual(m, m.convolve(k)) - self._assertMaskEqual(m, k.convolve(k.convolve(m))) + convolve_mask = k.convolve(k.convolve(m)) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + assertMaskEqual(self, m, convolve_mask) def test_convolve__with_output(self): """checks that convolution modifies only the correct portion of the output""" - m = random_mask((10,10)) - k = pygame.Mask((2,2)) - k.set_at((0,0)) + m = random_mask((10, 10)) + k = pygame.Mask((2, 2)) + k.set_at((0, 0)) + + o = pygame.Mask((50, 50)) + test = pygame.Mask((50, 50)) - o = pygame.Mask((50,50)) - test = pygame.Mask((50,50)) + m.convolve(k, o) + test.draw(m, (1, 1)) - m.convolve(k,o) - test.draw(m,(1,1)) - self._assertMaskEqual(o, test) + self.assertIsInstance(o, pygame.mask.Mask) + assertMaskEqual(self, o, test) o.clear() test.clear() - m.convolve(k,o, (10,10)) - test.draw(m,(11,11)) - self._assertMaskEqual(o, test) + m.convolve(other=k, output=o, offset=Vector2(10, 10)) + test.draw(m, (11, 11)) + + self.assertIsInstance(o, pygame.mask.Mask) + assertMaskEqual(self, o, test) def test_convolve__out_of_range(self): full = pygame.Mask((2, 2), fill=True) + # Tuple of points (out of range) and the expected count for each. + pts_data = (((0, 3), 0), ((0, 2), 3), ((-2, -2), 1), ((-3, -3), 0)) + + for pt, expected_count in pts_data: + convolve_mask = full.convolve(full, None, pt) - self.assertEqual(full.convolve(full, None, ( 0, 3)).count(), 0) - self.assertEqual(full.convolve(full, None, ( 0, 2)).count(), 3) - self.assertEqual(full.convolve(full, None, (-2, -2)).count(), 1) - self.assertEqual(full.convolve(full, None, (-3, -3)).count(), 0) + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertEqual(convolve_mask.count(), expected_count) + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") def test_convolve(self): """Tests the definition of convolution""" - m1 = random_mask((100,100)) - m2 = random_mask((100,100)) + m1 = random_mask((100, 100)) + m2 = random_mask((100, 100)) conv = m1.convolve(m2) + self.assertIsInstance(conv, pygame.mask.Mask) for i in range(conv.get_size()[0]): for j in range(conv.get_size()[1]): - self.assertEqual(conv.get_at((i,j)) == 0, - m1.overlap(m2, (i - 99, j - 99)) is None) + self.assertEqual( + conv.get_at((i, j)) == 0, m1.overlap(m2, (i - 99, j - 99)) is None + ) def _draw_component_pattern_box(self, mask, size, pos, inverse=False): # Helper method to create/draw a 'box' pattern for testing. @@ -1201,7 +2095,7 @@ class MaskTypeTest(unittest.TestCase): ymax = size - 1 for y in range(size): for x in range(size): - if x == y or x == ymax - y: + if x in [y, ymax - y]: pattern.set_at((x, y)) if inverse: @@ -1248,8 +2142,7 @@ class MaskTypeTest(unittest.TestCase): size = 4 offset = (width - size, 0) - pattern = self._draw_component_pattern_plus(original_mask, size, - offset) + pattern = self._draw_component_pattern_plus(original_mask, size, offset) patterns.append((pattern, offset)) # Make this one the largest connected component. @@ -1263,28 +2156,33 @@ class MaskTypeTest(unittest.TestCase): mask = original_mask.connected_component() + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) - self.assertEqual(mask.overlap_area(expected_pattern, expected_offset), - expected_count) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) # Ensure the original mask is unchanged. self.assertEqual(original_mask.count(), original_count) self.assertEqual(original_mask.get_size(), expected_size) for pattern, offset in patterns: - self.assertEqual(original_mask.overlap_area(pattern, offset), - pattern.count()) + self.assertEqual( + original_mask.overlap_area(pattern, offset), pattern.count() + ) def test_connected_component__full_mask(self): """Ensure a mask's connected component is correctly calculated - when the mask is full.""" + when the mask is full. + """ expected_size = (23, 31) original_mask = pygame.mask.Mask(expected_size, fill=True) expected_count = original_mask.count() mask = original_mask.connected_component() + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) @@ -1294,7 +2192,8 @@ class MaskTypeTest(unittest.TestCase): def test_connected_component__empty_mask(self): """Ensure a mask's connected component is correctly calculated - when the mask is empty.""" + when the mask is empty. + """ expected_size = (37, 43) original_mask = pygame.mask.Mask(expected_size) original_count = original_mask.count() @@ -1302,6 +2201,7 @@ class MaskTypeTest(unittest.TestCase): mask = original_mask.connected_component() + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) @@ -1321,23 +2221,28 @@ class MaskTypeTest(unittest.TestCase): expected_offset = (xset - 1, yset - 1) # This isolates the bit at set_pos from all the other bits. - expected_pattern = self._draw_component_pattern_box(original_mask, 3, - expected_offset, inverse=True) + expected_pattern = self._draw_component_pattern_box( + original_mask, 3, expected_offset, inverse=True + ) expected_count = 1 original_count = original_mask.count() mask = original_mask.connected_component(set_pos) + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) - self.assertEqual(mask.overlap_area(expected_pattern, expected_offset), - expected_count) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) # Ensure the original mask is unchanged. self.assertEqual(original_mask.count(), original_count) self.assertEqual(original_mask.get_size(), expected_size) - self.assertEqual(original_mask.overlap_area( - expected_pattern, expected_offset), expected_count) + self.assertEqual( + original_mask.overlap_area(expected_pattern, expected_offset), + expected_count, + ) def test_connected_component__multi_set_bits(self): """Ensure a mask's connected component is correctly calculated @@ -1354,7 +2259,7 @@ class MaskTypeTest(unittest.TestCase): # in the resulting pattern for this to work properly. for y in range(3, p_height): for x in range(1, p_width): - if x == y or x == y - 3 or x == p_width - 4: + if x in [y, y - 3, p_width - 4]: expected_pattern.set_at((x, y), 0) expected_count = expected_pattern.count() @@ -1362,16 +2267,20 @@ class MaskTypeTest(unittest.TestCase): mask = original_mask.connected_component(set_pos) + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) - self.assertEqual(mask.overlap_area(expected_pattern, expected_offset), - expected_count) + self.assertEqual( + mask.overlap_area(expected_pattern, expected_offset), expected_count + ) # Ensure the original mask is unchanged. self.assertEqual(original_mask.count(), expected_count) self.assertEqual(original_mask.get_size(), expected_size) - self.assertEqual(original_mask.overlap_area( - expected_pattern, expected_offset), expected_count) + self.assertEqual( + original_mask.overlap_area(expected_pattern, expected_offset), + expected_count, + ) def test_connected_component__unset_bit(self): """Ensure a mask's connected component is correctly calculated @@ -1387,6 +2296,7 @@ class MaskTypeTest(unittest.TestCase): mask = original_mask.connected_component(unset_pos) + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), expected_count) self.assertEqual(mask.get_size(), expected_size) @@ -1411,26 +2321,27 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(original_mask.get_size(), original_size) def test_connected_components(self): - """ - """ + """ """ + m = pygame.Mask((10, 10)) - m = pygame.Mask((10,10)) - self.assertEqual(repr(m.connected_components()), "[]") + self.assertListEqual(m.connected_components(), []) comp = m.connected_component() + self.assertEqual(m.count(), comp.count()) - m.set_at((0,0), 1) - m.set_at((1,1), 1) + m.set_at((0, 0), 1) + m.set_at((1, 1), 1) comp = m.connected_component() comps = m.connected_components() comps1 = m.connected_components(1) comps2 = m.connected_components(2) comps3 = m.connected_components(3) + self.assertEqual(comp.count(), comps[0].count()) self.assertEqual(comps1[0].count(), 2) self.assertEqual(comps2[0].count(), 2) - self.assertEqual(repr(comps3), "[]") + self.assertListEqual(comps3, []) m.set_at((9, 9), 1) comp = m.connected_component() @@ -1438,8 +2349,9 @@ class MaskTypeTest(unittest.TestCase): comp2 = m.connected_component((2, 2)) comps = m.connected_components() comps1 = m.connected_components(1) - comps2 = m.connected_components(2) + comps2 = m.connected_components(minimum=2) comps3 = m.connected_components(3) + self.assertEqual(comp.count(), 2) self.assertEqual(comp1.count(), 2) self.assertEqual(comp2.count(), 0) @@ -1448,122 +2360,2995 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(len(comps2), 1) self.assertEqual(len(comps3), 0) - def test_get_bounding_rects(self): - """ + for mask in comps: + self.assertIsInstance(mask, pygame.mask.Mask) + + def test_connected_components__negative_min_with_empty_mask(self): + """Ensures connected_components() properly handles negative min values + when the mask is empty. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. """ + expected_comps = [] + mask_count = 0 + mask_size = (65, 13) + mask = pygame.mask.Mask(mask_size) - m = pygame.Mask((10,10)) - m.set_at((0,0), 1) - m.set_at((1,0), 1) + connected_comps = mask.connected_components(-1) - m.set_at((0,1), 1) + self.assertListEqual(connected_comps, expected_comps) - m.set_at((0,3), 1) - m.set_at((3,3), 1) + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) - r = m.get_bounding_rects() + def test_connected_components__negative_min_with_full_mask(self): + """Ensures connected_components() properly handles negative min values + when the mask is full. - self.assertEqual( - repr(r), - "[, , ]") - - #1100 - #1111 - m = pygame.Mask((4,2)) - m.set_at((0,0), 1) - m.set_at((1,0), 1) - m.set_at((2,0), 0) - m.set_at((3,0), 0) - - m.set_at((0,1), 1) - m.set_at((1,1), 1) - m.set_at((2,1), 1) - m.set_at((3,1), 1) - - r = m.get_bounding_rects() - self.assertEqual(repr(r), "[]") - - #00100 - #01110 - #00100 - m = pygame.Mask((5,3)) - m.set_at((0,0), 0) - m.set_at((1,0), 0) - m.set_at((2,0), 1) - m.set_at((3,0), 0) - m.set_at((4,0), 0) - - m.set_at((0,1), 0) - m.set_at((1,1), 1) - m.set_at((2,1), 1) - m.set_at((3,1), 1) - m.set_at((4,1), 0) - - m.set_at((0,2), 0) - m.set_at((1,2), 0) - m.set_at((2,2), 1) - m.set_at((3,2), 0) - m.set_at((4,2), 0) - - r = m.get_bounding_rects() - self.assertEqual(repr(r), "[]") - - #00010 - #00100 - #01000 - m = pygame.Mask((5,3)) - m.set_at((0,0), 0) - m.set_at((1,0), 0) - m.set_at((2,0), 0) - m.set_at((3,0), 1) - m.set_at((4,0), 0) - - m.set_at((0,1), 0) - m.set_at((1,1), 0) - m.set_at((2,1), 1) - m.set_at((3,1), 0) - m.set_at((4,1), 0) - - m.set_at((0,2), 0) - m.set_at((1,2), 1) - m.set_at((2,2), 0) - m.set_at((3,2), 0) - m.set_at((4,2), 0) - - r = m.get_bounding_rects() - self.assertEqual(repr(r), "[]") - - #00011 - #11111 - m = pygame.Mask((5,2)) - m.set_at((0,0), 0) - m.set_at((1,0), 0) - m.set_at((2,0), 0) - m.set_at((3,0), 1) - m.set_at((4,0), 1) - - m.set_at((0,1), 1) - m.set_at((1,1), 1) - m.set_at((2,1), 1) - m.set_at((3,1), 1) - m.set_at((3,1), 1) - - r = m.get_bounding_rects() - #TODO: this should really make one bounding rect. - #self.assertEqual(repr(r), "[]") + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + mask_size = (64, 11) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_count = mask.count() + expected_len = 1 - def test_zero_mask(self): - mask = pygame.mask.Mask((0, 0)) - self.assertEqual(mask.get_size(), (0, 0)) + connected_comps = mask.connected_components(-2) - mask = pygame.mask.Mask((100, 0)) - self.assertEqual(mask.get_size(), (100, 0)) + self.assertEqual(len(connected_comps), expected_len) + assertMaskEqual(self, connected_comps[0], mask) - mask = pygame.mask.Mask((0, 100)) - self.assertEqual(mask.get_size(), (0, 100)) + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) - def test_zero_mask_get_size(self): - """Ensures get_size correctly handles zero sized masks.""" + def test_connected_components__negative_min_with_some_bits_set(self): + """Ensures connected_components() properly handles negative min values + when the mask has some bits set. + + Negative and zero values for the min parameter (minimum number of bits + per connected component) equate to setting it to one. + """ + mask_size = (64, 12) + mask = pygame.mask.Mask(mask_size) + expected_comps = {} + + # Set the corners and the center positions. A new expected component + # mask is created for each point. + for corner in corners(mask): + mask.set_at(corner) + + new_mask = pygame.mask.Mask(mask_size) + new_mask.set_at(corner) + expected_comps[corner] = new_mask + + center = (mask_size[0] // 2, mask_size[1] // 2) + mask.set_at(center) + + new_mask = pygame.mask.Mask(mask_size) + new_mask.set_at(center) + expected_comps[center] = new_mask + mask_count = mask.count() + + connected_comps = mask.connected_components(-3) + + self.assertEqual(len(connected_comps), len(expected_comps)) + + for comp in connected_comps: + # Since the masks in the connected component list can be in any + # order, loop the expected components to find its match. + found = False + + for pt in tuple(expected_comps.keys()): + if comp.get_at(pt): + found = True + assertMaskEqual(self, comp, expected_comps[pt]) + del expected_comps[pt] # Entry removed so it isn't reused. + break + + self.assertTrue(found, f"missing component for pt={pt}") + + # Ensure the original mask is unchanged. + self.assertEqual(mask.count(), mask_count) + self.assertEqual(mask.get_size(), mask_size) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_get_bounding_rects(self): + """Ensures get_bounding_rects works correctly.""" + # Create masks with different set point groups. Each group of + # connected set points will be contained in its own bounding rect. + # Diagonal points are considered connected. + mask_data = [] # [((size), ((rect1_pts), ...)), ...] + + # Mask 1: + # |0123456789 + # -+---------- + # 0|1100000000 + # 1|1000000000 + # 2|0000000000 + # 3|1001000000 + # 4|0000000000 + # 5|0000000000 + # 6|0000000000 + # 7|0000000000 + # 8|0000000000 + # 9|0000000000 + mask_data.append( + ( + (10, 10), # size + # Points to set for the 3 bounding rects. + (((0, 0), (1, 0), (0, 1)), ((0, 3),), ((3, 3),)), # rect1 # rect2 + ) + ) # rect3 + + # Mask 2: + # |0123 + # -+---- + # 0|1100 + # 1|1111 + mask_data.append( + ( + (4, 2), # size + # Points to set for the 1 bounding rect. + (((0, 0), (1, 0), (0, 1), (1, 1), (2, 1), (3, 1)),), + ) + ) + + # Mask 3: + # |01234 + # -+----- + # 0|00100 + # 1|01110 + # 2|00100 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 1 bounding rect. + (((2, 0), (1, 1), (2, 1), (3, 1), (2, 2)),), + ) + ) + + # Mask 4: + # |01234 + # -+----- + # 0|00010 + # 1|00100 + # 2|01000 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 1 bounding rect. + (((3, 0), (2, 1), (1, 2)),), + ) + ) + + # Mask 5: + # |01234 + # -+----- + # 0|00011 + # 1|11111 + mask_data.append( + ( + (5, 2), # size + # Points to set for the 1 bounding rect. + (((3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1)),), + ) + ) + + # Mask 6: + # |01234 + # -+----- + # 0|10001 + # 1|00100 + # 2|10001 + mask_data.append( + ( + (5, 3), # size + # Points to set for the 5 bounding rects. + ( + ((0, 0),), # rect1 + ((4, 0),), # rect2 + ((2, 1),), # rect3 + ((0, 2),), # rect4 + ((4, 2),), + ), + ) + ) # rect5 + + for size, rect_point_tuples in mask_data: + rects = [] + mask = pygame.Mask(size) + + for rect_points in rect_point_tuples: + rects.append(create_bounding_rect(rect_points)) + for pt in rect_points: + mask.set_at(pt) + + expected_rects = sorted(rects, key=tuple) + + rects = mask.get_bounding_rects() + + self.assertListEqual( + sorted(mask.get_bounding_rects(), key=tuple), + expected_rects, + f"size={size}", + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface(self): + """Ensures empty and full masks can be drawn onto surfaces.""" + expected_ref_count = 3 + size = (33, 65) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + test_fills = ((pygame.Color("white"), True), (pygame.Color("black"), False)) + + for expected_color, fill in test_fills: + surface.fill(surface_color) + mask = pygame.mask.Mask(size, fill=fill) + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__create_surface(self): + """Ensures empty and full masks can be drawn onto a created surface.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + size = (33, 65) + test_fills = ((pygame.Color("white"), True), (pygame.Color("black"), False)) + + for expected_color, fill in test_fills: + mask = pygame.mask.Mask(size, fill=fill) + + for use_arg in (True, False): + if use_arg: + to_surface = mask.to_surface(None) + else: + to_surface = mask.to_surface() + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_param(self): + """Ensures to_surface accepts a surface arg/kwarg.""" + expected_ref_count = 4 + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + kwargs = {"surface": surface} + + for use_kwargs in (True, False): + surface.fill(surface_color) + + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(kwargs["surface"]) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_param(self): + """Ensures to_surface accepts a setsurface arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + setsurface = pygame.Surface(size, expected_flag, expected_depth) + setsurface.fill(expected_color) + kwargs = {"setsurface": setsurface} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, kwargs["setsurface"]) + + self.assertIsInstance(to_surface, pygame.Surface) + + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_param(self): + """Ensures to_surface accepts a unsetsurface arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size) + unsetsurface = pygame.Surface(size, expected_flag, expected_depth) + unsetsurface.fill(expected_color) + kwargs = {"unsetsurface": unsetsurface} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, None, kwargs["unsetsurface"]) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setcolor_param(self): + """Ensures to_surface accepts a setcolor arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + kwargs = {"setcolor": expected_color} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface(None, None, None, kwargs["setcolor"]) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setcolor_default(self): + """Ensures the default setcolor is correct.""" + expected_color = pygame.Color("white") + size = (3, 7) + mask = pygame.mask.Mask(size, fill=True) + + to_surface = mask.to_surface( + surface=None, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetcolor_param(self): + """Ensures to_surface accepts a unsetcolor arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("red") + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"unsetcolor": expected_color} + + for use_kwargs in (True, False): + if use_kwargs: + to_surface = mask.to_surface(**kwargs) + else: + to_surface = mask.to_surface( + None, None, None, None, kwargs["unsetcolor"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetcolor_default(self): + """Ensures the default unsetcolor is correct.""" + expected_color = pygame.Color("black") + size = (3, 7) + mask = pygame.mask.Mask(size) + + to_surface = mask.to_surface( + surface=None, setsurface=None, unsetsurface=None, setcolor=None + ) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__dest_param(self): + """Ensures to_surface accepts a dest arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + default_surface_color = (0, 0, 0, 0) + default_unsetcolor = pygame.Color("black") + dest = (0, 0) + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"dest": dest} + + for use_kwargs in (True, False): + if use_kwargs: + expected_color = default_unsetcolor + + to_surface = mask.to_surface(**kwargs) + else: + expected_color = default_surface_color + + to_surface = mask.to_surface( + None, None, None, None, None, kwargs["dest"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__dest_default(self): + """Ensures the default dest is correct.""" + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + + mask_size = (3, 2) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + + # Make the surface bigger than the mask. + surf_size = (mask_size[0] + 2, mask_size[1] + 1) + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + @unittest.expectedFailure + def test_to_surface__area_param(self): + """Ensures to_surface accepts an area arg/kwarg.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + default_surface_color = (0, 0, 0, 0) + default_unsetcolor = pygame.Color("black") + size = (5, 3) + mask = pygame.mask.Mask(size) + kwargs = {"area": mask.get_rect()} + + for use_kwargs in (True, False): + if use_kwargs: + expected_color = default_unsetcolor + + to_surface = mask.to_surface(**kwargs) + else: + expected_color = default_surface_color + + to_surface = mask.to_surface( + None, None, None, None, None, (0, 0), kwargs["area"] + ) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__area_default(self): + """Ensures the default area is correct.""" + expected_color = pygame.Color("white") + surface_color = pygame.Color("red") + + mask_size = (3, 2) + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + + # Make the surface bigger than the mask. The default area is the full + # area of the mask. + surf_size = (mask_size[0] + 2, mask_size[1] + 1) + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, setsurface=None, unsetsurface=None, unsetcolor=None + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__kwargs(self): + """Ensures to_surface accepts the correct kwargs.""" + expected_color = pygame.Color("white") + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + setsurface = surface.copy() + setsurface.fill(expected_color) + + test_data = ( + (None, None), # None entry allows loop to test all kwargs on first pass. + ("dest", (0, 0)), + ("unsetcolor", pygame.Color("yellow")), + ("setcolor", expected_color), + ("unsetsurface", surface.copy()), + ("setsurface", setsurface), + ("surface", surface), + ) + + kwargs = dict(test_data) + + for name, _ in test_data: + kwargs.pop(name) + surface.fill(surface_color) # Clear for each test. + + to_surface = mask.to_surface(**kwargs) + + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__kwargs_create_surface(self): + """Ensures to_surface accepts the correct kwargs + when creating a surface. + """ + expected_color = pygame.Color("black") + size = (5, 3) + mask = pygame.mask.Mask(size) + setsurface = pygame.Surface(size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + unsetsurface = setsurface.copy() + unsetsurface.fill(expected_color) + + test_data = ( + (None, None), # None entry allows loop to test all kwargs on first pass. + ("dest", (0, 0)), + ("unsetcolor", expected_color), + ("setcolor", pygame.Color("yellow")), + ("unsetsurface", unsetsurface), + ("setsurface", setsurface), + ("surface", None), + ) + kwargs = dict(test_data) + + for name, _ in test_data: + kwargs.pop(name) + + to_surface = mask.to_surface(**kwargs) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__kwargs_order_independent(self): + """Ensures to_surface kwargs are not order dependent.""" + expected_color = pygame.Color("blue") + size = (3, 2) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size) + + to_surface = mask.to_surface( + dest=(0, 0), + setcolor=expected_color, + unsetcolor=None, + surface=surface, + unsetsurface=pygame.Surface(size), + setsurface=None, + ) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__args_invalid_types(self): + """Ensures to_surface detects invalid kwarg types.""" + size = (3, 2) + mask = pygame.mask.Mask(size, fill=True) + invalid_surf = pygame.Color("green") + invalid_color = pygame.Surface(size) + + with self.assertRaises(TypeError): + # Invalid dest. + mask.to_surface(None, None, None, None, None, (0,)) + + with self.assertRaises(TypeError): + # Invalid unsetcolor. + mask.to_surface(None, None, None, None, invalid_color) + + with self.assertRaises(TypeError): + # Invalid setcolor. + mask.to_surface(None, None, None, invalid_color, None) + + with self.assertRaises(TypeError): + # Invalid unsetsurface. + mask.to_surface(None, None, invalid_surf, None, None) + + with self.assertRaises(TypeError): + # Invalid setsurface. + mask.to_surface(None, invalid_surf, None, None, None) + + with self.assertRaises(TypeError): + # Invalid surface. + mask.to_surface(invalid_surf, None, None, None, None) + + def test_to_surface__kwargs_invalid_types(self): + """Ensures to_surface detects invalid kwarg types.""" + size = (3, 2) + mask = pygame.mask.Mask(size) + + valid_kwargs = { + "surface": pygame.Surface(size), + "setsurface": pygame.Surface(size), + "unsetsurface": pygame.Surface(size), + "setcolor": pygame.Color("green"), + "unsetcolor": pygame.Color("green"), + "dest": (0, 0), + } + + invalid_kwargs = { + "surface": (1, 2, 3, 4), + "setsurface": pygame.Color("green"), + "unsetsurface": ((1, 2), (2, 1)), + "setcolor": pygame.Mask((1, 2)), + "unsetcolor": pygame.Surface((2, 2)), + "dest": (0, 0, 0), + } + + kwarg_order = ( + "surface", + "setsurface", + "unsetsurface", + "setcolor", + "unsetcolor", + "dest", + ) + + for kwarg in kwarg_order: + kwargs = dict(valid_kwargs) + kwargs[kwarg] = invalid_kwargs[kwarg] + + with self.assertRaises(TypeError): + mask.to_surface(**kwargs) + + def test_to_surface__kwargs_invalid_name(self): + """Ensures to_surface detects invalid kwarg names.""" + mask = pygame.mask.Mask((3, 2)) + kwargs = {"setcolour": pygame.Color("red")} + + with self.assertRaises(TypeError): + mask.to_surface(**kwargs) + + def test_to_surface__args_and_kwargs(self): + """Ensures to_surface accepts a combination of args/kwargs""" + size = (5, 3) + + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + surface = pygame.Surface(size, SRCALPHA, 32) + setsurface = surface.copy() + unsetsurface = surface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + mask = pygame.mask.Mask(size, fill=True) + expected_color = setsurface_color + + test_data = ( + (None, None), # None entry allows loop to test all kwargs on first pass. + ("surface", surface), + ("setsurface", setsurface), + ("unsetsurface", unsetsurface), + ("setcolor", setcolor), + ("unsetcolor", unsetcolor), + ("dest", (0, 0)), + ) + + args = [] + kwargs = dict(test_data) + + # Loop gradually moves the kwargs to args. + for name, value in test_data: + if name is not None: + args.append(value) + kwargs.pop(name) + + surface.fill(surface_color) + + to_surface = mask.to_surface(*args, **kwargs) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__valid_setcolor_formats(self): + """Ensures to_surface handles valid setcolor formats correctly.""" + size = (5, 3) + mask = pygame.mask.Mask(size, fill=True) + surface = pygame.Surface(size, SRCALPHA, 32) + expected_color = pygame.Color("green") + test_colors = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(expected_color), + expected_color, + "green", + "#00FF00FF", + "0x00FF00FF", + ) + + for setcolor in test_colors: + to_surface = mask.to_surface(setcolor=setcolor) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__valid_unsetcolor_formats(self): + """Ensures to_surface handles valid unsetcolor formats correctly.""" + size = (5, 3) + mask = pygame.mask.Mask(size) + surface = pygame.Surface(size, SRCALPHA, 32) + expected_color = pygame.Color("green") + test_colors = ( + (0, 255, 0), + (0, 255, 0, 255), + surface.map_rgb(expected_color), + expected_color, + "green", + "#00FF00FF", + "0x00FF00FF", + ) + + for unsetcolor in test_colors: + to_surface = mask.to_surface(unsetcolor=unsetcolor) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__invalid_setcolor_formats(self): + """Ensures to_surface handles invalid setcolor formats correctly.""" + mask = pygame.mask.Mask((5, 3)) + + for setcolor in ("green color", "#00FF00FF0", "0x00FF00FF0", (1, 2)): + with self.assertRaises(ValueError): + mask.to_surface(setcolor=setcolor) + + for setcolor in (pygame.Surface((1, 2)), pygame.Mask((2, 1)), 1.1): + with self.assertRaises(TypeError): + mask.to_surface(setcolor=setcolor) + + def test_to_surface__invalid_unsetcolor_formats(self): + """Ensures to_surface handles invalid unsetcolor formats correctly.""" + mask = pygame.mask.Mask((5, 3)) + + for unsetcolor in ("green color", "#00FF00FF0", "0x00FF00FF0", (1, 2)): + with self.assertRaises(ValueError): + mask.to_surface(unsetcolor=unsetcolor) + + for unsetcolor in (pygame.Surface((1, 2)), pygame.Mask((2, 1)), 1.1): + with self.assertRaises(TypeError): + mask.to_surface(unsetcolor=unsetcolor) + + def test_to_surface__valid_dest_formats(self): + """Ensures to_surface handles valid dest formats correctly.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + (0, 0), + [0, 0], + Vector2(0, 0), + (0, 0, 100, 100), + pygame.Rect((0, 0), (10, 10)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__invalid_dest_formats(self): + """Ensures to_surface handles invalid dest formats correctly.""" + mask = pygame.mask.Mask((3, 5)) + invalid_dests = ( + (0,), # Incorrect size. + (0, 0, 0), # Incorrect size. + {0, 1}, # Incorrect type. + {0: 1}, # Incorrect type. + Rect, + ) # Incorrect type. + + for dest in invalid_dests: + with self.assertRaises(TypeError): + mask.to_surface(dest=dest) + + def test_to_surface__negative_sized_dest_rect(self): + """Ensures to_surface correctly handles negative sized dest rects.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + pygame.Rect((0, 0), (10, -10)), + pygame.Rect((0, 0), (-10, 10)), + pygame.Rect((0, 0), (-10, -10)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__zero_sized_dest_rect(self): + """Ensures to_surface correctly handles zero sized dest rects.""" + expected_color = pygame.Color("white") + mask = pygame.mask.Mask((3, 5), fill=True) + dests = ( + pygame.Rect((0, 0), (0, 10)), + pygame.Rect((0, 0), (10, 0)), + pygame.Rect((0, 0), (0, 0)), + ) + + for dest in dests: + to_surface = mask.to_surface(dest=dest) + + assertSurfaceFilled(self, to_surface, expected_color) + + @unittest.expectedFailure + def test_to_surface__valid_area_formats(self): + """Ensures to_surface handles valid area formats correctly.""" + size = (3, 5) + surface_color = pygame.Color("red") + expected_color = pygame.Color("white") + surface = pygame.Surface(size) + mask = pygame.mask.Mask(size, fill=True) + area_pos = (0, 0) + area_size = (2, 1) + areas = ( + (area_pos[0], area_pos[1], area_size[0], area_size[1]), + (area_pos, area_size), + (area_pos, list(area_size)), + (list(area_pos), area_size), + (list(area_pos), list(area_size)), + [area_pos[0], area_pos[1], area_size[0], area_size[1]], + [area_pos, area_size], + [area_pos, list(area_size)], + [list(area_pos), area_size], + [list(area_pos), list(area_size)], + pygame.Rect(area_pos, area_size), + ) + + for area in areas: + surface.fill(surface_color) + area_rect = pygame.Rect(area) + + to_surface = mask.to_surface(surface, area=area) + + assertSurfaceFilled(self, to_surface, expected_color, area_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, area_rect) + + @unittest.expectedFailure + def test_to_surface__invalid_area_formats(self): + """Ensures to_surface handles invalid area formats correctly.""" + mask = pygame.mask.Mask((3, 5)) + invalid_areas = ( + (0,), # Incorrect size. + (0, 0), # Incorrect size. + (0, 0, 1), # Incorrect size. + ((0, 0), (1,)), # Incorrect size. + ((0,), (1, 1)), # Incorrect size. + {0, 1, 2, 3}, # Incorrect type. + {0: 1, 2: 3}, # Incorrect type. + Rect, # Incorrect type. + ) + + for area in invalid_areas: + with self.assertRaisesRegex(TypeError, "invalid area argument"): + unused_to_surface = mask.to_surface(area=area) + + @unittest.expectedFailure + def test_to_surface__negative_sized_area_rect(self): + """Ensures to_surface correctly handles negative sized area rects.""" + size = (3, 5) + surface_color = pygame.Color("red") + expected_color = pygame.Color("white") + surface = pygame.Surface(size) + mask = pygame.mask.Mask(size) + mask.set_at((0, 0)) + + # These rects should cause position (0, 0) of the mask to be drawn. + areas = ( + pygame.Rect((0, 1), (1, -1)), + pygame.Rect((1, 0), (-1, 1)), + pygame.Rect((1, 1), (-1, -1)), + ) + + for area in areas: + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, area=area) + + assertSurfaceFilled(self, to_surface, expected_color, area) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, area) + + @unittest.expectedFailure + def test_to_surface__zero_sized_area_rect(self): + """Ensures to_surface correctly handles zero sized area rects.""" + size = (3, 5) + expected_color = pygame.Color("red") + surface = pygame.Surface(size) + mask = pygame.mask.Mask(size, fill=True) + + # Zero sized rect areas should cause none of the mask to be drawn. + areas = ( + pygame.Rect((0, 0), (0, 1)), + pygame.Rect((0, 0), (1, 0)), + pygame.Rect((0, 0), (0, 0)), + ) + + for area in areas: + surface.fill(expected_color) + + to_surface = mask.to_surface(surface, area=area) + + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__default_surface_with_param_combinations(self): + """Ensures to_surface works with a default surface value + and combinations of other parameters. + + This tests many different parameter combinations with full and empty + masks. + """ + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + size = (5, 3) + dest = (0, 0) + + default_surface_color = (0, 0, 0, 0) + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + setsurface = pygame.Surface(size, expected_flag, expected_depth) + unsetsurface = setsurface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "setsurface": None, + "unsetsurface": None, + "setcolor": None, + "unsetcolor": None, + "dest": None, + } + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # Test different combinations of parameters. + for setsurface_param in (setsurface, None): + kwargs["setsurface"] = setsurface_param + + for unsetsurface_param in (unsetsurface, None): + kwargs["unsetsurface"] = unsetsurface_param + + for setcolor_param in (setcolor, None): + kwargs["setcolor"] = setcolor_param + + for unsetcolor_param in (unsetcolor, None): + kwargs["unsetcolor"] = unsetcolor_param + + for dest_param in (dest, None): + if dest_param is None: + kwargs.pop("dest", None) + else: + kwargs["dest"] = dest_param + + if fill: + if setsurface_param is not None: + expected_color = setsurface_color + elif setcolor_param is not None: + expected_color = setcolor + else: + expected_color = default_surface_color + else: + if unsetsurface_param is not None: + expected_color = unsetsurface_color + elif unsetcolor_param is not None: + expected_color = unsetcolor + else: + expected_color = default_surface_color + + to_surface = mask.to_surface(**kwargs) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual( + sys.getrefcount(to_surface), expected_ref_count + ) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual( + to_surface.get_bitsize(), expected_depth + ) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_with_param_combinations(self): + """Ensures to_surface works with a surface value + and combinations of other parameters. + + This tests many different parameter combinations with full and empty + masks. + """ + expected_ref_count = 4 + expected_flag = SRCALPHA + expected_depth = 32 + size = (5, 3) + dest = (0, 0) + + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("yellow") + unsetsurface_color = pygame.Color("blue") + setcolor = pygame.Color("green") + unsetcolor = pygame.Color("cyan") + + surface = pygame.Surface(size, expected_flag, expected_depth) + setsurface = surface.copy() + unsetsurface = surface.copy() + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + kwargs = { + "surface": surface, + "setsurface": None, + "unsetsurface": None, + "setcolor": None, + "unsetcolor": None, + "dest": None, + } + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # Test different combinations of parameters. + for setsurface_param in (setsurface, None): + kwargs["setsurface"] = setsurface_param + + for unsetsurface_param in (unsetsurface, None): + kwargs["unsetsurface"] = unsetsurface_param + + for setcolor_param in (setcolor, None): + kwargs["setcolor"] = setcolor_param + + for unsetcolor_param in (unsetcolor, None): + kwargs["unsetcolor"] = unsetcolor_param + surface.fill(surface_color) # Clear for each test. + + for dest_param in (dest, None): + if dest_param is None: + kwargs.pop("dest", None) + else: + kwargs["dest"] = dest_param + + if fill: + if setsurface_param is not None: + expected_color = setsurface_color + elif setcolor_param is not None: + expected_color = setcolor + else: + expected_color = surface_color + else: + if unsetsurface_param is not None: + expected_color = unsetsurface_color + elif unsetcolor_param is not None: + expected_color = unsetcolor + else: + expected_color = surface_color + + to_surface = mask.to_surface(**kwargs) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual( + sys.getrefcount(to_surface), expected_ref_count + ) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual( + to_surface.get_bitsize(), expected_depth + ) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__set_and_unset_bits(self): + """Ensures that to_surface works correctly with with set/unset bits + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (10, 20) + mask = pygame.mask.Mask(size) + mask_rect = mask.get_rect() + + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + + # Create a checkerboard pattern of set/unset bits. + for pos in ((x, y) for x in range(width) for y in range(x & 1, height, 2)): + mask.set_at(pos) + + # Test different dest values. + for dest in self.ORIGIN_OFFSETS: + mask_rect.topleft = dest + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, dest=dest) + + to_surface.lock() # Lock for possible speed up. + for pos in ((x, y) for x in range(width) for y in range(height)): + mask_pos = (pos[0] - dest[0], pos[1] - dest[1]) + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(mask_pos): + expected_color = default_setcolor + else: + expected_color = default_unsetcolor + + self.assertEqual(to_surface.get_at(pos), expected_color, (dest, pos)) + to_surface.unlock() + + def test_to_surface__set_and_unset_bits_with_setsurface_unsetsurface(self): + """Ensures that to_surface works correctly with with set/unset bits + when using setsurface and unsetsurface. + """ + width, height = size = (10, 20) + mask = pygame.mask.Mask(size) + mask_rect = mask.get_rect() + + surface = pygame.Surface(size) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Create a checkerboard pattern of set/unset bits. + for pos in ((x, y) for x in range(width) for y in range(x & 1, height, 2)): + mask.set_at(pos) + + # Test different dest values. + for dest in self.ORIGIN_OFFSETS: + mask_rect.topleft = dest + + # Tests the color parameters set to None and also as their + # default values. Should have no effect as they are not being + # used, but this exercises different to_surface() code. + for disable_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + if disable_color_params: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + setcolor=None, + unsetcolor=None, + ) + else: + to_surface = mask.to_surface( + surface, + dest=dest, + setsurface=setsurface, + unsetsurface=unsetsurface, + ) + + to_surface.lock() # Lock for possible speed up. + + for pos in ((x, y) for x in range(width) for y in range(height)): + mask_pos = (pos[0] - dest[0], pos[1] - dest[1]) + + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(mask_pos): + expected_color = setsurface_color + else: + expected_color = unsetsurface_color + + self.assertEqual(to_surface.get_at(pos), expected_color) + to_surface.unlock() + + def test_to_surface__surface_narrower_than_mask(self): + """Ensures that surfaces narrower than the mask work correctly. + + For this test the surface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + surface = pygame.Surface(narrow_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_narrower_than_mask(self): + """Ensures that setsurfaces narrower than the mask work correctly. + + For this test the setsurface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + setsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_than_mask(self): + """Ensures that unsetsurfaces narrower than the mask work correctly. + + For this test the unsetsurface's width is less than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 20) + narrow_size = (6, 20) + + unsetsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__setsurface_narrower_than_mask_and_colors_none(self): + """Ensures that setsurfaces narrower than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the setsurface's width is less than the mask's width. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 20) + narrow_size = (6, 20) + + setsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + setsurface=setsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_surface_color) + + def test_to_surface__unsetsurface_narrower_than_mask_and_colors_none(self): + """Ensures that unsetsurfaces narrower than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the unsetsurface's width is less than the mask's width. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 20) + narrow_size = (6, 20) + + unsetsurface = pygame.Surface(narrow_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + unsetsurface=unsetsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_surface_color) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, unsetsurface_rect + ) + + def test_to_surface__surface_wider_than_mask(self): + """Ensures that surfaces wider than the mask work correctly. + + For this test the surface's width is greater than the mask's width. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (6, 15) + wide_size = (11, 15) + + surface = pygame.Surface(wide_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_than_mask(self): + """Ensures that setsurfaces wider than the mask work correctly. + + For this test the setsurface's width is greater than the mask's width. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (6, 15) + wide_size = (11, 15) + + setsurface = pygame.Surface(wide_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_wider_than_mask(self): + """Ensures that unsetsurfaces wider than the mask work correctly. + + For this test the unsetsurface's width is greater than the mask's + width. + """ + default_setcolor = pygame.Color("white") + mask_size = (6, 15) + wide_size = (11, 15) + + unsetsurface = pygame.Surface(wide_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_shorter_than_mask(self): + """Ensures that surfaces shorter than the mask work correctly. + + For this test the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + surface = pygame.Surface(short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), short_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__setsurface_shorter_than_mask(self): + """Ensures that setsurfaces shorter than the mask work correctly. + + For this test the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + setsurface = pygame.Surface(short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_shorter_than_mask(self): + """Ensures that unsetsurfaces shorter than the mask work correctly. + + For this test the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 11) + short_size = (10, 6) + + unsetsurface = pygame.Surface(short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__setsurface_shorter_than_mask_and_colors_none(self): + """Ensures that setsurfaces shorter than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the setsurface's height is less than the mask's height. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 11) + short_size = (10, 6) + + setsurface = pygame.Surface(short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + setsurface=setsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_surface_color) + + def test_to_surface__unsetsurface_shorter_than_mask_and_colors_none(self): + """Ensures that unsetsurfaces shorter than the mask work correctly + when setcolor and unsetcolor are set to None. + + For this test the unsetsurface's height is less than the mask's height. + """ + default_surface_color = (0, 0, 0, 0) + mask_size = (10, 11) + short_size = (10, 6) + + unsetsurface = pygame.Surface(short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface( + unsetsurface=unsetsurface, setcolor=None, unsetcolor=None + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_surface_color) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_surface_color, unsetsurface_rect + ) + + def test_to_surface__surface_taller_than_mask(self): + """Ensures that surfaces taller than the mask work correctly. + + For this test the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 6) + tall_size = (10, 11) + + surface = pygame.Surface(tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_taller_than_mask(self): + """Ensures that setsurfaces taller than the mask work correctly. + + For this test the setsurface's height is greater than the mask's + height. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (10, 6) + tall_size = (10, 11) + + setsurface = pygame.Surface(tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_taller_than_mask(self): + """Ensures that unsetsurfaces taller than the mask work correctly. + + For this test the unsetsurface's height is greater than the mask's + height. + """ + default_setcolor = pygame.Color("white") + mask_size = (10, 6) + tall_size = (10, 11) + + unsetsurface = pygame.Surface(tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_wider_and_taller_than_mask(self): + """Ensures that surfaces wider and taller than the mask work correctly. + + For this test the surface's width is greater than the mask's width and + the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + surface = pygame.Surface(wide_tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_and_taller_than_mask(self): + """Ensures that setsurfaces wider and taller than the mask work + correctly. + + For this test the setsurface's width is greater than the mask's width + and the setsurface's height is greater than the mask's height. + """ + default_unsetcolor = pygame.Color("black") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + setsurface = pygame.Surface(wide_tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = setsurface_color if fill else default_unsetcolor + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_wider_and_taller_than_mask(self): + """Ensures that unsetsurfaces wider and taller than the mask work + correctly. + + For this test the unsetsurface's width is greater than the mask's width + and the unsetsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + mask_size = (6, 8) + wide_tall_size = (11, 15) + + unsetsurface = pygame.Surface(wide_tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + expected_color = default_setcolor if fill else unsetsurface_color + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__surface_wider_and_shorter_than_mask(self): + """Ensures that surfaces wider and shorter than the mask work + correctly. + + For this test the surface's width is greater than the mask's width and + the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (13, 6) + + surface = pygame.Surface(wide_short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), wide_short_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_wider_and_shorter_than_mask(self): + """Ensures that setsurfaces wider and shorter than the mask work + correctly. + + For this test the setsurface's width is greater than the mask's width + and the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (10, 6) + + setsurface = pygame.Surface(wide_short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_wider_and_shorter_than_mask(self): + """Ensures that unsetsurfaces wider and shorter than the mask work + correctly. + + For this test the unsetsurface's width is greater than the mask's width + and the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (7, 11) + wide_short_size = (10, 6) + + unsetsurface = pygame.Surface(wide_short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__surface_narrower_and_taller_than_mask(self): + """Ensures that surfaces narrower and taller than the mask work + correctly. + + For this test the surface's width is less than the mask's width and + the surface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + surface = pygame.Surface(narrow_tall_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_tall_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_narrower_and_taller_than_mask(self): + """Ensures that setsurfaces narrower and taller than the mask work + correctly. + + For this test the setsurface's width is less than the mask's width + and the setsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + setsurface = pygame.Surface(narrow_tall_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_and_taller_than_mask(self): + """Ensures that unsetsurfaces narrower and taller than the mask work + correctly. + + For this test the unsetsurface's width is less than the mask's width + and the unsetsurface's height is greater than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 8) + narrow_tall_size = (6, 15) + + unsetsurface = pygame.Surface(narrow_tall_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + def test_to_surface__surface_narrower_and_shorter_than_mask(self): + """Ensures that surfaces narrower and shorter than the mask work + correctly. + + For this test the surface's width is less than the mask's width and + the surface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + surface = pygame.Surface(narrow_short_size) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + mask_rect = mask.get_rect() + surface.fill(surface_color) # Clear for each test. + expected_color = default_setcolor if fill else default_unsetcolor + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), narrow_short_size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea(self, to_surface, surface_color, mask_rect) + + def test_to_surface__setsurface_narrower_and_shorter_than_mask(self): + """Ensures that setsurfaces narrower and shorter than the mask work + correctly. + + For this test the setsurface's width is less than the mask's width + and the setsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + setsurface = pygame.Surface(narrow_short_size, SRCALPHA, 32) + setsurface_color = pygame.Color("red") + setsurface.fill(setsurface_color) + setsurface_rect = setsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, setsurface_color, setsurface_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_setcolor, setsurface_rect + ) + else: + assertSurfaceFilled(self, to_surface, default_unsetcolor) + + def test_to_surface__unsetsurface_narrower_and_shorter_than_mask(self): + """Ensures that unsetsurfaces narrower and shorter than the mask work + correctly. + + For this test the unsetsurface's width is less than the mask's width + and the unsetsurface's height is less than the mask's height. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + mask_size = (10, 18) + narrow_short_size = (6, 15) + + unsetsurface = pygame.Surface(narrow_short_size, SRCALPHA, 32) + unsetsurface_color = pygame.Color("red") + unsetsurface.fill(unsetsurface_color) + unsetsurface_rect = unsetsurface.get_rect() + + for fill in (True, False): + mask = pygame.mask.Mask(mask_size, fill=fill) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + # Different checks depending on if the mask was filled or not. + if fill: + assertSurfaceFilled(self, to_surface, default_setcolor) + else: + assertSurfaceFilled( + self, to_surface, unsetsurface_color, unsetsurface_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, default_unsetcolor, unsetsurface_rect + ) + + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__all_surfaces_different_sizes_than_mask(self): + """Ensures that all the surface parameters can be of different sizes.""" + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + mask_size = (10, 15) + surface_size = (11, 14) + setsurface_size = (9, 8) + unsetsurface_size = (12, 16) + + surface = pygame.Surface(surface_size) + setsurface = pygame.Surface(setsurface_size) + unsetsurface = pygame.Surface(unsetsurface_size) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + surface_rect = surface.get_rect() + setsurface_rect = setsurface.get_rect() + unsetsurface_rect = unsetsurface.get_rect() + + # Create a mask that is filled except for a rect in the center. + mask = pygame.mask.Mask(mask_size, fill=True) + mask_rect = mask.get_rect() + unfilled_rect = pygame.Rect((0, 0), (4, 5)) + unfilled_rect.center = mask_rect.center + + for pos in ( + (x, y) + for x in range(unfilled_rect.x, unfilled_rect.w) + for y in range(unfilled_rect.y, unfilled_rect.h) + ): + mask.set_at(pos, 0) + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surface_size) + + # Check each surface pixel for the correct color. + to_surface.lock() # Lock for possible speed up. + + for pos in ( + (x, y) for x in range(surface_rect.w) for y in range(surface_rect.h) + ): + if not mask_rect.collidepoint(pos): + expected_color = surface_color + elif mask.get_at(pos): + # Checking set bit colors. + if setsurface_rect.collidepoint(pos): + expected_color = setsurface_color + else: + expected_color = default_setcolor + else: + # Checking unset bit colors. + if unsetsurface_rect.collidepoint(pos): + expected_color = unsetsurface_color + else: + expected_color = default_unsetcolor + + self.assertEqual(to_surface.get_at(pos), expected_color) + + to_surface.unlock() + + def test_to_surface__dest_locations(self): + """Ensures dest values can be different locations on/off the surface.""" + SIDE = 7 + surface = pygame.Surface((SIDE, SIDE)) + surface_rect = surface.get_rect() + dest_rect = surface_rect.copy() + + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + directions = ( + ((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right + ((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom + ((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag + ((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag + ) + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + expected_color = default_setcolor if fill else default_unsetcolor + + for direction in directions: + for pos in direction: + dest_rect.topleft = pos + overlap_rect = dest_rect.clip(surface_rect) + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, dest=dest_rect) + + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_locations(self): + """Ensures area rects can be different locations on/off the mask.""" + SIDE = 7 + surface = pygame.Surface((SIDE, SIDE)) + + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + directions = ( + ((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right + ((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom + ((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag + ((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag + ) + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + for direction in directions: + for pos in direction: + area_rect.topleft = pos + overlap_rect = area_rect.clip(mask_rect) + overlap_rect.topleft = (0, 0) + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, area=area_rect) + + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + def test_to_surface__dest_and_area_locations(self): + """Ensures dest/area values can be different locations on/off the + surface/mask. + """ + SIDE = 5 + surface = pygame.Surface((SIDE, SIDE)) + surface_rect = surface.get_rect() + dest_rect = surface_rect.copy() + + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + dest_directions = ( + ((s, 0) for s in range(-SIDE, SIDE + 1)), # left to right + ((0, s) for s in range(-SIDE, SIDE + 1)), # top to bottom + ((s, s) for s in range(-SIDE, SIDE + 1)), # topleft to bottomright diag + ((-s, s) for s in range(-SIDE, SIDE + 1)), # topright to bottomleft diag + ) + + # Using only the topleft to bottomright diagonal to test the area (to + # reduce the number of loop iterations). + area_positions = list(dest_directions[2]) + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + for dest_direction in dest_directions: + for dest_pos in dest_direction: + dest_rect.topleft = dest_pos + + for area_pos in area_positions: + area_rect.topleft = area_pos + area_overlap_rect = area_rect.clip(mask_rect) + area_overlap_rect.topleft = dest_rect.topleft + dest_overlap_rect = dest_rect.clip(area_overlap_rect) + + surface.fill(surface_color) + + to_surface = mask.to_surface( + surface, dest=dest_rect, area=area_rect + ) + + assertSurfaceFilled( + self, to_surface, expected_color, dest_overlap_rect + ) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, dest_overlap_rect + ) + + @unittest.expectedFailure + def test_to_surface__area_sizes(self): + """Ensures area rects can be different sizes.""" + SIDE = 7 + SIZES = ( + (0, 0), + (0, 1), + (1, 0), + (1, 1), + (SIDE - 1, SIDE - 1), + (SIDE - 1, SIDE), + (SIDE, SIDE - 1), + (SIDE, SIDE), + (SIDE + 1, SIDE), + (SIDE, SIDE + 1), + (SIDE + 1, SIDE + 1), + ) + + surface = pygame.Surface((SIDE, SIDE)) + surface_color = pygame.Color("red") + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + + for fill in (True, False): + mask = pygame.mask.Mask((SIDE, SIDE), fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + for size in SIZES: + area_rect = pygame.Rect((0, 0), size) + + for pos in self.ORIGIN_OFFSETS: + area_rect.topleft = pos + overlap_rect = area_rect.clip(mask_rect) + overlap_rect.topleft = (0, 0) + surface.fill(surface_color) + + to_surface = mask.to_surface(surface, area=area_rect) + + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + def test_to_surface__surface_color_alphas(self): + """Ensures the setsurface/unsetsurface color alpha values are respected.""" + size = (13, 17) + setsurface_color = pygame.Color("green") + setsurface_color.a = 53 + unsetsurface_color = pygame.Color("blue") + unsetsurface_color.a = 109 + + setsurface = pygame.Surface(size, flags=SRCALPHA, depth=32) + unsetsurface = pygame.Surface(size, flags=SRCALPHA, depth=32) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__color_alphas(self): + """Ensures the setcolor/unsetcolor alpha values are respected.""" + size = (13, 17) + setcolor = pygame.Color("green") + setcolor.a = 35 + unsetcolor = pygame.Color("blue") + unsetcolor.a = 213 + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setcolor if fill else unsetcolor + + to_surface = mask.to_surface(setcolor=setcolor, unsetcolor=unsetcolor) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__depths(self): + """Ensures to_surface works correctly with supported surface depths.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + for depth in (8, 16, 24, 32): + surface = pygame.Surface(size, depth=depth) + setsurface = pygame.Surface(size, depth=depth) + unsetsurface = pygame.Surface(size, depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + + # For non-32 bit depths, the actual color can be different from + # what was filled. + expected_color = ( + setsurface.get_at((0, 0)) if fill else unsetsurface.get_at((0, 0)) + ) + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__different_depths(self): + """Ensures an exception is raised when surfaces have different depths.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of depths. + test_depths = ( + (8, 8, 16), # surface/setsurface/unsetsurface + (8, 8, 24), + (8, 8, 32), + (16, 16, 24), + (16, 16, 32), + (24, 16, 8), + (32, 16, 16), + (32, 32, 16), + (32, 24, 32), + ) + + for depths in test_depths: + surface = pygame.Surface(size, depth=depths[0]) + setsurface = pygame.Surface(size, depth=depths[1]) + unsetsurface = pygame.Surface(size, depth=depths[2]) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(surface, setsurface, unsetsurface) + + def test_to_surface__different_depths_with_created_surfaces(self): + """Ensures an exception is raised when surfaces have different depths + than the created surface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of depths. The created surface always has + # a depth of 32. + test_depths = ( + (8, 8), # setsurface/unsetsurface + (16, 16), + (24, 24), + (24, 16), + (32, 8), + (32, 16), + (32, 24), + (16, 32), + ) + + for set_depth, unset_depth in test_depths: + setsurface = pygame.Surface(size, depth=set_depth) + unsetsurface = pygame.Surface(size, depth=unset_depth) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(setsurface=setsurface, unsetsurface=unsetsurface) + + def test_to_surface__same_srcalphas(self): + """Ensures to_surface works correctly when the SRCALPHA flag is set or not.""" + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + + for depth in (16, 32): + for flags in (0, SRCALPHA): + surface = pygame.Surface(size, flags=flags, depth=depth) + setsurface = pygame.Surface(size, flags=flags, depth=depth) + unsetsurface = pygame.Surface(size, flags=flags, depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface(surface, setsurface, unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + if flags: + self.assertTrue(to_surface.get_flags() & flags) + + def test_to_surface__same_srcalphas_with_created_surfaces(self): + """Ensures to_surface works correctly when it creates a surface + and the SRCALPHA flag is set on both setsurface and unsetsurface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + # The created surface always has a depth of 32 and the SRCALPHA flag set. + expected_flags = SRCALPHA + + setsurface = pygame.Surface(size, flags=expected_flags, depth=32) + unsetsurface = pygame.Surface(size, flags=expected_flags, depth=32) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + expected_color = setsurface_color if fill else unsetsurface_color + + to_surface = mask.to_surface( + setsurface=setsurface, unsetsurface=unsetsurface + ) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + self.assertTrue(to_surface.get_flags() & expected_flags) + + def test_to_surface__different_srcalphas(self): + """Ensures an exception is raised when surfaces have different SRCALPHA + flag settings. + """ + size = (13, 17) + surface_color = pygame.Color("red") + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + # Test different combinations of SRCALPHA flags. + test_flags = ( + (SRCALPHA, 0, 0), # surface/setsurface/unsetsurface + (SRCALPHA, SRCALPHA, 0), + (0, SRCALPHA, SRCALPHA), + (0, 0, SRCALPHA), + ) + + for depth in (16, 32): + for flags in test_flags: + surface = pygame.Surface(size, flags=flags[0], depth=depth) + setsurface = pygame.Surface(size, flags=flags[1], depth=depth) + unsetsurface = pygame.Surface(size, flags=flags[2], depth=depth) + + surface.fill(surface_color) + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(surface, setsurface, unsetsurface) + + def test_to_surface__different_srcalphas_with_created_surfaces(self): + """Ensures an exception is raised when surfaces have different SRCALPHA + flag settings than the created surface. + """ + size = (13, 17) + setsurface_color = pygame.Color("green") + unsetsurface_color = pygame.Color("blue") + mask = pygame.mask.Mask(size) + + for depth in (16, 32): + # Test different combinations of SRCALPHA flags. The created + # surface always has the SRCALPHA flag set. + for flags in ((0, 0), (SRCALPHA, 0), (0, SRCALPHA)): + setsurface = pygame.Surface(size, flags=flags[0], depth=depth) + unsetsurface = pygame.Surface(size, flags=flags[1], depth=depth) + + setsurface.fill(setsurface_color) + unsetsurface.fill(unsetsurface_color) + + with self.assertRaises(ValueError): + mask.to_surface(setsurface=setsurface, unsetsurface=unsetsurface) + + def test_to_surface__dest_on_surface(self): + """Ensures dest values on the surface work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + # Test the dest parameter at different locations on the surface. + for dest in ((x, y) for y in range(height) for x in range(width)): + surface.fill(surface_color) # Clear for each test. + mask_rect.topleft = dest + + to_surface = mask.to_surface(surface, dest=dest) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_on_surface_with_setsurface_unsetsurface(self): + """Ensures dest values on the surface work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Using different kwargs to exercise different to_surface() code. + # Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "dest": None, + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + # Test the dest parameter at different locations on the surface. + for dest in ((x, y) for y in range(height) for x in range(width)): + mask_rect.topleft = dest + + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["dest"] = dest + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_off_surface(self): + """Ensures dest values off the surface work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + # Test different dests off the surface. + dests = [(-width, -height), (-width, 0), (0, -height)] + dests.extend(off_corners(surface.get_rect())) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = default_setcolor if fill else default_unsetcolor + + for dest in dests: + surface.fill(surface_color) # Clear for each test. + mask_rect.topleft = dest + + to_surface = mask.to_surface(surface, dest=dest) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + def test_to_surface__dest_off_surface_with_setsurface_unsetsurface(self): + """Ensures dest values off the surface work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Test different dests off the surface. + dests = [(-width, -height), (-width, 0), (0, -height)] + dests.extend(off_corners(surface.get_rect())) + + # Using different kwargs to exercise different to_surface() code. + # Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "dest": None, + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + for dest in dests: + mask_rect.topleft = dest + + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["dest"] = dest + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, mask_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, mask_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_on_mask(self): + """Ensures area values on the mask work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + # Testing the area parameter at different locations on the mask. + for pos in ((x, y) for y in range(height) for x in range(width)): + surface.fill(surface_color) # Clear for each test. + area_rect.topleft = pos + overlap_rect = mask_rect.clip(area_rect) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(surface, area=area_rect) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + def test_to_surface__area_on_mask_with_setsurface_unsetsurface(self): + """Ensures area values on the mask work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 9) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Using the values in kwargs vs color_kwargs tests different to_surface + # code. Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "area": pygame.Rect((0, 0), size), + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = setsurface_color if fill else unsetsurface_color + + # Testing the area parameter at different locations on the mask. + for pos in ((x, y) for y in range(height) for x in range(width)): + area_rect.topleft = pos + overlap_rect = mask_rect.clip(area_rect) + overlap_rect.topleft = (0, 0) + + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["area"].topleft = pos + overlap_rect = mask_rect.clip(test_kwargs["area"]) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_off_mask(self): + """Ensures area values off the mask work correctly + when using the defaults for setcolor and unsetcolor. + """ + default_setcolor = pygame.Color("white") + default_unsetcolor = pygame.Color("black") + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + # Testing positions off the mask. + positions = [(-width, -height), (-width, 0), (0, -height)] + positions.extend(off_corners(pygame.Rect((0, 0), (width, height)))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + area_rect = mask_rect.copy() + expected_color = default_setcolor if fill else default_unsetcolor + + for pos in positions: + surface.fill(surface_color) # Clear for each test. + area_rect.topleft = pos + overlap_rect = mask_rect.clip(area_rect) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(surface, area=area_rect) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + @unittest.expectedFailure + @unittest.skipIf(IS_PYPY, "Segfaults on pypy") + def test_to_surface__area_off_mask_with_setsurface_unsetsurface(self): + """Ensures area values off the mask work correctly + when using setsurface and unsetsurface. + """ + width, height = size = (5, 7) + surface = pygame.Surface(size, SRCALPHA, 32) + surface_color = pygame.Color("red") + + setsurface = surface.copy() + setsurface_color = pygame.Color("green") + setsurface.fill(setsurface_color) + + unsetsurface = surface.copy() + unsetsurface_color = pygame.Color("blue") + unsetsurface.fill(unsetsurface_color) + + # Testing positions off the mask. + positions = [(-width, -height), (-width, 0), (0, -height)] + positions.extend(off_corners(pygame.Rect((0, 0), (width, height)))) + + # Using the values in kwargs vs color_kwargs tests different to_surface + # code. Should not have any impact on the resulting drawn surfaces. + kwargs = { + "surface": surface, + "setsurface": setsurface, + "unsetsurface": unsetsurface, + "area": pygame.Rect((0, 0), size), + } + + color_kwargs = dict(kwargs) + color_kwargs.update((("setcolor", None), ("unsetcolor", None))) + + for fill in (True, False): + mask = pygame.mask.Mask(size, fill=fill) + mask_rect = mask.get_rect() + expected_color = setsurface_color if fill else unsetsurface_color + + for pos in positions: + for use_color_params in (True, False): + surface.fill(surface_color) # Clear for each test. + test_kwargs = color_kwargs if use_color_params else kwargs + test_kwargs["area"].topleft = pos + overlap_rect = mask_rect.clip(test_kwargs["area"]) + overlap_rect.topleft = (0, 0) + + to_surface = mask.to_surface(**test_kwargs) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color, overlap_rect) + assertSurfaceFilledIgnoreArea( + self, to_surface, surface_color, overlap_rect + ) + + def test_to_surface__surface_with_zero_size(self): + """Ensures zero sized surfaces are handled correctly.""" + expected_ref_count = 3 + size = (0, 0) + surface = pygame.Surface(size) + mask = pygame.mask.Mask((3, 4), fill=True) + + to_surface = mask.to_surface(surface) + + self.assertIs(to_surface, surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertEqual(to_surface.get_size(), size) + + def test_to_surface__setsurface_with_zero_size(self): + """Ensures zero sized setsurfaces are handled correctly.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("white") # Default setcolor. + mask_size = (2, 4) + mask = pygame.mask.Mask(mask_size, fill=True) + setsurface = pygame.Surface((0, 0), expected_flag, expected_depth) + + to_surface = mask.to_surface(setsurface=setsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_to_surface__unsetsurface_with_zero_size(self): + """Ensures zero sized unsetsurfaces are handled correctly.""" + expected_ref_count = 2 + expected_flag = SRCALPHA + expected_depth = 32 + expected_color = pygame.Color("black") # Default unsetcolor. + mask_size = (4, 2) + mask = pygame.mask.Mask(mask_size) + unsetsurface = pygame.Surface((0, 0), expected_flag, expected_depth) + + to_surface = mask.to_surface(unsetsurface=unsetsurface) + + self.assertIsInstance(to_surface, pygame.Surface) + if not IS_PYPY: + self.assertEqual(sys.getrefcount(to_surface), expected_ref_count) + self.assertTrue(to_surface.get_flags() & expected_flag) + self.assertEqual(to_surface.get_bitsize(), expected_depth) + self.assertEqual(to_surface.get_size(), mask_size) + assertSurfaceFilled(self, to_surface, expected_color) + + def test_zero_mask(self): + """Ensures masks can be created with zero sizes.""" + for size in ((100, 0), (0, 100), (0, 0)): + for fill in (True, False): + msg = f"size={size}, fill={fill}" + + mask = pygame.mask.Mask(size, fill=fill) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), size, msg) + + def test_zero_mask_copy(self): + """Ensures copy correctly handles zero sized masks.""" + for expected_size in ((11, 0), (0, 11), (0, 0)): + mask = pygame.mask.Mask(expected_size) + + mask_copy = mask.copy() + + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + def test_zero_mask_get_size(self): + """Ensures get_size correctly handles zero sized masks.""" for expected_size in ((41, 0), (0, 40), (0, 0)): mask = pygame.mask.Mask(expected_size) @@ -1571,6 +5356,16 @@ class MaskTypeTest(unittest.TestCase): self.assertEqual(size, expected_size) + def test_zero_mask_get_rect(self): + """Ensures get_rect correctly handles zero sized masks.""" + for expected_size in ((4, 0), (0, 4), (0, 0)): + expected_rect = pygame.Rect((0, 0), expected_size) + mask = pygame.mask.Mask(expected_size) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + def test_zero_mask_get_at(self): """Ensures get_at correctly handles zero sized masks.""" for size in ((51, 0), (0, 50), (0, 0)): @@ -1588,42 +5383,67 @@ class MaskTypeTest(unittest.TestCase): mask.set_at((0, 0)) def test_zero_mask_overlap(self): - sizes = ((100, 0), (0, 100), (0, 0)) + """Ensures overlap correctly handles zero sized masks. - for size in sizes: - mask = pygame.mask.Mask(size) - mask2 = pygame.mask.Mask((100, 100)) - self.assertEqual(mask.overlap(mask2, (0, 0)), None) - self.assertEqual(mask2.overlap(mask, (0, 0)), None) + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(51, 42): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_pos = mask1.overlap(mask2, offset) + + self.assertIsNone(overlap_pos, msg) def test_zero_mask_overlap_area(self): - sizes = ((100, 0), (0, 100), (0, 0)) + """Ensures overlap_area correctly handles zero sized masks. - for size in sizes: - mask = pygame.mask.Mask(size) - mask2 = pygame.mask.Mask((100, 100)) - self.assertEqual(mask.overlap_area(mask2, (0, 0)), 0) - self.assertEqual(mask2.overlap_area(mask, (0, 0)), 0) + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + expected_count = 0 + + for size1, size2 in zero_size_pairs(41, 52): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + + overlap_count = mask1.overlap_area(mask2, offset) + + self.assertEqual(overlap_count, expected_count, msg) def test_zero_mask_overlap_mask(self): - sizes = ((100, 0), (0, 100), (0, 0)) + """Ensures overlap_mask correctly handles zero sized masks. - for size in sizes: - mask = pygame.mask.Mask(size) - mask2 = pygame.mask.Mask((100, 100)) + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + expected_count = 0 + + for size1, size2 in zero_size_pairs(43, 53): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) - overlap_mask = mask.overlap_mask(mask2, (0, 0)) - overlap_mask2 = mask2.overlap_mask(mask, (0, 0)) + overlap_mask = mask1.overlap_mask(mask2, offset) - self.assertEqual(mask.get_size(), overlap_mask.get_size()) - self.assertEqual(mask2.get_size(), overlap_mask2.get_size()) + self.assertIsInstance(overlap_mask, pygame.mask.Mask, msg) + self.assertEqual(overlap_mask.count(), expected_count, msg) + self.assertEqual(overlap_mask.get_size(), size1, msg) def test_zero_mask_fill(self): - sizes = ((100, 0), (0, 100), (0, 0)) + """Ensures fill correctly handles zero sized masks.""" + expected_count = 0 - for size in sizes: - mask = pygame.mask.Mask(size, fill=True) - self.assertEqual(mask.count(), 0) + for size in ((100, 0), (0, 100), (0, 0)): + mask = pygame.mask.Mask(size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count, f"size={size}") def test_zero_mask_clear(self): sizes = ((100, 0), (0, 100), (0, 0)) @@ -1647,29 +5467,45 @@ class MaskTypeTest(unittest.TestCase): for size in sizes: mask = pygame.mask.Mask(size) mask2 = mask.scale((2, 3)) + + self.assertIsInstance(mask2, pygame.mask.Mask) self.assertEqual(mask2.get_size(), (2, 3)) def test_zero_mask_draw(self): - sizes = ((100, 0), (0, 100), (0, 0)) + """Ensures draw correctly handles zero sized masks. - for size in sizes: - mask = pygame.mask.Mask(size) - mask2 = pygame.mask.Mask((100, 100), fill=True) - before = [mask2.get_at((x, y)) for x in range(100) for y in range(100)] - mask.draw(mask2, (0, 0)) - after = [mask2.get_at((x, y)) for x in range(100) for y in range(100)] - self.assertEqual(before, after) + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(31, 37): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_count = mask1.count() + + mask1.draw(mask2, offset) + + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), size1, msg) def test_zero_mask_erase(self): - sizes = ((100, 0), (0, 100), (0, 0)) + """Ensures erase correctly handles zero sized masks. - for size in sizes: - mask = pygame.mask.Mask(size) - mask2 = pygame.mask.Mask((100, 100), fill=True) - before = [mask2.get_at((x, y)) for x in range(100) for y in range(100)] - mask.erase(mask2, (0, 0)) - after = [mask2.get_at((x, y)) for x in range(100) for y in range(100)] - self.assertEqual(before, after) + Tests combinations of sized and zero sized masks. + """ + offset = (0, 0) + + for size1, size2 in zero_size_pairs(29, 23): + msg = f"size1={size1}, size2={size2}" + mask1 = pygame.mask.Mask(size1, fill=True) + mask2 = pygame.mask.Mask(size2, fill=True) + expected_count = mask1.count() + + mask1.erase(mask2, offset) + + self.assertEqual(mask1.count(), expected_count, msg) + self.assertEqual(mask1.get_size(), size1, msg) def test_zero_mask_count(self): sizes = ((100, 0), (0, 100), (0, 0)) @@ -1701,8 +5537,7 @@ class MaskTypeTest(unittest.TestCase): points = mask.outline() - self.assertListEqual(points, expected_points, - 'size={}'.format(size)) + self.assertListEqual(points, expected_points, f"size={size}") def test_zero_mask_outline__with_arg(self): """Ensures outline correctly handles zero sized masks @@ -1714,8 +5549,7 @@ class MaskTypeTest(unittest.TestCase): points = mask.outline(10) - self.assertListEqual(points, expected_points, - 'size={}'.format(size)) + self.assertListEqual(points, expected_points, f"size={size}") def test_zero_mask_convolve(self): """Ensures convolve correctly handles zero sized masks. @@ -1726,13 +5560,16 @@ class MaskTypeTest(unittest.TestCase): mask1 = pygame.mask.Mask(size1, fill=True) for size2 in ((11, 7), (81, 0), (0, 60), (0, 0)): - msg = 'sizes={}, {}'.format(size1, size2) + msg = f"sizes={size1}, {size2}" mask2 = pygame.mask.Mask(size2, fill=True) - expected_size = (max(0, size1[0] + size2[0] - 1), - max(0, size1[1] + size2[1] - 1)) + expected_size = ( + max(0, size1[0] + size2[0] - 1), + max(0, size1[1] + size2[1] - 1), + ) mask = mask1.convolve(mask2) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertIsNot(mask, mask2, msg) self.assertEqual(mask.get_size(), expected_size, msg) @@ -1749,11 +5586,12 @@ class MaskTypeTest(unittest.TestCase): mask2 = pygame.mask.Mask(size2, fill=True) for output_size in ((7, 5), (71, 0), (0, 70), (0, 0)): - msg = 'sizes={}, {}, {}'.format(size1, size2, output_size) + msg = f"sizes={size1}, {size2}, {output_size}" output_mask = pygame.mask.Mask(output_size) mask = mask1.convolve(mask2, output_mask) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertIs(mask, output_mask, msg) self.assertEqual(mask.get_size(), output_size, msg) @@ -1762,13 +5600,14 @@ class MaskTypeTest(unittest.TestCase): expected_count = 0 for size in ((81, 0), (0, 80), (0, 0)): + msg = f"size={size}" mask = pygame.mask.Mask(size) cc_mask = mask.connected_component() + self.assertIsInstance(cc_mask, pygame.mask.Mask, msg) self.assertEqual(cc_mask.get_size(), size) - self.assertEqual(cc_mask.count(), expected_count, - 'size={}'.format(size)) + self.assertEqual(cc_mask.count(), expected_count, msg) def test_zero_mask_connected_component__indexed(self): """Ensures connected_component correctly handles zero sized masks @@ -1788,8 +5627,7 @@ class MaskTypeTest(unittest.TestCase): cc_masks = mask.connected_components() - self.assertListEqual(cc_masks, expected_cc_masks, - 'size={}'.format(size)) + self.assertListEqual(cc_masks, expected_cc_masks, f"size={size}") def test_zero_mask_get_bounding_rects(self): """Ensures get_bounding_rects correctly handles zero sized masks.""" @@ -1800,14 +5638,436 @@ class MaskTypeTest(unittest.TestCase): bounding_rects = mask.get_bounding_rects() - self.assertListEqual(bounding_rects, expected_bounding_rects, - 'size={}'.format(size)) + self.assertListEqual( + bounding_rects, expected_bounding_rects, f"size={size}" + ) + + def test_zero_mask_to_surface(self): + """Ensures to_surface correctly handles zero sized masks and surfaces.""" + mask_color = pygame.Color("blue") + surf_color = pygame.Color("red") + + for surf_size in ((7, 3), (7, 0), (0, 7), (0, 0)): + surface = pygame.Surface(surf_size, SRCALPHA, 32) + surface.fill(surf_color) + + for mask_size in ((5, 0), (0, 5), (0, 0)): + mask = pygame.mask.Mask(mask_size, fill=True) + + to_surface = mask.to_surface(surface, setcolor=mask_color) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), surf_size) + + if 0 not in surf_size: + assertSurfaceFilled(self, to_surface, surf_color) + + def test_zero_mask_to_surface__create_surface(self): + """Ensures to_surface correctly handles zero sized masks and surfaces + when it has to create a default surface. + """ + mask_color = pygame.Color("blue") + + for mask_size in ((3, 0), (0, 3), (0, 0)): + mask = pygame.mask.Mask(mask_size, fill=True) + + to_surface = mask.to_surface(setcolor=mask_color) + + self.assertIsInstance(to_surface, pygame.Surface) + self.assertEqual(to_surface.get_size(), mask_size) + + +class SubMask(pygame.mask.Mask): + """Subclass of the Mask class to help test subclassing.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_attribute = True + + +class SubMaskCopy(SubMask): + """Subclass of the Mask class to help test copying subclasses.""" + + def copy(self): + mask_copy = super().copy() + mask_copy.test_attribute = self.test_attribute + return mask_copy + + +class SubMaskDunderCopy(SubMask): + """Subclass of the Mask class to help test copying subclasses.""" + + def __copy__(self): + mask_copy = super().__copy__() + mask_copy.test_attribute = self.test_attribute + return mask_copy + +class SubMaskCopyAndDunderCopy(SubMaskDunderCopy): + """Subclass of the Mask class to help test copying subclasses.""" + def copy(self): + return super().copy() + + +class MaskSubclassTest(unittest.TestCase): + """Test subclassed Masks.""" + + def test_subclass_mask(self): + """Ensures the Mask class can be subclassed.""" + mask = SubMask((5, 3), fill=True) + + self.assertIsInstance(mask, pygame.mask.Mask) + self.assertIsInstance(mask, SubMask) + self.assertTrue(mask.test_attribute) + + def test_subclass_copy(self): + """Ensures copy works for subclassed Masks.""" + mask = SubMask((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMask) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + # No subclass attributes because copy()/__copy__() not overridden. + self.assertFalse(hasattr(mask_copy, "test_attribute")) + + def test_subclass_copy__override_copy(self): + """Ensures copy works for subclassed Masks overriding copy.""" + mask = SubMaskCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for i, mask_copy in enumerate((mask.copy(), copy.copy(mask))): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + + if 1 == i: + # No subclass attributes because __copy__() not overridden. + self.assertFalse(hasattr(mask_copy, "test_attribute")) + else: + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_copy__override_dunder_copy(self): + """Ensures copy works for subclassed Masks overriding __copy__.""" + mask = SubMaskDunderCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskDunderCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + # Calls to copy() eventually call __copy__() internally so the + # attributes will be copied. + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_copy__override_both_copy_methods(self): + """Ensures copy works for subclassed Masks overriding copy/__copy__.""" + mask = SubMaskCopyAndDunderCopy((65, 2), fill=True) + + # Test both the copy() and __copy__() methods. + for mask_copy in (mask.copy(), copy.copy(mask)): + self.assertIsInstance(mask_copy, pygame.mask.Mask) + self.assertIsInstance(mask_copy, SubMaskCopyAndDunderCopy) + self.assertIsNot(mask_copy, mask) + assertMaskEqual(self, mask_copy, mask) + self.assertTrue(mask_copy.test_attribute) + + def test_subclass_get_size(self): + """Ensures get_size works for subclassed Masks.""" + expected_size = (2, 3) + mask = SubMask(expected_size) + + size = mask.get_size() + + self.assertEqual(size, expected_size) + + def test_subclass_mask_get_rect(self): + """Ensures get_rect works for subclassed Masks.""" + expected_rect = pygame.Rect((0, 0), (65, 33)) + mask = SubMask(expected_rect.size, fill=True) + + rect = mask.get_rect() + + self.assertEqual(rect, expected_rect) + + def test_subclass_get_at(self): + """Ensures get_at works for subclassed Masks.""" + expected_bit = 1 + mask = SubMask((3, 2), fill=True) + + bit = mask.get_at((0, 0)) + + self.assertEqual(bit, expected_bit) + + def test_subclass_set_at(self): + """Ensures set_at works for subclassed Masks.""" + expected_bit = 1 + expected_count = 1 + pos = (0, 0) + mask = SubMask(fill=False, size=(4, 2)) + + mask.set_at(pos) + + self.assertEqual(mask.get_at(pos), expected_bit) + self.assertEqual(mask.count(), expected_count) + + def test_subclass_overlap(self): + """Ensures overlap works for subclassed Masks.""" + expected_pos = (0, 0) + mask_size = (2, 3) + masks = (pygame.mask.Mask(fill=True, size=mask_size), SubMask(mask_size, True)) + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_pos = mask.overlap(arg_mask, (0, 0)) + + self.assertEqual(overlap_pos, expected_pos) + + def test_subclass_overlap_area(self): + """Ensures overlap_area works for subclassed Masks.""" + mask_size = (3, 2) + expected_count = mask_size[0] * mask_size[1] + masks = (pygame.mask.Mask(fill=True, size=mask_size), SubMask(mask_size, True)) + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_count = mask.overlap_area(arg_mask, (0, 0)) + + self.assertEqual(overlap_count, expected_count) + + def test_subclass_overlap_mask(self): + """Ensures overlap_mask works for subclassed Masks.""" + expected_size = (4, 5) + expected_count = expected_size[0] * expected_size[1] + masks = ( + pygame.mask.Mask(fill=True, size=expected_size), + SubMask(expected_size, True), + ) + arg_masks = ( + pygame.mask.Mask(fill=True, size=expected_size), + SubMask(expected_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + overlap_mask = mask.overlap_mask(arg_mask, (0, 0)) + + self.assertIsInstance(overlap_mask, pygame.mask.Mask) + self.assertNotIsInstance(overlap_mask, SubMask) + self.assertEqual(overlap_mask.count(), expected_count) + self.assertEqual(overlap_mask.get_size(), expected_size) + + def test_subclass_fill(self): + """Ensures fill works for subclassed Masks.""" + mask_size = (2, 4) + expected_count = mask_size[0] * mask_size[1] + mask = SubMask(fill=False, size=mask_size) + + mask.fill() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_clear(self): + """Ensures clear works for subclassed Masks.""" + mask_size = (4, 3) + expected_count = 0 + mask = SubMask(mask_size, True) + + mask.clear() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_invert(self): + """Ensures invert works for subclassed Masks.""" + mask_size = (1, 4) + expected_count = mask_size[0] * mask_size[1] + mask = SubMask(fill=False, size=mask_size) + + mask.invert() + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_scale(self): + """Ensures scale works for subclassed Masks.""" + expected_size = (5, 2) + mask = SubMask((1, 4)) + + scaled_mask = mask.scale(expected_size) + + self.assertIsInstance(scaled_mask, pygame.mask.Mask) + self.assertNotIsInstance(scaled_mask, SubMask) + self.assertEqual(scaled_mask.get_size(), expected_size) + + def test_subclass_draw(self): + """Ensures draw works for subclassed Masks.""" + mask_size = (5, 4) + expected_count = mask_size[0] * mask_size[1] + arg_masks = ( + pygame.mask.Mask(fill=True, size=mask_size), + SubMask(mask_size, True), + ) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in (pygame.mask.Mask(mask_size), SubMask(mask_size)): + for arg_mask in arg_masks: + mask.clear() # Clear for each test. + + mask.draw(arg_mask, (0, 0)) + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_erase(self): + """Ensures erase works for subclassed Masks.""" + mask_size = (3, 4) + expected_count = 0 + masks = (pygame.mask.Mask(mask_size, True), SubMask(mask_size, True)) + arg_masks = (pygame.mask.Mask(mask_size, True), SubMask(mask_size, True)) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in masks: + for arg_mask in arg_masks: + mask.fill() # Fill for each test. + + mask.erase(arg_mask, (0, 0)) + + self.assertEqual(mask.count(), expected_count) + + def test_subclass_count(self): + """Ensures count works for subclassed Masks.""" + mask_size = (5, 2) + expected_count = mask_size[0] * mask_size[1] - 1 + mask = SubMask(fill=True, size=mask_size) + mask.set_at((1, 1), 0) + + count = mask.count() + + self.assertEqual(count, expected_count) + + def test_subclass_centroid(self): + """Ensures centroid works for subclassed Masks.""" + expected_centroid = (0, 0) + mask_size = (3, 2) + mask = SubMask((3, 2)) + + centroid = mask.centroid() + + self.assertEqual(centroid, expected_centroid) + + def test_subclass_angle(self): + """Ensures angle works for subclassed Masks.""" + expected_angle = 0.0 + mask = SubMask(size=(5, 4)) + + angle = mask.angle() + + self.assertAlmostEqual(angle, expected_angle) + + def test_subclass_outline(self): + """Ensures outline works for subclassed Masks.""" + expected_outline = [] + mask = SubMask((3, 4)) + + outline = mask.outline() + + self.assertListEqual(outline, expected_outline) + + def test_subclass_convolve(self): + """Ensures convolve works for subclassed Masks.""" + width, height = 7, 5 + mask_size = (width, height) + expected_count = 0 + expected_size = (max(0, width * 2 - 1), max(0, height * 2 - 1)) + + arg_masks = (pygame.mask.Mask(mask_size), SubMask(mask_size)) + output_masks = (pygame.mask.Mask(mask_size), SubMask(mask_size)) + + # Test different combinations of subclassed and non-subclassed Masks. + for mask in (pygame.mask.Mask(mask_size), SubMask(mask_size)): + for arg_mask in arg_masks: + convolve_mask = mask.convolve(arg_mask) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertNotIsInstance(convolve_mask, SubMask) + self.assertEqual(convolve_mask.count(), expected_count) + self.assertEqual(convolve_mask.get_size(), expected_size) + + # Test subclassed masks for the output_mask as well. + for output_mask in output_masks: + convolve_mask = mask.convolve(arg_mask, output_mask) + + self.assertIsInstance(convolve_mask, pygame.mask.Mask) + self.assertEqual(convolve_mask.count(), expected_count) + self.assertEqual(convolve_mask.get_size(), mask_size) + + if isinstance(output_mask, SubMask): + self.assertIsInstance(convolve_mask, SubMask) + else: + self.assertNotIsInstance(convolve_mask, SubMask) + + def test_subclass_connected_component(self): + """Ensures connected_component works for subclassed Masks.""" + expected_count = 0 + expected_size = (3, 4) + mask = SubMask(expected_size) + + cc_mask = mask.connected_component() + + self.assertIsInstance(cc_mask, pygame.mask.Mask) + self.assertNotIsInstance(cc_mask, SubMask) + self.assertEqual(cc_mask.count(), expected_count) + self.assertEqual(cc_mask.get_size(), expected_size) + + def test_subclass_connected_components(self): + """Ensures connected_components works for subclassed Masks.""" + expected_ccs = [] + mask = SubMask((5, 4)) + + ccs = mask.connected_components() + + self.assertListEqual(ccs, expected_ccs) + + def test_subclass_get_bounding_rects(self): + """Ensures get_bounding_rects works for subclassed Masks.""" + expected_bounding_rects = [] + mask = SubMask((3, 2)) + + bounding_rects = mask.get_bounding_rects() + + self.assertListEqual(bounding_rects, expected_bounding_rects) + + def test_subclass_to_surface(self): + """Ensures to_surface works for subclassed Masks.""" + expected_color = pygame.Color("blue") + size = (5, 3) + mask = SubMask(size, fill=True) + surface = pygame.Surface(size, SRCALPHA, 32) + surface.fill(pygame.Color("red")) + + to_surface = mask.to_surface(surface, setcolor=expected_color) + + self.assertIs(to_surface, surface) + self.assertEqual(to_surface.get_size(), size) + assertSurfaceFilled(self, to_surface, expected_color) + + +@unittest.skipIf(IS_PYPY, "pypy has lots of mask failures") # TODO class MaskModuleTest(unittest.TestCase): - # The @unittest.expectedFailure decorator can be removed when issue #897 - # is fixed. - @unittest.expectedFailure def test_from_surface(self): """Ensures from_surface creates a mask with the correct bits set. @@ -1838,20 +6098,21 @@ class MaskModuleTest(unittest.TestCase): # Test the mask created at threshold values low, high and # around alpha. - threshold_test_values = set( - [-1, 0, alpha - 1, alpha, alpha + 1, 255, 256]) + threshold_test_values = {-1, 0, alpha - 1, alpha, alpha + 1, 255, 256} for threshold in threshold_test_values: - msg = 'depth={}, alpha={}, threshold={}'.format( - depth, alpha, threshold) + msg = f"depth={depth}, alpha={alpha}, threshold={threshold}" if alpha > threshold: expected_count = all_set_count else: expected_count = none_set_count - mask = pygame.mask.from_surface(surface, threshold) + mask = pygame.mask.from_surface( + surface=surface, threshold=threshold + ) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertEqual(mask.get_size(), expected_size, msg) self.assertEqual(mask.count(), expected_count, msg) @@ -1880,20 +6141,19 @@ class MaskModuleTest(unittest.TestCase): # Test the mask created for each different alpha threshold. for threshold in range(threshold_count): - msg = 'threshold={}'.format(threshold) + msg = f"threshold={threshold}" expected_mask.set_at((threshold, 0), 0) expected_count = expected_mask.count() mask = pygame.mask.from_surface(surface, threshold) + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertEqual(mask.get_size(), expected_size, msg) self.assertEqual(mask.count(), expected_count, msg) - self.assertEqual(mask.overlap_area(expected_mask, offset), - expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) - # The @unittest.expectedFailure decorator can be removed when issue #897 - # is fixed. - @unittest.expectedFailure def test_from_surface__different_alphas_16bit(self): """Ensures from_surface creates a mask with the correct bits set when pixels have different alpha values (16 bit surfaces). @@ -1967,55 +6227,150 @@ class MaskModuleTest(unittest.TestCase): expected_count = expected_mask.count() for threshold in range(from_threshold, to_threshold): - msg = 'threshold={}'.format(threshold) + msg = f"threshold={threshold}" mask = pygame.mask.from_surface(surface, threshold) + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_surface__with_colorkey_mask_cleared(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with the colorkey color so the resulting masks + are expected to have no bits set. + """ + colorkeys = ((0, 0, 0), (1, 2, 3), (50, 100, 200), (255, 255, 255)) + expected_size = (7, 11) + expected_count = 0 + + for depth in (8, 16, 24, 32): + msg = f"depth={depth}" + surface = pygame.Surface(expected_size, 0, depth) + + for colorkey in colorkeys: + surface.set_colorkey(colorkey) + # With some depths (i.e. 8 and 16) the actual colorkey can be + # different than what was requested via the set. + surface.fill(surface.get_colorkey()) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) self.assertEqual(mask.get_size(), expected_size, msg) self.assertEqual(mask.count(), expected_count, msg) - self.assertEqual(mask.overlap_area(expected_mask, offset), - expected_count, msg) - def todo_test_from_surface__with_colorkey(self): + def test_from_surface__with_colorkey_mask_filled(self): """Ensures from_surface creates a mask with the correct bits set when the surface uses a colorkey. + + The surface is filled with a color that is not the colorkey color so + the resulting masks are expected to have all bits set. """ - self.fail() + colorkeys = ((0, 0, 0), (1, 2, 3), (10, 100, 200), (255, 255, 255)) + surface_color = (50, 100, 200) + expected_size = (11, 7) + expected_count = expected_size[0] * expected_size[1] - def test_from_threshold(self): - """ Does mask.from_threshold() work correctly? + for depth in (8, 16, 24, 32): + msg = f"depth={depth}" + surface = pygame.Surface(expected_size, 0, depth) + surface.fill(surface_color) + + for colorkey in colorkeys: + surface.set_colorkey(colorkey) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + + def test_from_surface__with_colorkey_mask_pattern(self): + """Ensures from_surface creates a mask with the correct bits set + when the surface uses a colorkey. + + The surface is filled with alternating pixels of colorkey and + non-colorkey colors, so the resulting masks are expected to have + alternating bits set. """ + def alternate(func, set_value, unset_value, width, height): + # Helper function to set alternating values. + setbit = False + for pos in ((x, y) for x in range(width) for y in range(height)): + func(pos, set_value if setbit else unset_value) + setbit = not setbit + + surface_color = (5, 10, 20) + colorkey = (50, 60, 70) + expected_size = (11, 2) + expected_mask = pygame.mask.Mask(expected_size) + alternate(expected_mask.set_at, 1, 0, *expected_size) + expected_count = expected_mask.count() + offset = (0, 0) + + for depth in (8, 16, 24, 32): + msg = f"depth={depth}" + surface = pygame.Surface(expected_size, 0, depth) + # Fill the surface with alternating colors. + alternate(surface.set_at, surface_color, colorkey, *expected_size) + surface.set_colorkey(colorkey) + + mask = pygame.mask.from_surface(surface) + + self.assertIsInstance(mask, pygame.mask.Mask, msg) + self.assertEqual(mask.get_size(), expected_size, msg) + self.assertEqual(mask.count(), expected_count, msg) + self.assertEqual( + mask.overlap_area(expected_mask, offset), expected_count, msg + ) + + def test_from_threshold(self): + """Does mask.from_threshold() work correctly?""" + a = [16, 24, 32] for i in a: - surf = pygame.surface.Surface((70,70), 0, i) - surf.fill((100,50,200),(20,20,20,20)) - mask = pygame.mask.from_threshold(surf,(100,50,200,255),(10,10,10,255)) + surf = pygame.surface.Surface((70, 70), 0, i) + surf.fill((100, 50, 200), (20, 20, 20, 20)) + mask = pygame.mask.from_threshold( + surf, (100, 50, 200, 255), (10, 10, 10, 255) + ) rects = mask.get_bounding_rects() self.assertEqual(mask.count(), 400) - self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((20,20,20,20))]) + self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((20, 20, 20, 20))]) for i in a: - surf = pygame.surface.Surface((70,70), 0, i) - surf2 = pygame.surface.Surface((70,70), 0, i) - surf.fill((100,100,100)) - surf2.fill((150,150,150)) - surf2.fill((100,100,100), (40,40,10,10)) - mask = pygame.mask.from_threshold(surf, (0,0,0,0), (10,10,10,255), surf2) - + surf = pygame.surface.Surface((70, 70), 0, i) + surf2 = pygame.surface.Surface((70, 70), 0, i) + surf.fill((100, 100, 100)) + surf2.fill((150, 150, 150)) + surf2.fill((100, 100, 100), (40, 40, 10, 10)) + mask = pygame.mask.from_threshold( + surface=surf, + color=(0, 0, 0, 0), + threshold=(10, 10, 10, 255), + othersurface=surf2, + ) + + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), 100) - self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((40,40,10,10))]) + self.assertEqual(mask.get_bounding_rects(), [pygame.Rect((40, 40, 10, 10))]) def test_zero_size_from_surface(self): """Ensures from_surface can create masks from zero sized surfaces.""" for size in ((100, 0), (0, 100), (0, 0)): mask = pygame.mask.from_surface(pygame.Surface(size)) - self.assertIsInstance(mask, pygame.mask.MaskType, - 'size={}'.format(size)) + self.assertIsInstance(mask, pygame.mask.MaskType, f"size={size}") self.assertEqual(mask.get_size(), size) def test_zero_size_from_threshold(self): @@ -2026,7 +6381,9 @@ class MaskModuleTest(unittest.TestCase): for i in a: surf = pygame.surface.Surface(size, 0, i) surf.fill((100, 50, 200), (20, 20, 20, 20)) - mask = pygame.mask.from_threshold(surf, (100, 50, 200, 255), (10, 10, 10, 255)) + mask = pygame.mask.from_threshold( + surf, (100, 50, 200, 255), (10, 10, 10, 255) + ) self.assertEqual(mask.count(), 0) @@ -2039,12 +6396,46 @@ class MaskModuleTest(unittest.TestCase): surf.fill((100, 100, 100)) surf2.fill((150, 150, 150)) surf2.fill((100, 100, 100), (40, 40, 10, 10)) - mask = pygame.mask.from_threshold(surf, (0, 0, 0, 0), (10, 10, 10, 255), surf2) + mask = pygame.mask.from_threshold( + surf, (0, 0, 0, 0), (10, 10, 10, 255), surf2 + ) + self.assertIsInstance(mask, pygame.mask.Mask) self.assertEqual(mask.count(), 0) rects = mask.get_bounding_rects() self.assertEqual(rects, []) -if __name__ == '__main__': + def test_buffer_interface(self): + size = (1000, 100) + pixels_set = ((0, 1), (100, 10), (173, 90)) + pixels_unset = ((0, 0), (101, 10), (173, 91)) + + mask = pygame.Mask(size) + for point in pixels_set: + mask.set_at(point, 1) + + view = memoryview(mask) + intwidth = 8 * view.strides[1] + + for point in pixels_set: + x, y = point + col = x // intwidth + self.assertEqual( + (view[col, y] >> (x % intwidth)) & 1, + 1, + f"the pixel at {point} is not set to 1", + ) + + for point in pixels_unset: + x, y = point + col = x // intwidth + self.assertEqual( + (view[col, y] >> (x % intwidth)) & 1, + 0, + f"the pixel at {point} is not set to 0", + ) + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/math_test.py b/venv/Lib/site-packages/pygame/tests/math_test.py index 07a1dee52ff95a647db4bb8e06b48a2eda931557..cdb20b1dd2f3833d571c661a148dea83cc226904 100644 --- a/venv/Lib/site-packages/pygame/tests/math_test.py +++ b/venv/Lib/site-packages/pygame/tests/math_test.py @@ -1,21 +1,83 @@ -# -*- coding: utf-8 -*- -import sys -import unittest import math -from time import clock import platform +import unittest +from collections.abc import Collection, Sequence import pygame.math from pygame.math import Vector2, Vector3 -IS_PYPY = 'PyPy' == platform.python_implementation() -PY3 = sys.version_info.major == 3 +IS_PYPY = "PyPy" == platform.python_implementation() -class Vector2TypeTest(unittest.TestCase): +class MathModuleTest(unittest.TestCase): + """Math module tests.""" + def test_lerp(self): + result = pygame.math.lerp(10, 100, 0.5) # 55.0 + self.assertAlmostEqual(result, 55.0) + + result = pygame.math.lerp(10, 100, 0.0) # 10 + self.assertAlmostEqual(result, 10.0) + + result = pygame.math.lerp(10, 100, 1.0) # 100 + self.assertAlmostEqual(result, 100.0) + + # Not enough args + self.assertRaises(TypeError, pygame.math.lerp, 1) + + # Wrong arg type + self.assertRaises(TypeError, pygame.math.lerp, "str", "str", "str") + + # Percent outside range [0, 1] + self.assertRaises(ValueError, pygame.math.lerp, 10, 100, 1.1) + self.assertRaises(ValueError, pygame.math.lerp, 10, 100, -0.5) + + def test_clamp(self): + """Test clamp function.""" + + # Int tests + # Test going above max + result = pygame.math.clamp(10, 1, 5) + self.assertEqual(result, 5) + # Test going below min + result = pygame.math.clamp(-10, 1, 5) + self.assertEqual(result, 1) + # Test equal to max + result = pygame.math.clamp(5, 1, 5) + self.assertEqual(result, 5) + # Test equal to min + result = pygame.math.clamp(1, 1, 5) + self.assertEqual(result, 1) + # Test between min and max + result = pygame.math.clamp(3, 1, 5) + self.assertEqual(result, 3) + + # Float tests + # Test going above max + result = pygame.math.clamp(10.0, 1.12, 5.0) + self.assertAlmostEqual(result, 5.0) + # Test going below min + result = pygame.math.clamp(-10.0, 1.12, 5.0) + self.assertAlmostEqual(result, 1.12) + # Test equal to max + result = pygame.math.clamp(5.0, 1.12, 5.0) + self.assertAlmostEqual(result, 5.0) + # Test equal to min + result = pygame.math.clamp(1.12, 1.12, 5.0) + self.assertAlmostEqual(result, 1.12) + # Test between min and max + result = pygame.math.clamp(2.5, 1.12, 5.0) + self.assertAlmostEqual(result, 2.5) + + # Error tests + # Not enough args + self.assertRaises(TypeError, pygame.math.clamp, 10) + # Non numeric args + self.assertRaises(TypeError, pygame.math.clamp, "hello", "py", "thon") + + +class Vector2TypeTest(unittest.TestCase): def setUp(self): - pygame.math.enable_swizzling() self.zeroVec = Vector2() self.e1 = Vector2(1, 0) self.e2 = Vector2(0, 1) @@ -28,28 +90,25 @@ class Vector2TypeTest(unittest.TestCase): self.s1 = 5.6 self.s2 = 7.8 - def tearDown(self): - pygame.math.enable_swizzling() - def testConstructionDefault(self): v = Vector2() - self.assertEqual(v.x, 0.) - self.assertEqual(v.y, 0.) + self.assertEqual(v.x, 0.0) + self.assertEqual(v.y, 0.0) def testConstructionScalar(self): v = Vector2(1) - self.assertEqual(v.x, 1.) - self.assertEqual(v.y, 1.) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) def testConstructionScalarKeywords(self): v = Vector2(x=1) - self.assertEqual(v.x, 1.) - self.assertEqual(v.y, 1.) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) def testConstructionKeywords(self): v = Vector2(x=1, y=2) - self.assertEqual(v.x, 1.) - self.assertEqual(v.y, 2.) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 2.0) def testConstructionXY(self): v = Vector2(1.2, 3.4) @@ -71,7 +130,7 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(v.x, 1.2) self.assertEqual(v.y, 3.4) - def testAttributAccess(self): + def testAttributeAccess(self): tmp = self.v1.x self.assertEqual(tmp, self.v1.x) self.assertEqual(tmp, self.v1[0]) @@ -82,21 +141,210 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(self.v1.x, 3.141) self.v1.y = 3.141 self.assertEqual(self.v1.y, 3.141) + def assign_nonfloat(): v = Vector2() v.x = "spam" + self.assertRaises(TypeError, assign_nonfloat) + def test___round___basic(self): + self.assertEqual(round(pygame.Vector2(0.0, 0.0)), pygame.Vector2(0.0, 0.0)) + self.assertEqual(type(round(pygame.Vector2(0.0, 0.0))), pygame.Vector2) + self.assertEqual( + round(pygame.Vector2(1.0, 1.0)), round(pygame.Vector2(1.0, 1.0)) + ) + self.assertEqual( + round(pygame.Vector2(10.0, 10.0)), round(pygame.Vector2(10.0, 10.0)) + ) + self.assertEqual( + round(pygame.Vector2(1000000000.0, 1000000000.0)), + pygame.Vector2(1000000000.0, 1000000000.0), + ) + self.assertEqual(round(pygame.Vector2(1e20, 1e20)), pygame.Vector2(1e20, 1e20)) + + self.assertEqual(round(pygame.Vector2(-1.0, -1.0)), pygame.Vector2(-1.0, -1.0)) + self.assertEqual( + round(pygame.Vector2(-10.0, -10.0)), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector2(-1000000000.0, -1000000000.0)), + pygame.Vector2(-1000000000.0, -1000000000.0), + ) + self.assertEqual( + round(pygame.Vector2(-1e20, -1e20)), pygame.Vector2(-1e20, -1e20) + ) + + self.assertEqual(round(pygame.Vector2(0.1, 0.1)), pygame.Vector2(0.0, 0.0)) + self.assertEqual(round(pygame.Vector2(1.1, 1.1)), pygame.Vector2(1.0, 1.0)) + self.assertEqual(round(pygame.Vector2(10.1, 10.1)), pygame.Vector2(10.0, 10.0)) + self.assertEqual( + round(pygame.Vector2(1000000000.1, 1000000000.1)), + pygame.Vector2(1000000000.0, 1000000000.0), + ) + + self.assertEqual(round(pygame.Vector2(-1.1, -1.1)), pygame.Vector2(-1.0, -1.0)) + self.assertEqual( + round(pygame.Vector2(-10.1, -10.1)), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector2(-1000000000.1, -1000000000.1)), + pygame.Vector2(-1000000000.0, -1000000000.0), + ) + + self.assertEqual(round(pygame.Vector2(0.9, 0.9)), pygame.Vector2(1.0, 1.0)) + self.assertEqual(round(pygame.Vector2(9.9, 9.9)), pygame.Vector2(10.0, 10.0)) + self.assertEqual( + round(pygame.Vector2(999999999.9, 999999999.9)), + pygame.Vector2(1000000000.0, 1000000000.0), + ) + + self.assertEqual(round(pygame.Vector2(-0.9, -0.9)), pygame.Vector2(-1.0, -1.0)) + self.assertEqual( + round(pygame.Vector2(-9.9, -9.9)), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector2(-999999999.9, -999999999.9)), + pygame.Vector2(-1000000000.0, -1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector2(-8.0, -8.0), -1), pygame.Vector2(-10.0, -10.0) + ) + self.assertEqual(type(round(pygame.Vector2(-8.0, -8.0), -1)), pygame.Vector2) + + self.assertEqual(type(round(pygame.Vector2(-8.0, -8.0), 0)), pygame.Vector2) + self.assertEqual(type(round(pygame.Vector2(-8.0, -8.0), 1)), pygame.Vector2) + + # Check even / odd rounding behaviour + self.assertEqual(round(pygame.Vector2(5.5, 5.5)), pygame.Vector2(6, 6)) + self.assertEqual(round(pygame.Vector2(5.4, 5.4)), pygame.Vector2(5.0, 5.0)) + self.assertEqual(round(pygame.Vector2(5.6, 5.6)), pygame.Vector2(6.0, 6.0)) + self.assertEqual(round(pygame.Vector2(-5.5, -5.5)), pygame.Vector2(-6, -6)) + self.assertEqual(round(pygame.Vector2(-5.4, -5.4)), pygame.Vector2(-5, -5)) + self.assertEqual(round(pygame.Vector2(-5.6, -5.6)), pygame.Vector2(-6, -6)) + + self.assertRaises(TypeError, round, pygame.Vector2(1.0, 1.0), 1.5) + self.assertRaises(TypeError, round, pygame.Vector2(1.0, 1.0), "a") + + def testCopy(self): + v_copy0 = Vector2(2004.0, 2022.0) + v_copy1 = v_copy0.copy() + self.assertEqual(v_copy0.x, v_copy1.x) + self.assertEqual(v_copy0.y, v_copy1.y) + + def test_move_towards_basic(self): + expected = Vector2(8.08, 2006.87) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, 3) + change_ip.move_towards_ip(target, 3) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_towards_max_distance(self): + expected = Vector2(12.30, 2021) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, 25) + change_ip.move_towards_ip(target, 25) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_nowhere(self): + expected = Vector2(7.22, 2004.0) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, 0) + change_ip.move_towards_ip(target, 0) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_away(self): + expected = Vector2(6.36, 2001.13) + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + change_ip = Vector2(7.22, 2004.0) + + change = origin.move_towards(target, -3) + change_ip.move_towards_ip(target, -3) + + self.assertEqual(round(change.x, 2), expected.x) + self.assertEqual(round(change.y, 2), expected.y) + self.assertEqual(round(change_ip.x, 2), expected.x) + self.assertEqual(round(change_ip.y, 2), expected.y) + + def test_move_towards_self(self): + vec = Vector2(6.36, 2001.13) + vec2 = vec.copy() + for dist in (-3.54, -1, 0, 0.234, 12): + self.assertEqual(vec.move_towards(vec2, dist), vec) + vec2.move_towards_ip(vec, dist) + self.assertEqual(vec, vec2) + + def test_move_towards_errors(self): + def overpopulate(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards(target, 3, 2) + + def overpopulate_ip(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards_ip(target, 3, 2) + + def invalid_types1(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards(target, "novial") + + def invalid_types_ip1(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards_ip(target, "is") + + def invalid_types2(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards("kinda", 3) + + def invalid_types_ip2(): + origin = Vector2(7.22, 2004.0) + target = Vector2(12.30, 2021.0) + origin.move_towards_ip("cool", 3) + + self.assertRaises(TypeError, overpopulate) + self.assertRaises(TypeError, overpopulate_ip) + self.assertRaises(TypeError, invalid_types1) + self.assertRaises(TypeError, invalid_types_ip1) + self.assertRaises(TypeError, invalid_types2) + self.assertRaises(TypeError, invalid_types_ip2) + def testSequence(self): v = Vector2(1.2, 3.4) Vector2()[:] self.assertEqual(len(v), 2) self.assertEqual(v[0], 1.2) self.assertEqual(v[1], 3.4) - self.assertRaises(IndexError, lambda : v[2]) + self.assertRaises(IndexError, lambda: v[2]) self.assertEqual(v[-1], 3.4) self.assertEqual(v[-2], 1.2) - self.assertRaises(IndexError, lambda : v[-3]) + self.assertRaises(IndexError, lambda: v[-3]) self.assertEqual(v[:], [1.2, 3.4]) self.assertEqual(v[1:], [3.4]) self.assertEqual(v[:1], [1.2]) @@ -109,17 +357,23 @@ class Vector2TypeTest(unittest.TestCase): v[:] = [9.1, 11.12] self.assertEqual(v.x, 9.1) self.assertEqual(v.y, 11.12) + def overpopulate(): v = Vector2() v[:] = [1, 2, 3] + self.assertRaises(ValueError, overpopulate) + def underpopulate(): v = Vector2() v[:] = [1] + self.assertRaises(ValueError, underpopulate) + def assign_nonfloat(): v = Vector2() v[0] = "spam" + self.assertRaises(TypeError, assign_nonfloat) def testExtendedSlicing(self): @@ -131,6 +385,7 @@ class Vector2TypeTest(unittest.TestCase): del vec[start::step] elif start is None and stop is None and step is not None: del vec[::step] + v = Vector2(self.v1) self.assertRaises(TypeError, delSlice, v, None, None, 2) self.assertRaises(TypeError, delSlice, v, 1, None, 2) @@ -150,7 +405,7 @@ class Vector2TypeTest(unittest.TestCase): b = Vector2(self.v1) c = Vector2(self.v1) a[1:2] = [2.2] - b[slice(1,2)] = [2.2] + b[slice(1, 2)] = [2.2] c[1:2:] = (2.2,) self.assertEqual(a, b) self.assertEqual(a, c) @@ -158,6 +413,19 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(type(b), type(self.v1)) self.assertEqual(type(c), type(self.v1)) + def test_contains(self): + c = Vector2(0, 1) + + # call __contains__ explicitly to test that it is defined + self.assertTrue(c.__contains__(0)) + self.assertTrue(0 in c) + self.assertTrue(1 in c) + self.assertTrue(2 not in c) + self.assertFalse(c.__contains__(2)) + + self.assertRaises(TypeError, lambda: "string" in c) + self.assertRaises(TypeError, lambda: 3 + 4j in c) + def testAdd(self): v3 = self.v1 + self.v2 self.assertTrue(isinstance(v3, type(self.v1))) @@ -267,18 +535,15 @@ class Vector2TypeTest(unittest.TestCase): def testIter(self): it = self.v1.__iter__() - if PY3: - next_ = it.__next__ - else: - next_ = it.next + next_ = it.__next__ self.assertEqual(next_(), self.v1[0]) self.assertEqual(next_(), self.v1[1]) - self.assertRaises(StopIteration, lambda : next_()) + self.assertRaises(StopIteration, lambda: next_()) it1 = self.v1.__iter__() it2 = self.v1.__iter__() self.assertNotEqual(id(it1), id(it2)) self.assertEqual(id(it1), id(it1.__iter__())) - self.assertEqual(list(it1), list(it2)); + self.assertEqual(list(it1), list(it2)) self.assertEqual(list(self.v1.__iter__()), self.l1) idx = 0 for val in self.v1: @@ -308,6 +573,19 @@ class Vector2TypeTest(unittest.TestCase): # issue 214 self.assertEqual(Vector2(0, 1).rotate(359.99999999), Vector2(0, 1)) + def test_rotate_rad(self): + tests = ( + ((1, 0), math.pi), + ((1, 0), math.pi / 2), + ((1, 0), -math.pi / 2), + ((1, 0), math.pi / 4), + ) + for initialVec, radians in tests: + self.assertEqual( + Vector2(initialVec).rotate_rad(radians), + (math.cos(radians), math.sin(radians)), + ) + def test_rotate_ip(self): v = Vector2(1, 0) self.assertEqual(v.rotate_ip(90), None) @@ -318,28 +596,40 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(v.x, -1) self.assertEqual(v.y, 1) + def test_rotate_rad_ip(self): + tests = ( + ((1, 0), math.pi), + ((1, 0), math.pi / 2), + ((1, 0), -math.pi / 2), + ((1, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector2(initialVec) + vec.rotate_rad_ip(radians) + self.assertEqual(vec, (math.cos(radians), math.sin(radians))) + def test_normalize(self): v = self.v1.normalize() # length is 1 - self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.) + self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.0) # v1 is unchanged self.assertEqual(self.v1.x, self.l1[0]) self.assertEqual(self.v1.y, self.l1[1]) - # v2 is paralell to v1 - self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.) - self.assertRaises(ValueError, lambda : self.zeroVec.normalize()) + # v2 is parallel to v1 + self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize()) def test_normalize_ip(self): v = +self.v1 # v has length != 1 before normalizing - self.assertNotEqual(v.x * v.x + v.y * v.y, 1.) + self.assertNotEqual(v.x * v.x + v.y * v.y, 1.0) # inplace operations should return None self.assertEqual(v.normalize_ip(), None) # length is 1 - self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.) - # v2 is paralell to v1 - self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.) - self.assertRaises(ValueError, lambda : self.zeroVec.normalize_ip()) + self.assertAlmostEqual(v.x * v.x + v.y * v.y, 1.0) + # v2 is parallel to v1 + self.assertAlmostEqual(self.v1.x * v.y - self.v1.y * v.x, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize_ip()) def test_is_normalized(self): self.assertEqual(self.v1.is_normalized(), False) @@ -349,28 +639,35 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(self.zeroVec.is_normalized(), False) def test_cross(self): - self.assertEqual(self.v1.cross(self.v2), - self.v1.x * self.v2.y - self.v1.y * self.v2.x) - self.assertEqual(self.v1.cross(self.l2), - self.v1.x * self.l2[1] - self.v1.y * self.l2[0]) - self.assertEqual(self.v1.cross(self.t2), - self.v1.x * self.t2[1] - self.v1.y * self.t2[0]) + self.assertEqual( + self.v1.cross(self.v2), self.v1.x * self.v2.y - self.v1.y * self.v2.x + ) + self.assertEqual( + self.v1.cross(self.l2), self.v1.x * self.l2[1] - self.v1.y * self.l2[0] + ) + self.assertEqual( + self.v1.cross(self.t2), self.v1.x * self.t2[1] - self.v1.y * self.t2[0] + ) self.assertEqual(self.v1.cross(self.v2), -self.v2.cross(self.v1)) self.assertEqual(self.v1.cross(self.v1), 0) def test_dot(self): - self.assertAlmostEqual(self.v1.dot(self.v2), - self.v1.x * self.v2.x + self.v1.y * self.v2.y) - self.assertAlmostEqual(self.v1.dot(self.l2), - self.v1.x * self.l2[0] + self.v1.y * self.l2[1]) - self.assertAlmostEqual(self.v1.dot(self.t2), - self.v1.x * self.t2[0] + self.v1.y * self.t2[1]) + self.assertAlmostEqual( + self.v1.dot(self.v2), self.v1.x * self.v2.x + self.v1.y * self.v2.y + ) + self.assertAlmostEqual( + self.v1.dot(self.l2), self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + ) + self.assertAlmostEqual( + self.v1.dot(self.t2), self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + ) self.assertEqual(self.v1.dot(self.v2), self.v2.dot(self.v1)) self.assertEqual(self.v1.dot(self.v2), self.v1 * self.v2) def test_angle_to(self): - self.assertEqual(self.v1.rotate(self.v1.angle_to(self.v2)).normalize(), - self.v2.normalize()) + self.assertEqual( + self.v1.rotate(self.v1.angle_to(self.v2)).normalize(), self.v2.normalize() + ) self.assertEqual(Vector2(1, 1).angle_to((-1, 1)), 90) self.assertEqual(Vector2(1, 0).angle_to((0, -1)), -90) self.assertEqual(Vector2(1, 0).angle_to((-1, 1)), 135) @@ -380,7 +677,7 @@ class Vector2TypeTest(unittest.TestCase): v = Vector2(1, 1) v.scale_to_length(2.5) self.assertEqual(v, Vector2(2.5, 2.5) / math.sqrt(2)) - self.assertRaises(ValueError, lambda : self.zeroVec.scale_to_length(1)) + self.assertRaises(ValueError, lambda: self.zeroVec.scale_to_length(1)) self.assertEqual(v.scale_to_length(0), None) self.assertEqual(v, self.zeroVec) @@ -398,9 +695,9 @@ class Vector2TypeTest(unittest.TestCase): v = Vector2(1, -1) n = Vector2(0, 1) self.assertEqual(v.reflect(n), Vector2(1, 1)) - self.assertEqual(v.reflect(3*n), v.reflect(n)) + self.assertEqual(v.reflect(3 * n), v.reflect(n)) self.assertEqual(v.reflect(-v), -v) - self.assertRaises(ValueError, lambda : v.reflect(self.zeroVec)) + self.assertRaises(ValueError, lambda: v.reflect(self.zeroVec)) def test_reflect_ip(self): v1 = Vector2(1, -1) @@ -409,30 +706,60 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(v2.reflect_ip(n), None) self.assertEqual(v2, Vector2(1, 1)) v2 = Vector2(v1) - v2.reflect_ip(3*n) + v2.reflect_ip(3 * n) self.assertEqual(v2, v1.reflect(n)) v2 = Vector2(v1) v2.reflect_ip(-v1) self.assertEqual(v2, -v1) - self.assertRaises(ValueError, lambda : v2.reflect_ip(Vector2())) + self.assertRaises(ValueError, lambda: v2.reflect_ip(Vector2())) def test_distance_to(self): diff = self.v1 - self.v2 self.assertEqual(self.e1.distance_to(self.e2), math.sqrt(2)) - self.assertAlmostEqual(self.v1.distance_to(self.v2), - math.sqrt(diff.x * diff.x + diff.y * diff.y)) + self.assertEqual(self.e1.distance_to((0, 1)), math.sqrt(2)) + self.assertEqual(self.e1.distance_to([0, 1]), math.sqrt(2)) + self.assertAlmostEqual( + self.v1.distance_to(self.v2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) + self.assertAlmostEqual( + self.v1.distance_to(self.t2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) + self.assertAlmostEqual( + self.v1.distance_to(self.l2), math.sqrt(diff.x * diff.x + diff.y * diff.y) + ) self.assertEqual(self.v1.distance_to(self.v1), 0) - self.assertEqual(self.v1.distance_to(self.v2), - self.v2.distance_to(self.v1)) + self.assertEqual(self.v1.distance_to(self.t1), 0) + self.assertEqual(self.v1.distance_to(self.l1), 0) + self.assertEqual(self.v1.distance_to(self.t2), self.v2.distance_to(self.t1)) + self.assertEqual(self.v1.distance_to(self.l2), self.v2.distance_to(self.l1)) + self.assertEqual(self.v1.distance_to(self.v2), self.v2.distance_to(self.v1)) def test_distance_squared_to(self): diff = self.v1 - self.v2 self.assertEqual(self.e1.distance_squared_to(self.e2), 2) - self.assertAlmostEqual(self.v1.distance_squared_to(self.v2), - diff.x * diff.x + diff.y * diff.y) + self.assertEqual(self.e1.distance_squared_to((0, 1)), 2) + self.assertEqual(self.e1.distance_squared_to([0, 1]), 2) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.v2), diff.x * diff.x + diff.y * diff.y + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.t2), diff.x * diff.x + diff.y * diff.y + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.l2), diff.x * diff.x + diff.y * diff.y + ) self.assertEqual(self.v1.distance_squared_to(self.v1), 0) - self.assertEqual(self.v1.distance_squared_to(self.v2), - self.v2.distance_squared_to(self.v1)) + self.assertEqual(self.v1.distance_squared_to(self.t1), 0) + self.assertEqual(self.v1.distance_squared_to(self.l1), 0) + self.assertEqual( + self.v1.distance_squared_to(self.v2), self.v2.distance_squared_to(self.v1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.t2), self.v2.distance_squared_to(self.t1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.l2), self.v2.distance_squared_to(self.l1) + ) def test_update(self): v = Vector2(3, 4) @@ -444,32 +771,34 @@ class Vector2TypeTest(unittest.TestCase): self.assertNotEqual(v, Vector2((5, 1))) def test_swizzle(self): - self.assertTrue(hasattr(pygame.math, "enable_swizzling")) - self.assertTrue(hasattr(pygame.math, "disable_swizzling")) - # swizzling not disabled by default - pygame.math.disable_swizzling() - self.assertRaises(AttributeError, lambda : self.v1.yx) - pygame.math.enable_swizzling() - self.assertEqual(self.v1.yx, (self.v1.y, self.v1.x)) - self.assertEqual(self.v1.xxyyxy, (self.v1.x, self.v1.x, self.v1.y, - self.v1.y, self.v1.x, self.v1.y)) + self.assertEqual( + self.v1.xxyyxy, + (self.v1.x, self.v1.x, self.v1.y, self.v1.y, self.v1.x, self.v1.y), + ) self.v1.xy = self.t2 self.assertEqual(self.v1, self.t2) self.v1.yx = self.t2 self.assertEqual(self.v1, (self.t2[1], self.t2[0])) self.assertEqual(type(self.v1), Vector2) + def invalidSwizzleX(): Vector2().xx = (1, 2) + def invalidSwizzleY(): Vector2().yy = (1, 2) + self.assertRaises(AttributeError, invalidSwizzleX) self.assertRaises(AttributeError, invalidSwizzleY) + def invalidAssignment(): Vector2().xy = 3 + self.assertRaises(TypeError, invalidAssignment) + def unicodeAttribute(): getattr(Vector2(), "ä") + self.assertRaises(AttributeError, unicodeAttribute) def test_swizzle_return_types(self): @@ -480,108 +809,6 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(type(self.v1.xyxy), tuple) self.assertEqual(type(self.v1.xyxyx), tuple) - def test_elementwise(self): - # behaviour for "elementwise op scalar" - self.assertEqual(self.v1.elementwise() + self.s1, - (self.v1.x + self.s1, self.v1.y + self.s1)) - self.assertEqual(self.v1.elementwise() - self.s1, - (self.v1.x - self.s1, self.v1.y - self.s1)) - self.assertEqual(self.v1.elementwise() * self.s2, - (self.v1.x * self.s2, self.v1.y * self.s2)) - self.assertEqual(self.v1.elementwise() / self.s2, - (self.v1.x / self.s2, self.v1.y / self.s2)) - self.assertEqual(self.v1.elementwise() // self.s1, - (self.v1.x // self.s1, self.v1.y // self.s1)) - self.assertEqual(self.v1.elementwise() ** self.s1, - (self.v1.x ** self.s1, self.v1.y ** self.s1)) - self.assertEqual(self.v1.elementwise() % self.s1, - (self.v1.x % self.s1, self.v1.y % self.s1)) - self.assertEqual(self.v1.elementwise() > self.s1, - self.v1.x > self.s1 and self.v1.y > self.s1) - self.assertEqual(self.v1.elementwise() < self.s1, - self.v1.x < self.s1 and self.v1.y < self.s1) - self.assertEqual(self.v1.elementwise() == self.s1, - self.v1.x == self.s1 and self.v1.y == self.s1) - self.assertEqual(self.v1.elementwise() != self.s1, - self.v1.x != self.s1 and self.v1.y != self.s1) - self.assertEqual(self.v1.elementwise() >= self.s1, - self.v1.x >= self.s1 and self.v1.y >= self.s1) - self.assertEqual(self.v1.elementwise() <= self.s1, - self.v1.x <= self.s1 and self.v1.y <= self.s1) - self.assertEqual(self.v1.elementwise() != self.s1, - self.v1.x != self.s1 and self.v1.y != self.s1) - # behaviour for "scalar op elementwise" - self.assertEqual(5 + self.v1.elementwise(), Vector2(5, 5) + self.v1) - self.assertEqual(3.5 - self.v1.elementwise(), Vector2(3.5, 3.5) - self.v1) - self.assertEqual(7.5 * self.v1.elementwise() , 7.5 * self.v1) - self.assertEqual(-3.5 / self.v1.elementwise(), (-3.5 / self.v1.x, -3.5 / self.v1.y)) - self.assertEqual(-3.5 // self.v1.elementwise(), (-3.5 // self.v1.x, -3.5 // self.v1.y)) - self.assertEqual(-3.5 ** self.v1.elementwise(), (-3.5 ** self.v1.x, -3.5 ** self.v1.y)) - self.assertEqual(3 % self.v1.elementwise(), (3 % self.v1.x, 3 % self.v1.y)) - self.assertEqual(2 < self.v1.elementwise(), 2 < self.v1.x and 2 < self.v1.y) - self.assertEqual(2 > self.v1.elementwise(), 2 > self.v1.x and 2 > self.v1.y) - self.assertEqual(1 == self.v1.elementwise(), 1 == self.v1.x and 1 == self.v1.y) - self.assertEqual(1 != self.v1.elementwise(), 1 != self.v1.x and 1 != self.v1.y) - self.assertEqual(2 <= self.v1.elementwise(), 2 <= self.v1.x and 2 <= self.v1.y) - self.assertEqual(-7 >= self.v1.elementwise(), -7 >= self.v1.x and -7 >= self.v1.y) - self.assertEqual(-7 != self.v1.elementwise(), -7 != self.v1.x and -7 != self.v1.y) - - # behaviour for "elementwise op vector" - self.assertEqual(type(self.v1.elementwise() * self.v2), type(self.v1)) - self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) - self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) - self.assertEqual(self.v1.elementwise() - self.v2, self.v1 - self.v2) - self.assertEqual(self.v1.elementwise() * self.v2, (self.v1.x * self.v2.x, self.v1.y * self.v2.y)) - self.assertEqual(self.v1.elementwise() / self.v2, (self.v1.x / self.v2.x, self.v1.y / self.v2.y)) - self.assertEqual(self.v1.elementwise() // self.v2, (self.v1.x // self.v2.x, self.v1.y // self.v2.y)) - self.assertEqual(self.v1.elementwise() ** self.v2, (self.v1.x ** self.v2.x, self.v1.y ** self.v2.y)) - self.assertEqual(self.v1.elementwise() % self.v2, (self.v1.x % self.v2.x, self.v1.y % self.v2.y)) - self.assertEqual(self.v1.elementwise() > self.v2, self.v1.x > self.v2.x and self.v1.y > self.v2.y) - self.assertEqual(self.v1.elementwise() < self.v2, self.v1.x < self.v2.x and self.v1.y < self.v2.y) - self.assertEqual(self.v1.elementwise() >= self.v2, self.v1.x >= self.v2.x and self.v1.y >= self.v2.y) - self.assertEqual(self.v1.elementwise() <= self.v2, self.v1.x <= self.v2.x and self.v1.y <= self.v2.y) - self.assertEqual(self.v1.elementwise() == self.v2, self.v1.x == self.v2.x and self.v1.y == self.v2.y) - self.assertEqual(self.v1.elementwise() != self.v2, self.v1.x != self.v2.x and self.v1.y != self.v2.y) - # behaviour for "vector op elementwise" - self.assertEqual(self.v2 + self.v1.elementwise(), self.v2 + self.v1) - self.assertEqual(self.v2 - self.v1.elementwise(), self.v2 - self.v1) - self.assertEqual(self.v2 * self.v1.elementwise(), (self.v2.x * self.v1.x, self.v2.y * self.v1.y)) - self.assertEqual(self.v2 / self.v1.elementwise(), (self.v2.x / self.v1.x, self.v2.y / self.v1.y)) - self.assertEqual(self.v2 // self.v1.elementwise(), (self.v2.x // self.v1.x, self.v2.y // self.v1.y)) - self.assertEqual(self.v2 ** self.v1.elementwise(), (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y)) - self.assertEqual(self.v2 % self.v1.elementwise(), (self.v2.x % self.v1.x, self.v2.y % self.v1.y)) - self.assertEqual(self.v2 < self.v1.elementwise(), self.v2.x < self.v1.x and self.v2.y < self.v1.y) - self.assertEqual(self.v2 > self.v1.elementwise(), self.v2.x > self.v1.x and self.v2.y > self.v1.y) - self.assertEqual(self.v2 <= self.v1.elementwise(), self.v2.x <= self.v1.x and self.v2.y <= self.v1.y) - self.assertEqual(self.v2 >= self.v1.elementwise(), self.v2.x >= self.v1.x and self.v2.y >= self.v1.y) - self.assertEqual(self.v2 == self.v1.elementwise(), self.v2.x == self.v1.x and self.v2.y == self.v1.y) - self.assertEqual(self.v2 != self.v1.elementwise(), self.v2.x != self.v1.x and self.v2.y != self.v1.y) - - # behaviour for "elementwise op elementwise" - self.assertEqual(self.v2.elementwise() + self.v1.elementwise(), self.v2 + self.v1) - self.assertEqual(self.v2.elementwise() - self.v1.elementwise(), self.v2 - self.v1) - self.assertEqual(self.v2.elementwise() * self.v1.elementwise(), (self.v2.x * self.v1.x, self.v2.y * self.v1.y)) - self.assertEqual(self.v2.elementwise() / self.v1.elementwise(), (self.v2.x / self.v1.x, self.v2.y / self.v1.y)) - self.assertEqual(self.v2.elementwise() // self.v1.elementwise(), (self.v2.x // self.v1.x, self.v2.y // self.v1.y)) - self.assertEqual(self.v2.elementwise() ** self.v1.elementwise(), (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y)) - self.assertEqual(self.v2.elementwise() % self.v1.elementwise(), (self.v2.x % self.v1.x, self.v2.y % self.v1.y)) - self.assertEqual(self.v2.elementwise() < self.v1.elementwise(), self.v2.x < self.v1.x and self.v2.y < self.v1.y) - self.assertEqual(self.v2.elementwise() > self.v1.elementwise(), self.v2.x > self.v1.x and self.v2.y > self.v1.y) - self.assertEqual(self.v2.elementwise() <= self.v1.elementwise(), self.v2.x <= self.v1.x and self.v2.y <= self.v1.y) - self.assertEqual(self.v2.elementwise() >= self.v1.elementwise(), self.v2.x >= self.v1.x and self.v2.y >= self.v1.y) - self.assertEqual(self.v2.elementwise() == self.v1.elementwise(), self.v2.x == self.v1.x and self.v2.y == self.v1.y) - self.assertEqual(self.v2.elementwise() != self.v1.elementwise(), self.v2.x != self.v1.x and self.v2.y != self.v1.y) - - # other behaviour - self.assertEqual(abs(self.v1.elementwise()), (abs(self.v1.x), abs(self.v1.y))) - self.assertEqual(-self.v1.elementwise(), -self.v1) - self.assertEqual(+self.v1.elementwise(), +self.v1) - self.assertEqual(bool(self.v1.elementwise()), bool(self.v1)) - self.assertEqual(bool(Vector2().elementwise()), bool(Vector2())) - self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1)) - self.assertRaises(ValueError, lambda : pow(Vector2(-1, 0).elementwise(), 1.2)) - self.assertRaises(ZeroDivisionError, lambda : self.zeroVec.elementwise() ** -1) - def test_elementwise(self): v1 = self.v1 v2 = self.v2 @@ -593,30 +820,30 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(v1.elementwise() * s2, (v1.x * s2, v1.y * s2)) self.assertEqual(v1.elementwise() / s2, (v1.x / s2, v1.y / s2)) self.assertEqual(v1.elementwise() // s1, (v1.x // s1, v1.y // s1)) - self.assertEqual(v1.elementwise() ** s1, (v1.x ** s1, v1.y ** s1)) + self.assertEqual(v1.elementwise() ** s1, (v1.x**s1, v1.y**s1)) self.assertEqual(v1.elementwise() % s1, (v1.x % s1, v1.y % s1)) self.assertEqual(v1.elementwise() > s1, v1.x > s1 and v1.y > s1) self.assertEqual(v1.elementwise() < s1, v1.x < s1 and v1.y < s1) self.assertEqual(v1.elementwise() == s1, v1.x == s1 and v1.y == s1) - self.assertEqual(v1.elementwise() != s1, v1.x != s1 and v1.y != s1) + self.assertEqual(v1.elementwise() != s1, s1 not in [v1.x, v1.y]) self.assertEqual(v1.elementwise() >= s1, v1.x >= s1 and v1.y >= s1) self.assertEqual(v1.elementwise() <= s1, v1.x <= s1 and v1.y <= s1) - self.assertEqual(v1.elementwise() != s1, v1.x != s1 and v1.y != s1) + self.assertEqual(v1.elementwise() != s1, s1 not in [v1.x, v1.y]) # behaviour for "scalar op elementwise" self.assertEqual(s1 + v1.elementwise(), (s1 + v1.x, s1 + v1.y)) self.assertEqual(s1 - v1.elementwise(), (s1 - v1.x, s1 - v1.y)) self.assertEqual(s1 * v1.elementwise(), (s1 * v1.x, s1 * v1.y)) self.assertEqual(s1 / v1.elementwise(), (s1 / v1.x, s1 / v1.y)) self.assertEqual(s1 // v1.elementwise(), (s1 // v1.x, s1 // v1.y)) - self.assertEqual(s1 ** v1.elementwise(), (s1 ** v1.x, s1 ** v1.y)) + self.assertEqual(s1 ** v1.elementwise(), (s1**v1.x, s1**v1.y)) self.assertEqual(s1 % v1.elementwise(), (s1 % v1.x, s1 % v1.y)) self.assertEqual(s1 < v1.elementwise(), s1 < v1.x and s1 < v1.y) self.assertEqual(s1 > v1.elementwise(), s1 > v1.x and s1 > v1.y) self.assertEqual(s1 == v1.elementwise(), s1 == v1.x and s1 == v1.y) - self.assertEqual(s1 != v1.elementwise(), s1 != v1.x and s1 != v1.y) + self.assertEqual(s1 != v1.elementwise(), s1 not in [v1.x, v1.y]) self.assertEqual(s1 <= v1.elementwise(), s1 <= v1.x and s1 <= v1.y) self.assertEqual(s1 >= v1.elementwise(), s1 >= v1.x and s1 >= v1.y) - self.assertEqual(s1 != v1.elementwise(), s1 != v1.x and s1 != v1.y) + self.assertEqual(s1 != v1.elementwise(), s1 not in [v1.x, v1.y]) # behaviour for "elementwise op vector" self.assertEqual(type(v1.elementwise() * v2), type(v1)) @@ -625,7 +852,7 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(v1.elementwise() * v2, (v1.x * v2.x, v1.y * v2.y)) self.assertEqual(v1.elementwise() / v2, (v1.x / v2.x, v1.y / v2.y)) self.assertEqual(v1.elementwise() // v2, (v1.x // v2.x, v1.y // v2.y)) - self.assertEqual(v1.elementwise() ** v2, (v1.x ** v2.x, v1.y ** v2.y)) + self.assertEqual(v1.elementwise() ** v2, (v1.x**v2.x, v1.y**v2.y)) self.assertEqual(v1.elementwise() % v2, (v1.x % v2.x, v1.y % v2.y)) self.assertEqual(v1.elementwise() > v2, v1.x > v2.x and v1.y > v2.y) self.assertEqual(v1.elementwise() < v2, v1.x < v2.x and v1.y < v2.y) @@ -639,7 +866,7 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(v2 * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y)) self.assertEqual(v2 / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y)) self.assertEqual(v2 // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y)) - self.assertEqual(v2 ** v1.elementwise(), (v2.x ** v1.x, v2.y ** v1.y)) + self.assertEqual(v2 ** v1.elementwise(), (v2.x**v1.x, v2.y**v1.y)) self.assertEqual(v2 % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y)) self.assertEqual(v2 < v1.elementwise(), v2.x < v1.x and v2.y < v1.y) self.assertEqual(v2 > v1.elementwise(), v2.x > v1.x and v2.y > v1.y) @@ -651,17 +878,39 @@ class Vector2TypeTest(unittest.TestCase): # behaviour for "elementwise op elementwise" self.assertEqual(v2.elementwise() + v1.elementwise(), v2 + v1) self.assertEqual(v2.elementwise() - v1.elementwise(), v2 - v1) - self.assertEqual(v2.elementwise() * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y)) - self.assertEqual(v2.elementwise() / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y)) - self.assertEqual(v2.elementwise() // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y)) - self.assertEqual(v2.elementwise() ** v1.elementwise(), (v2.x ** v1.x, v2.y ** v1.y)) - self.assertEqual(v2.elementwise() % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y)) - self.assertEqual(v2.elementwise() < v1.elementwise(), v2.x < v1.x and v2.y < v1.y) - self.assertEqual(v2.elementwise() > v1.elementwise(), v2.x > v1.x and v2.y > v1.y) - self.assertEqual(v2.elementwise() <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y) - self.assertEqual(v2.elementwise() >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y) - self.assertEqual(v2.elementwise() == v1.elementwise(), v2.x == v1.x and v2.y == v1.y) - self.assertEqual(v2.elementwise() != v1.elementwise(), v2.x != v1.x and v2.y != v1.y) + self.assertEqual( + v2.elementwise() * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y) + ) + self.assertEqual( + v2.elementwise() / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y) + ) + self.assertEqual( + v2.elementwise() // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y) + ) + self.assertEqual( + v2.elementwise() ** v1.elementwise(), (v2.x**v1.x, v2.y**v1.y) + ) + self.assertEqual( + v2.elementwise() % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y) + ) + self.assertEqual( + v2.elementwise() < v1.elementwise(), v2.x < v1.x and v2.y < v1.y + ) + self.assertEqual( + v2.elementwise() > v1.elementwise(), v2.x > v1.x and v2.y > v1.y + ) + self.assertEqual( + v2.elementwise() <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y + ) + self.assertEqual( + v2.elementwise() >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y + ) + self.assertEqual( + v2.elementwise() == v1.elementwise(), v2.x == v1.x and v2.y == v1.y + ) + self.assertEqual( + v2.elementwise() != v1.elementwise(), v2.x != v1.x and v2.y != v1.y + ) # other behaviour self.assertEqual(abs(v1.elementwise()), (abs(v1.x), abs(v1.y))) @@ -670,29 +919,34 @@ class Vector2TypeTest(unittest.TestCase): self.assertEqual(bool(v1.elementwise()), bool(v1)) self.assertEqual(bool(Vector2().elementwise()), bool(Vector2())) self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1)) - self.assertRaises(ValueError, lambda : pow(Vector2(-1, 0).elementwise(), 1.2)) - self.assertRaises(ZeroDivisionError, lambda : self.zeroVec.elementwise() ** -1) - self.assertRaises(ZeroDivisionError, lambda : self.zeroVec.elementwise() ** -1) - self.assertRaises(ZeroDivisionError, lambda : Vector2(1,1).elementwise() / 0) - self.assertRaises(ZeroDivisionError, lambda : Vector2(1,1).elementwise() // 0) - self.assertRaises(ZeroDivisionError, lambda : Vector2(1,1).elementwise() % 0) - self.assertRaises(ZeroDivisionError, lambda : Vector2(1,1).elementwise() / self.zeroVec) - self.assertRaises(ZeroDivisionError, lambda : Vector2(1,1).elementwise() // self.zeroVec) - self.assertRaises(ZeroDivisionError, lambda : Vector2(1,1).elementwise() % self.zeroVec) - self.assertRaises(ZeroDivisionError, lambda : 2 / self.zeroVec.elementwise()) - self.assertRaises(ZeroDivisionError, lambda : 2 // self.zeroVec.elementwise()) - self.assertRaises(ZeroDivisionError, lambda : 2 % self.zeroVec.elementwise()) + self.assertRaises(ValueError, lambda: pow(Vector2(-1, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() / 0) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() // 0) + self.assertRaises(ZeroDivisionError, lambda: Vector2(1, 1).elementwise() % 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() / self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() // self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector2(1, 1).elementwise() % self.zeroVec + ) + self.assertRaises(ZeroDivisionError, lambda: 2 / self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 // self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 % self.zeroVec.elementwise()) def test_slerp(self): - self.assertRaises(ValueError, lambda : self.zeroVec.slerp(self.v1, .5)) - self.assertRaises(ValueError, lambda : self.v1.slerp(self.zeroVec, .5)) - self.assertRaises(ValueError, - lambda : self.zeroVec.slerp(self.zeroVec, .5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.v1, 0.5)) + self.assertRaises(ValueError, lambda: self.v1.slerp(self.zeroVec, 0.5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.zeroVec, 0.5)) v1 = Vector2(1, 0) v2 = Vector2(0, 1) steps = 10 angle_step = v1.angle_to(v2) / steps - for i, u in ((i, v1.slerp(v2, i/float(steps))) for i in range(steps+1)): + for i, u in ((i, v1.slerp(v2, i / float(steps))) for i in range(steps + 1)): self.assertAlmostEqual(u.length(), 1) self.assertAlmostEqual(v1.angle_to(u), i * angle_step) self.assertEqual(u, v2) @@ -700,18 +954,21 @@ class Vector2TypeTest(unittest.TestCase): v1 = Vector2(100, 0) v2 = Vector2(0, 10) radial_factor = v2.length() / v1.length() - for i, u in ((i, v1.slerp(v2, -i/float(steps))) for i in range(steps+1)): - self.assertAlmostEqual(u.length(), (v2.length() - v1.length()) * (float(i)/steps) + v1.length()) + for i, u in ((i, v1.slerp(v2, -i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual( + u.length(), + (v2.length() - v1.length()) * (float(i) / steps) + v1.length(), + ) self.assertEqual(u, v2) - self.assertEqual(v1.slerp(v1, .5), v1) - self.assertEqual(v2.slerp(v2, .5), v2) - self.assertRaises(ValueError, lambda : v1.slerp(-v1, 0.5)) + self.assertEqual(v1.slerp(v1, 0.5), v1) + self.assertEqual(v2.slerp(v2, 0.5), v2) + self.assertRaises(ValueError, lambda: v1.slerp(-v1, 0.5)) def test_lerp(self): v1 = Vector2(0, 0) v2 = Vector2(10, 10) self.assertEqual(v1.lerp(v2, 0.5), (5, 5)) - self.assertRaises(ValueError, lambda : v1.lerp(v2, 2.5)) + self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) v1 = Vector2(-10, -5) v2 = Vector2(10, 10) @@ -721,19 +978,30 @@ class Vector2TypeTest(unittest.TestCase): v = Vector2() v.from_polar(self.v1.as_polar()) self.assertEqual(self.v1, v) + self.assertEqual(self.v1, Vector2.from_polar(self.v1.as_polar())) self.assertEqual(self.e1.as_polar(), (1, 0)) self.assertEqual(self.e2.as_polar(), (1, 90)) self.assertEqual((2 * self.e2).as_polar(), (2, 90)) - self.assertRaises(TypeError, lambda : v.from_polar((None, None))) - self.assertRaises(TypeError, lambda : v.from_polar("ab")) - self.assertRaises(TypeError, lambda : v.from_polar((None, 1))) - self.assertRaises(TypeError, lambda : v.from_polar((1, 2, 3))) - self.assertRaises(TypeError, lambda : v.from_polar((1,))) - self.assertRaises(TypeError, lambda : v.from_polar(1, 2)) - v.from_polar((.5, 90)) - self.assertEqual(v, .5 * self.e2) + self.assertRaises(TypeError, lambda: v.from_polar((None, None))) + self.assertRaises(TypeError, lambda: v.from_polar("ab")) + self.assertRaises(TypeError, lambda: v.from_polar((None, 1))) + self.assertRaises(TypeError, lambda: v.from_polar((1, 2, 3))) + self.assertRaises(TypeError, lambda: v.from_polar((1,))) + self.assertRaises(TypeError, lambda: v.from_polar(1, 2)) + self.assertRaises(TypeError, lambda: Vector2.from_polar((None, None))) + self.assertRaises(TypeError, lambda: Vector2.from_polar("ab")) + self.assertRaises(TypeError, lambda: Vector2.from_polar((None, 1))) + self.assertRaises(TypeError, lambda: Vector2.from_polar((1, 2, 3))) + self.assertRaises(TypeError, lambda: Vector2.from_polar((1,))) + self.assertRaises(TypeError, lambda: Vector2.from_polar(1, 2)) + v.from_polar((0.5, 90)) + self.assertEqual(v, 0.5 * self.e2) + self.assertEqual(Vector2.from_polar((0.5, 90)), 0.5 * self.e2) + self.assertEqual(Vector2.from_polar((0.5, 90)), v) v.from_polar((1, 0)) self.assertEqual(v, self.e1) + self.assertEqual(Vector2.from_polar((1, 0)), self.e1) + self.assertEqual(Vector2.from_polar((1, 0)), v) def test_subclass_operation(self): class Vector(pygame.math.Vector2): @@ -747,10 +1015,206 @@ class Vector2TypeTest(unittest.TestCase): vec_a + vec_b vec_a *= 2 + def test_project_v2_onto_x_axis(self): + """Project onto x-axis, e.g. get the component pointing in the x-axis direction.""" + # arrange + v = Vector2(2, 2) + x_axis = Vector2(10, 0) + + # act + actual = v.project(x_axis) + + # assert + self.assertEqual(v.x, actual.x) + self.assertEqual(0, actual.y) + + def test_project_v2_onto_y_axis(self): + """Project onto y-axis, e.g. get the component pointing in the y-axis direction.""" + # arrange + v = Vector2(2, 2) + y_axis = Vector2(0, 100) + + # act + actual = v.project(y_axis) + + # assert + self.assertEqual(0, actual.x) + self.assertEqual(v.y, actual.y) + + def test_project_v2_onto_other(self): + """Project onto other vector.""" + # arrange + v = Vector2(2, 3) + other = Vector2(3, 5) + + # act + actual = v.project(other) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertEqual(expected.x, actual.x) + self.assertEqual(expected.y, actual.y) + + def test_project_v2_onto_other_as_tuple(self): + """Project onto other tuple as vector.""" + # arrange + v = Vector2(2, 3) + other = Vector2(3, 5) + + # act + actual = v.project(tuple(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertEqual(expected.x, actual.x) + self.assertEqual(expected.y, actual.y) + + def test_project_v2_onto_other_as_list(self): + """Project onto other list as vector.""" + # arrange + v = Vector2(2, 3) + other = Vector2(3, 5) + + # act + actual = v.project(list(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertEqual(expected.x, actual.x) + self.assertEqual(expected.y, actual.y) + + def test_project_v2_raises_if_other_has_zero_length(self): + """Check if exception is raise when projected on vector has zero length.""" + # arrange + v = Vector2(2, 3) + other = Vector2(0, 0) + + # act / assert + self.assertRaises(ValueError, v.project, other) + + def test_project_v2_raises_if_other_is_not_iterable(self): + """Check if exception is raise when projected on vector is not iterable.""" + # arrange + v = Vector2(2, 3) + other = 10 + + # act / assert + self.assertRaises(TypeError, v.project, other) + + def test_collection_abc(self): + v = Vector2(3, 4) + self.assertTrue(isinstance(v, Collection)) + self.assertFalse(isinstance(v, Sequence)) + + def test_clamp_mag_v2_max(self): + v1 = Vector2(7, 2) + v2 = v1.clamp_magnitude(5) + v3 = v1.clamp_magnitude(0, 5) + self.assertEqual(v2, v3) + + v1.clamp_magnitude_ip(5) + self.assertEqual(v1, v2) + + v1.clamp_magnitude_ip(0, 5) + self.assertEqual(v1, v2) + + expected_v2 = Vector2(4.807619738204116, 1.3736056394868903) + self.assertEqual(expected_v2, v2) + + def test_clamp_mag_v2_min(self): + v1 = Vector2(1, 2) + v2 = v1.clamp_magnitude(3, 5) + v1.clamp_magnitude_ip(3, 5) + expected_v2 = Vector2(1.3416407864998738, 2.6832815729997477) + self.assertEqual(expected_v2, v2) + self.assertEqual(expected_v2, v1) + + def test_clamp_mag_v2_no_change(self): + v1 = Vector2(1, 2) + for args in ( + (1, 6), + (1.12, 3.55), + (0.93, 2.83), + (7.6,), + ): + with self.subTest(args=args): + v2 = v1.clamp_magnitude(*args) + v1.clamp_magnitude_ip(*args) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector2(1, 2)) + + def test_clamp_mag_v2_edge_cases(self): + v1 = Vector2(1, 2) + v2 = v1.clamp_magnitude(6, 6) + v1.clamp_magnitude_ip(6, 6) + self.assertEqual(v1, v2) + self.assertAlmostEqual(v1.length(), 6) + + v2 = v1.clamp_magnitude(0) + v1.clamp_magnitude_ip(0, 0) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector2()) + + def test_clamp_mag_v2_errors(self): + v1 = Vector2(1, 2) + for invalid_args in ( + ("foo", "bar"), + (1, 2, 3), + (342.234, "test"), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(TypeError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(TypeError, v1.clamp_magnitude_ip, *invalid_args) + + for invalid_args in ( + (-1,), + (4, 3), # min > max + (-4, 10), + (-4, -2), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(ValueError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(ValueError, v1.clamp_magnitude_ip, *invalid_args) + + # 0 vector + v2 = Vector2() + self.assertRaises(ValueError, v2.clamp_magnitude, 3) + self.assertRaises(ValueError, v2.clamp_magnitude_ip, 4) + + def test_subclassing_v2(self): + """Check if Vector2 is subclassable""" + v = Vector2(4, 2) + + class TestVector(Vector2): + def supermariobrosiscool(self): + return 722 + + other = TestVector(4, 1) + + self.assertEqual(other.supermariobrosiscool(), 722) + self.assertNotEqual(type(v), TestVector) + self.assertNotEqual(type(v), type(other.copy())) + self.assertEqual(TestVector, type(other.reflect(v))) + self.assertEqual(TestVector, type(other.lerp(v, 1))) + self.assertEqual(TestVector, type(other.slerp(v, 1))) + self.assertEqual(TestVector, type(other.rotate(5))) + self.assertEqual(TestVector, type(other.rotate_rad(5))) + self.assertEqual(TestVector, type(other.project(v))) + self.assertEqual(TestVector, type(other.move_towards(v, 5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(1, 5))) + self.assertEqual(TestVector, type(other.elementwise() + other)) + + other1 = TestVector(4, 2) + + self.assertEqual(type(other + other1), TestVector) + self.assertEqual(type(other - other1), TestVector) + self.assertEqual(type(other * 3), TestVector) + self.assertEqual(type(other / 3), TestVector) + self.assertEqual(type(other.elementwise() ** 3), TestVector) class Vector3TypeTest(unittest.TestCase): - def setUp(self): self.zeroVec = Vector3() self.e1 = Vector3(1, 0, 0) @@ -767,9 +1231,9 @@ class Vector3TypeTest(unittest.TestCase): def testConstructionDefault(self): v = Vector3() - self.assertEqual(v.x, 0.) - self.assertEqual(v.y, 0.) - self.assertEqual(v.z, 0.) + self.assertEqual(v.x, 0.0) + self.assertEqual(v.y, 0.0) + self.assertEqual(v.z, 0.0) def testConstructionXYZ(self): v = Vector3(1.2, 3.4, 9.6) @@ -797,32 +1261,27 @@ class Vector3TypeTest(unittest.TestCase): def testConstructionScalar(self): v = Vector3(1) - self.assertEqual(v.x, 1.) - self.assertEqual(v.y, 1.) - self.assertEqual(v.z, 1.) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + self.assertEqual(v.z, 1.0) def testConstructionScalarKeywords(self): v = Vector3(x=1) - self.assertEqual(v.x, 1.) - self.assertEqual(v.y, 1.) - self.assertEqual(v.z, 1.) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 1.0) + self.assertEqual(v.z, 1.0) def testConstructionKeywords(self): v = Vector3(x=1, y=2, z=3) - self.assertEqual(v.x, 1.) - self.assertEqual(v.y, 2.) - self.assertEqual(v.z, 3.) + self.assertEqual(v.x, 1.0) + self.assertEqual(v.y, 2.0) + self.assertEqual(v.z, 3.0) def testConstructionMissing(self): - def assign_missing_value(): - v = Vector3(1, 2) - self.assertRaises(ValueError, assign_missing_value) + self.assertRaises(ValueError, Vector3, 1, 2) + self.assertRaises(ValueError, Vector3, x=1, y=2) - def assign_missing_value(): - v = Vector3(x=1, y=2) - self.assertRaises(ValueError, assign_missing_value) - - def testAttributAccess(self): + def testAttributeAccess(self): tmp = self.v1.x self.assertEqual(tmp, self.v1.x) self.assertEqual(tmp, self.v1[0]) @@ -838,22 +1297,31 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(self.v1.y, 3.141) self.v1.z = 3.141 self.assertEqual(self.v1.z, 3.141) + def assign_nonfloat(): v = Vector2() v.x = "spam" + self.assertRaises(TypeError, assign_nonfloat) + def testCopy(self): + v_copy0 = Vector3(2014.0, 2032.0, 2076.0) + v_copy1 = v_copy0.copy() + self.assertEqual(v_copy0.x, v_copy1.x) + self.assertEqual(v_copy0.y, v_copy1.y) + self.assertEqual(v_copy0.z, v_copy1.z) + def testSequence(self): v = Vector3(1.2, 3.4, -9.6) self.assertEqual(len(v), 3) self.assertEqual(v[0], 1.2) self.assertEqual(v[1], 3.4) self.assertEqual(v[2], -9.6) - self.assertRaises(IndexError, lambda : v[3]) + self.assertRaises(IndexError, lambda: v[3]) self.assertEqual(v[-1], -9.6) self.assertEqual(v[-2], 3.4) self.assertEqual(v[-3], 1.2) - self.assertRaises(IndexError, lambda : v[-4]) + self.assertRaises(IndexError, lambda: v[-4]) self.assertEqual(v[:], [1.2, 3.4, -9.6]) self.assertEqual(v[1:], [3.4, -9.6]) self.assertEqual(v[:1], [1.2]) @@ -871,17 +1339,23 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(v.x, 9.1) self.assertEqual(v.y, 11.12) self.assertEqual(v.z, -13.41) + def overpopulate(): v = Vector3() v[:] = [1, 2, 3, 4] + self.assertRaises(ValueError, overpopulate) + def underpopulate(): v = Vector3() v[:] = [1] + self.assertRaises(ValueError, underpopulate) + def assign_nonfloat(): v = Vector2() v[0] = "spam" + self.assertRaises(TypeError, assign_nonfloat) def testExtendedSlicing(self): @@ -893,6 +1367,7 @@ class Vector3TypeTest(unittest.TestCase): del vec[start::step] elif start is None and stop is None and step is not None: del vec[::step] + v = Vector3(self.v1) self.assertRaises(TypeError, delSlice, v, None, None, 2) self.assertRaises(TypeError, delSlice, v, 1, None, 2) @@ -912,7 +1387,7 @@ class Vector3TypeTest(unittest.TestCase): b = Vector3(self.v1) c = Vector3(self.v1) a[1:2] = [2.2] - b[slice(1,2)] = [2.2] + b[slice(1, 2)] = [2.2] c[1:2:] = (2.2,) self.assertEqual(a, b) self.assertEqual(a, c) @@ -920,6 +1395,20 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(type(b), type(self.v1)) self.assertEqual(type(c), type(self.v1)) + def test_contains(self): + c = Vector3(0, 1, 2) + + # call __contains__ explicitly to test that it is defined + self.assertTrue(c.__contains__(0)) + self.assertTrue(0 in c) + self.assertTrue(1 in c) + self.assertTrue(2 in c) + self.assertTrue(3 not in c) + self.assertFalse(c.__contains__(10)) + + self.assertRaises(TypeError, lambda: "string" in c) + self.assertRaises(TypeError, lambda: 3 + 4j in c) + def testAdd(self): v3 = self.v1 + self.v2 self.assertTrue(isinstance(v3, type(self.v1))) @@ -1019,7 +1508,7 @@ class Vector3TypeTest(unittest.TestCase): def testCompare(self): int_vec = Vector3(3, -2, 13) - flt_vec = Vector3(3.0, -2.0, 13.) + flt_vec = Vector3(3.0, -2.0, 13.0) zero_vec = Vector3(0, 0, 0) self.assertEqual(int_vec == flt_vec, True) self.assertEqual(int_vec != flt_vec, False) @@ -1045,25 +1534,142 @@ class Vector3TypeTest(unittest.TestCase): def testIter(self): it = self.v1.__iter__() - if PY3: - next_ = it.__next__ - else: - next_ = it.next + next_ = it.__next__ self.assertEqual(next_(), self.v1[0]) self.assertEqual(next_(), self.v1[1]) self.assertEqual(next_(), self.v1[2]) - self.assertRaises(StopIteration, lambda : next_()) + self.assertRaises(StopIteration, lambda: next_()) it1 = self.v1.__iter__() it2 = self.v1.__iter__() self.assertNotEqual(id(it1), id(it2)) self.assertEqual(id(it1), id(it1.__iter__())) - self.assertEqual(list(it1), list(it2)); + self.assertEqual(list(it1), list(it2)) self.assertEqual(list(self.v1.__iter__()), self.l1) idx = 0 for val in self.v1: self.assertEqual(val, self.v1[idx]) idx += 1 + def test___round___basic(self): + self.assertEqual( + round(pygame.Vector3(0.0, 0.0, 0.0)), pygame.Vector3(0.0, 0.0, 0.0) + ) + self.assertEqual(type(round(pygame.Vector3(0.0, 0.0, 0.0))), pygame.Vector3) + self.assertEqual( + round(pygame.Vector3(1.0, 1.0, 1.0)), round(pygame.Vector3(1.0, 1.0, 1.0)) + ) + self.assertEqual( + round(pygame.Vector3(10.0, 10.0, 10.0)), + round(pygame.Vector3(10.0, 10.0, 10.0)), + ) + self.assertEqual( + round(pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0)), + pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0), + ) + self.assertEqual( + round(pygame.Vector3(1e20, 1e20, 1e20)), pygame.Vector3(1e20, 1e20, 1e20) + ) + + self.assertEqual( + round(pygame.Vector3(-1.0, -1.0, -1.0)), pygame.Vector3(-1.0, -1.0, -1.0) + ) + self.assertEqual( + round(pygame.Vector3(-10.0, -10.0, -10.0)), + pygame.Vector3(-10.0, -10.0, -10.0), + ) + self.assertEqual( + round(pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0)), + pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0), + ) + self.assertEqual( + round(pygame.Vector3(-1e20, -1e20, -1e20)), + pygame.Vector3(-1e20, -1e20, -1e20), + ) + + self.assertEqual( + round(pygame.Vector3(0.1, 0.1, 0.1)), pygame.Vector3(0.0, 0.0, 0.0) + ) + self.assertEqual( + round(pygame.Vector3(1.1, 1.1, 1.1)), pygame.Vector3(1.0, 1.0, 1.0) + ) + self.assertEqual( + round(pygame.Vector3(10.1, 10.1, 10.1)), pygame.Vector3(10.0, 10.0, 10.0) + ) + self.assertEqual( + round(pygame.Vector3(1000000000.1, 1000000000.1, 1000000000.1)), + pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(-1.1, -1.1, -1.1)), pygame.Vector3(-1.0, -1.0, -1.0) + ) + self.assertEqual( + round(pygame.Vector3(-10.1, -10.1, -10.1)), + pygame.Vector3(-10.0, -10.0, -10.0), + ) + self.assertEqual( + round(pygame.Vector3(-1000000000.1, -1000000000.1, -1000000000.1)), + pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(0.9, 0.9, 0.9)), pygame.Vector3(1.0, 1.0, 1.0) + ) + self.assertEqual( + round(pygame.Vector3(9.9, 9.9, 9.9)), pygame.Vector3(10.0, 10.0, 10.0) + ) + self.assertEqual( + round(pygame.Vector3(999999999.9, 999999999.9, 999999999.9)), + pygame.Vector3(1000000000.0, 1000000000.0, 1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(-0.9, -0.9, -0.9)), pygame.Vector3(-1.0, -1.0, -1.0) + ) + self.assertEqual( + round(pygame.Vector3(-9.9, -9.9, -9.9)), pygame.Vector3(-10.0, -10.0, -10.0) + ) + self.assertEqual( + round(pygame.Vector3(-999999999.9, -999999999.9, -999999999.9)), + pygame.Vector3(-1000000000.0, -1000000000.0, -1000000000.0), + ) + + self.assertEqual( + round(pygame.Vector3(-8.0, -8.0, -8.0), -1), + pygame.Vector3(-10.0, -10.0, -10.0), + ) + self.assertEqual( + type(round(pygame.Vector3(-8.0, -8.0, -8.0), -1)), pygame.Vector3 + ) + + self.assertEqual( + type(round(pygame.Vector3(-8.0, -8.0, -8.0), 0)), pygame.Vector3 + ) + self.assertEqual( + type(round(pygame.Vector3(-8.0, -8.0, -8.0), 1)), pygame.Vector3 + ) + + # Check even / odd rounding behaviour + self.assertEqual(round(pygame.Vector3(5.5, 5.5, 5.5)), pygame.Vector3(6, 6, 6)) + self.assertEqual( + round(pygame.Vector3(5.4, 5.4, 5.4)), pygame.Vector3(5.0, 5.0, 5.0) + ) + self.assertEqual( + round(pygame.Vector3(5.6, 5.6, 5.6)), pygame.Vector3(6.0, 6.0, 6.0) + ) + self.assertEqual( + round(pygame.Vector3(-5.5, -5.5, -5.5)), pygame.Vector3(-6, -6, -6) + ) + self.assertEqual( + round(pygame.Vector3(-5.4, -5.4, -5.4)), pygame.Vector3(-5, -5, -5) + ) + self.assertEqual( + round(pygame.Vector3(-5.6, -5.6, -5.6)), pygame.Vector3(-6, -6, -6) + ) + + self.assertRaises(TypeError, round, pygame.Vector3(1.0, 1.0, 1.0), 1.5) + self.assertRaises(TypeError, round, pygame.Vector3(1.0, 1.0, 1.0), "a") + def test_rotate(self): v1 = Vector3(1, 0, 0) axis = Vector3(0, 1, 0) @@ -1092,8 +1698,21 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(v1.y, v2.y) self.assertEqual(v1.z, v2.z) # issue 214 - self.assertEqual(Vector3(0, 1, 0).rotate(359.9999999, Vector3(0, 0, 1)), - Vector3(0, 1, 0)) + self.assertEqual( + Vector3(0, 1, 0).rotate(359.9999999, Vector3(0, 0, 1)), Vector3(0, 1, 0) + ) + + def test_rotate_rad(self): + axis = Vector3(0, 0, 1) + tests = ( + ((1, 0, 0), math.pi), + ((1, 0, 0), math.pi / 2), + ((1, 0, 0), -math.pi / 2), + ((1, 0, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector3(initialVec).rotate_rad(radians, axis) + self.assertEqual(vec, (math.cos(radians), math.sin(radians), 0)) def test_rotate_ip(self): v = Vector3(1, 0, 0) @@ -1108,6 +1727,19 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(v.y, -1) self.assertEqual(v.z, -1) + def test_rotate_rad_ip(self): + axis = Vector3(0, 0, 1) + tests = ( + ((1, 0, 0), math.pi), + ((1, 0, 0), math.pi / 2), + ((1, 0, 0), -math.pi / 2), + ((1, 0, 0), math.pi / 4), + ) + for initialVec, radians in tests: + vec = Vector3(initialVec) + vec.rotate_rad_ip(radians, axis) + self.assertEqual(vec, (math.cos(radians), math.sin(radians), 0)) + def test_rotate_x(self): v1 = Vector3(1, 0, 0) v2 = v1.rotate_x(90) @@ -1135,6 +1767,11 @@ class Vector3TypeTest(unittest.TestCase): self.assertAlmostEqual(v1.y, v2.y) self.assertAlmostEqual(v1.z, v2.z) + def test_rotate_x_rad(self): + vec = Vector3(0, 1, 0) + result = vec.rotate_x_rad(math.pi / 2) + self.assertEqual(result, (0, 0, 1)) + def test_rotate_x_ip(self): v = Vector3(1, 0, 0) self.assertEqual(v.rotate_x_ip(90), None) @@ -1147,6 +1784,11 @@ class Vector3TypeTest(unittest.TestCase): self.assertAlmostEqual(v.y, 1) self.assertAlmostEqual(v.z, 1) + def test_rotate_x_rad_ip(self): + vec = Vector3(0, 1, 0) + vec.rotate_x_rad_ip(math.pi / 2) + self.assertEqual(vec, (0, 0, 1)) + def test_rotate_y(self): v1 = Vector3(1, 0, 0) v2 = v1.rotate_y(90) @@ -1174,6 +1816,11 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(v1.y, v2.y) self.assertEqual(v1.z, v2.z) + def test_rotate_y_rad(self): + vec = Vector3(1, 0, 0) + result = vec.rotate_y_rad(math.pi / 2) + self.assertEqual(result, (0, 0, -1)) + def test_rotate_y_ip(self): v = Vector3(1, 0, 0) self.assertEqual(v.rotate_y_ip(90), None) @@ -1186,6 +1833,11 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(v.y, -1) self.assertAlmostEqual(v.z, -1) + def test_rotate_y_rad_ip(self): + vec = Vector3(1, 0, 0) + vec.rotate_y_rad_ip(math.pi / 2) + self.assertEqual(vec, (0, 0, -1)) + def test_rotate_z(self): v1 = Vector3(1, 0, 0) v2 = v1.rotate_z(90) @@ -1213,6 +1865,11 @@ class Vector3TypeTest(unittest.TestCase): self.assertAlmostEqual(v1.y, v2.y) self.assertEqual(v1.z, v2.z) + def test_rotate_z_rad(self): + vec = Vector3(1, 0, 0) + result = vec.rotate_z_rad(math.pi / 2) + self.assertEqual(result, (0, 1, 0)) + def test_rotate_z_ip(self): v = Vector3(1, 0, 0) self.assertEqual(v.rotate_z_ip(90), None) @@ -1225,35 +1882,44 @@ class Vector3TypeTest(unittest.TestCase): self.assertAlmostEqual(v.y, 1) self.assertEqual(v.z, 1) + def test_rotate_z_rad_ip(self): + vec = Vector3(1, 0, 0) + vec.rotate_z_rad_ip(math.pi / 2) + self.assertEqual(vec, (0, 1, 0)) + def test_normalize(self): v = self.v1.normalize() # length is 1 - self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.) + self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) # v1 is unchanged self.assertEqual(self.v1.x, self.l1[0]) self.assertEqual(self.v1.y, self.l1[1]) self.assertEqual(self.v1.z, self.l1[2]) - # v2 is paralell to v1 (tested via cross product) - cross = ((self.v1.y * v.z - self.v1.z * v.y) ** 2 + - (self.v1.z * v.x - self.v1.x * v.z) ** 2 + - (self.v1.x * v.y - self.v1.y * v.x) ** 2) - self.assertAlmostEqual(cross, 0.) - self.assertRaises(ValueError, lambda : self.zeroVec.normalize()) + # v2 is parallel to v1 (tested via cross product) + cross = ( + (self.v1.y * v.z - self.v1.z * v.y) ** 2 + + (self.v1.z * v.x - self.v1.x * v.z) ** 2 + + (self.v1.x * v.y - self.v1.y * v.x) ** 2 + ) + self.assertAlmostEqual(cross, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize()) def test_normalize_ip(self): v = +self.v1 # v has length != 1 before normalizing - self.assertNotEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.) + self.assertNotEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) # inplace operations should return None self.assertEqual(v.normalize_ip(), None) # length is 1 - self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.) - # v2 is paralell to v1 (tested via cross product) - cross = ((self.v1.y * v.z - self.v1.z * v.y) ** 2 + - (self.v1.z * v.x - self.v1.x * v.z) ** 2 + - (self.v1.x * v.y - self.v1.y * v.x) ** 2) - self.assertAlmostEqual(cross, 0.) - self.assertRaises(ValueError, lambda : self.zeroVec.normalize_ip()) + self.assertAlmostEqual(v.x * v.x + v.y * v.y + v.z * v.z, 1.0) + # v2 is parallel to v1 (tested via cross product) + cross = ( + (self.v1.y * v.z - self.v1.z * v.y) ** 2 + + (self.v1.z * v.x - self.v1.x * v.z) ** 2 + + (self.v1.x * v.y - self.v1.y * v.x) ** 2 + ) + self.assertAlmostEqual(cross, 0.0) + self.assertRaises(ValueError, lambda: self.zeroVec.normalize_ip()) def test_is_normalized(self): self.assertEqual(self.v1.is_normalized(), False) @@ -1264,9 +1930,12 @@ class Vector3TypeTest(unittest.TestCase): def test_cross(self): def cross(a, b): - return Vector3(a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0]) + return Vector3( + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ) + self.assertEqual(self.v1.cross(self.v2), cross(self.v1, self.v2)) self.assertEqual(self.v1.cross(self.l2), cross(self.v1, self.l2)) self.assertEqual(self.v1.cross(self.t2), cross(self.v1, self.t2)) @@ -1274,12 +1943,18 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(self.v1.cross(self.v1), self.zeroVec) def test_dot(self): - self.assertAlmostEqual(self.v1.dot(self.v2), - self.v1.x * self.v2.x + self.v1.y * self.v2.y + self.v1.z * self.v2.z) - self.assertAlmostEqual(self.v1.dot(self.l2), - self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + self.v1.z * self.l2[2]) - self.assertAlmostEqual(self.v1.dot(self.t2), - self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + self.v1.z * self.t2[2]) + self.assertAlmostEqual( + self.v1.dot(self.v2), + self.v1.x * self.v2.x + self.v1.y * self.v2.y + self.v1.z * self.v2.z, + ) + self.assertAlmostEqual( + self.v1.dot(self.l2), + self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + self.v1.z * self.l2[2], + ) + self.assertAlmostEqual( + self.v1.dot(self.t2), + self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + self.v1.z * self.t2[2], + ) self.assertAlmostEqual(self.v1.dot(self.v2), self.v2.dot(self.v1)) self.assertAlmostEqual(self.v1.dot(self.v2), self.v1 * self.v2) @@ -1290,14 +1965,18 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(abs(Vector3(1, 0, 1).angle_to((-1, 0, -1))), 180) # if we rotate v1 by the angle_to v2 around their cross product # we should look in the same direction - self.assertEqual(self.v1.rotate(self.v1.angle_to(self.v2), self.v1.cross(self.v2)).normalize(), - self.v2.normalize()) + self.assertEqual( + self.v1.rotate( + self.v1.angle_to(self.v2), self.v1.cross(self.v2) + ).normalize(), + self.v2.normalize(), + ) def test_scale_to_length(self): v = Vector3(1, 1, 1) v.scale_to_length(2.5) self.assertEqual(v, Vector3(2.5, 2.5, 2.5) / math.sqrt(3)) - self.assertRaises(ValueError, lambda : self.zeroVec.scale_to_length(1)) + self.assertRaises(ValueError, lambda: self.zeroVec.scale_to_length(1)) self.assertEqual(v.scale_to_length(0), None) self.assertEqual(v, self.zeroVec) @@ -1315,9 +1994,9 @@ class Vector3TypeTest(unittest.TestCase): v = Vector3(1, -1, 1) n = Vector3(0, 1, 0) self.assertEqual(v.reflect(n), Vector3(1, 1, 1)) - self.assertEqual(v.reflect(3*n), v.reflect(n)) + self.assertEqual(v.reflect(3 * n), v.reflect(n)) self.assertEqual(v.reflect(-v), -v) - self.assertRaises(ValueError, lambda : v.reflect(self.zeroVec)) + self.assertRaises(ValueError, lambda: v.reflect(self.zeroVec)) def test_reflect_ip(self): v1 = Vector3(1, -1, 1) @@ -1326,43 +2005,172 @@ class Vector3TypeTest(unittest.TestCase): self.assertEqual(v2.reflect_ip(n), None) self.assertEqual(v2, Vector3(1, 1, 1)) v2 = Vector3(v1) - v2.reflect_ip(3*n) + v2.reflect_ip(3 * n) self.assertEqual(v2, v1.reflect(n)) v2 = Vector3(v1) v2.reflect_ip(-v1) self.assertEqual(v2, -v1) - self.assertRaises(ValueError, lambda : v2.reflect_ip(self.zeroVec)) + self.assertRaises(ValueError, lambda: v2.reflect_ip(self.zeroVec)) def test_distance_to(self): diff = self.v1 - self.v2 self.assertEqual(self.e1.distance_to(self.e2), math.sqrt(2)) - self.assertEqual(self.v1.distance_to(self.v2), - math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z)) + self.assertEqual(self.e1.distance_to((0, 1, 0)), math.sqrt(2)) + self.assertEqual(self.e1.distance_to([0, 1, 0]), math.sqrt(2)) + self.assertEqual( + self.v1.distance_to(self.v2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) + self.assertEqual( + self.v1.distance_to(self.t2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) + self.assertEqual( + self.v1.distance_to(self.l2), + math.sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z), + ) self.assertEqual(self.v1.distance_to(self.v1), 0) - self.assertEqual(self.v1.distance_to(self.v2), - self.v2.distance_to(self.v1)) + self.assertEqual(self.v1.distance_to(self.t1), 0) + self.assertEqual(self.v1.distance_to(self.l1), 0) + self.assertEqual(self.v1.distance_to(self.v2), self.v2.distance_to(self.v1)) + self.assertEqual(self.v1.distance_to(self.t2), self.v2.distance_to(self.t1)) + self.assertEqual(self.v1.distance_to(self.l2), self.v2.distance_to(self.l1)) + + def test_distance_to_exceptions(self): + v2 = Vector2(10, 10) + v3 = Vector3(1, 1, 1) + + # illegal distance Vector3-Vector2 / Vector2-Vector3 + self.assertRaises(ValueError, v2.distance_to, v3) + self.assertRaises(ValueError, v3.distance_to, v2) + + # distance to illegal tuple/list positions + self.assertRaises(ValueError, v2.distance_to, (1, 1, 1)) + self.assertRaises(ValueError, v2.distance_to, (1, 1, 0)) + self.assertRaises(ValueError, v2.distance_to, (1,)) + self.assertRaises(ValueError, v2.distance_to, [1, 1, 1]) + self.assertRaises(ValueError, v2.distance_to, [1, 1, 0]) + self.assertRaises( + ValueError, + v2.distance_to, + [ + 1, + ], + ) + self.assertRaises(ValueError, v2.distance_to, (1, 1, 1)) + # vec3 + self.assertRaises(ValueError, v3.distance_to, (1, 1)) + self.assertRaises(ValueError, v3.distance_to, (1,)) + self.assertRaises(ValueError, v3.distance_to, [1, 1]) + self.assertRaises( + ValueError, + v3.distance_to, + [ + 1, + ], + ) + + # illegal types as positions + self.assertRaises(TypeError, v2.distance_to, (1, "hello")) + self.assertRaises(TypeError, v2.distance_to, ([], [])) + self.assertRaises(TypeError, v2.distance_to, (1, ("hello",))) + + # illegal args number + self.assertRaises(TypeError, v2.distance_to) + self.assertRaises(TypeError, v2.distance_to, (1, 1), (1, 2)) + self.assertRaises(TypeError, v2.distance_to, (1, 1), (1, 2), 1) + + def test_distance_squared_to_exceptions(self): + v2 = Vector2(10, 10) + v3 = Vector3(1, 1, 1) + dist_t = v2.distance_squared_to + dist_t3 = v3.distance_squared_to + # illegal distance Vector3-Vector2 / Vector2-Vector3 + self.assertRaises(ValueError, dist_t, v3) + self.assertRaises(ValueError, dist_t3, v2) + + # distance to illegal tuple/list positions + self.assertRaises(ValueError, dist_t, (1, 1, 1)) + self.assertRaises(ValueError, dist_t, (1, 1, 0)) + self.assertRaises(ValueError, dist_t, (1,)) + self.assertRaises(ValueError, dist_t, [1, 1, 1]) + self.assertRaises(ValueError, dist_t, [1, 1, 0]) + self.assertRaises( + ValueError, + dist_t, + [ + 1, + ], + ) + self.assertRaises(ValueError, dist_t, (1, 1, 1)) + # vec3 + self.assertRaises(ValueError, dist_t3, (1, 1)) + self.assertRaises(ValueError, dist_t3, (1,)) + self.assertRaises(ValueError, dist_t3, [1, 1]) + self.assertRaises( + ValueError, + dist_t3, + [ + 1, + ], + ) + + # illegal types as positions + self.assertRaises(TypeError, dist_t, (1, "hello")) + self.assertRaises(TypeError, dist_t, ([], [])) + self.assertRaises(TypeError, dist_t, (1, ("hello",))) + + # illegal args number + self.assertRaises(TypeError, dist_t) + self.assertRaises(TypeError, dist_t, (1, 1), (1, 2)) + self.assertRaises(TypeError, dist_t, (1, 1), (1, 2), 1) def test_distance_squared_to(self): diff = self.v1 - self.v2 self.assertEqual(self.e1.distance_squared_to(self.e2), 2) - self.assertAlmostEqual(self.v1.distance_squared_to(self.v2), - diff.x * diff.x + diff.y * diff.y + diff.z * diff.z) + self.assertEqual(self.e1.distance_squared_to((0, 1, 0)), 2) + self.assertEqual(self.e1.distance_squared_to([0, 1, 0]), 2) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.v2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.t2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) + self.assertAlmostEqual( + self.v1.distance_squared_to(self.l2), + diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, + ) self.assertEqual(self.v1.distance_squared_to(self.v1), 0) - self.assertEqual(self.v1.distance_squared_to(self.v2), - self.v2.distance_squared_to(self.v1)) + self.assertEqual(self.v1.distance_squared_to(self.t1), 0) + self.assertEqual(self.v1.distance_squared_to(self.l1), 0) + self.assertEqual( + self.v1.distance_squared_to(self.v2), self.v2.distance_squared_to(self.v1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.t2), self.v2.distance_squared_to(self.t1) + ) + self.assertEqual( + self.v1.distance_squared_to(self.l2), self.v2.distance_squared_to(self.l1) + ) def test_swizzle(self): - self.assertTrue(hasattr(pygame.math, "enable_swizzling")) - self.assertTrue(hasattr(pygame.math, "disable_swizzling")) - # swizzling enabled by default - pygame.math.disable_swizzling() - self.assertRaises(AttributeError, lambda : self.v1.yx) - pygame.math.enable_swizzling() - self.assertEqual(self.v1.yxz, (self.v1.y, self.v1.x, self.v1.z)) - self.assertEqual(self.v1.xxyyzzxyz, (self.v1.x, self.v1.x, self.v1.y, - self.v1.y, self.v1.z, self.v1.z, - self.v1.x, self.v1.y, self.v1.z)) + self.assertEqual( + self.v1.xxyyzzxyz, + ( + self.v1.x, + self.v1.x, + self.v1.y, + self.v1.y, + self.v1.z, + self.v1.z, + self.v1.x, + self.v1.y, + self.v1.z, + ), + ) self.v1.xyz = self.t2 self.assertEqual(self.v1, self.t2) self.v1.zxy = self.t2 @@ -1375,18 +2183,24 @@ class Vector3TypeTest(unittest.TestCase): def test_invalid_swizzle(self): def invalidSwizzleX(): Vector3().xx = (1, 2) + def invalidSwizzleY(): Vector3().yy = (1, 2) + def invalidSwizzleZ(): Vector3().zz = (1, 2) + def invalidSwizzleW(): Vector3().ww = (1, 2) + self.assertRaises(AttributeError, invalidSwizzleX) self.assertRaises(AttributeError, invalidSwizzleY) self.assertRaises(AttributeError, invalidSwizzleZ) self.assertRaises(AttributeError, invalidSwizzleW) + def invalidAssignment(): Vector3().xy = 3 + self.assertRaises(TypeError, invalidAssignment) def test_swizzle_return_types(self): @@ -1399,139 +2213,321 @@ class Vector3TypeTest(unittest.TestCase): def test_dir_works(self): # not every single one of the attributes... - attributes = set(['lerp', 'normalize', 'normalize_ip', 'reflect', 'slerp', 'x', 'y']) + attributes = {"lerp", "normalize", "normalize_ip", "reflect", "slerp", "x", "y"} # check if this selection of attributes are all there. self.assertTrue(attributes.issubset(set(dir(self.v1)))) def test_elementwise(self): # behaviour for "elementwise op scalar" - self.assertEqual(self.v1.elementwise() + self.s1, - (self.v1.x + self.s1, self.v1.y + self.s1, self.v1.z + self.s1)) - self.assertEqual(self.v1.elementwise() - self.s1, - (self.v1.x - self.s1, self.v1.y - self.s1, self.v1.z - self.s1)) - self.assertEqual(self.v1.elementwise() * self.s2, - (self.v1.x * self.s2, self.v1.y * self.s2, self.v1.z * self.s2)) - self.assertEqual(self.v1.elementwise() / self.s2, - (self.v1.x / self.s2, self.v1.y / self.s2, self.v1.z / self.s2)) - self.assertEqual(self.v1.elementwise() // self.s1, - (self.v1.x // self.s1, self.v1.y // self.s1, self.v1.z // self.s1)) - self.assertEqual(self.v1.elementwise() ** self.s1, - (self.v1.x ** self.s1, self.v1.y ** self.s1, self.v1.z ** self.s1)) - self.assertEqual(self.v1.elementwise() % self.s1, - (self.v1.x % self.s1, self.v1.y % self.s1, self.v1.z % self.s1)) - self.assertEqual(self.v1.elementwise() > self.s1, - self.v1.x > self.s1 and self.v1.y > self.s1 and self.v1.z > self.s1) - self.assertEqual(self.v1.elementwise() < self.s1, - self.v1.x < self.s1 and self.v1.y < self.s1 and self.v1.z < self.s1) - self.assertEqual(self.v1.elementwise() == self.s1, - self.v1.x == self.s1 and self.v1.y == self.s1 and self.v1.z == self.s1) - self.assertEqual(self.v1.elementwise() != self.s1, - self.v1.x != self.s1 and self.v1.y != self.s1 and self.v1.z != self.s1) - self.assertEqual(self.v1.elementwise() >= self.s1, - self.v1.x >= self.s1 and self.v1.y >= self.s1 and self.v1.z >= self.s1) - self.assertEqual(self.v1.elementwise() <= self.s1, - self.v1.x <= self.s1 and self.v1.y <= self.s1 and self.v1.z <= self.s1) + self.assertEqual( + self.v1.elementwise() + self.s1, + (self.v1.x + self.s1, self.v1.y + self.s1, self.v1.z + self.s1), + ) + self.assertEqual( + self.v1.elementwise() - self.s1, + (self.v1.x - self.s1, self.v1.y - self.s1, self.v1.z - self.s1), + ) + self.assertEqual( + self.v1.elementwise() * self.s2, + (self.v1.x * self.s2, self.v1.y * self.s2, self.v1.z * self.s2), + ) + self.assertEqual( + self.v1.elementwise() / self.s2, + (self.v1.x / self.s2, self.v1.y / self.s2, self.v1.z / self.s2), + ) + self.assertEqual( + self.v1.elementwise() // self.s1, + (self.v1.x // self.s1, self.v1.y // self.s1, self.v1.z // self.s1), + ) + self.assertEqual( + self.v1.elementwise() ** self.s1, + (self.v1.x**self.s1, self.v1.y**self.s1, self.v1.z**self.s1), + ) + self.assertEqual( + self.v1.elementwise() % self.s1, + (self.v1.x % self.s1, self.v1.y % self.s1, self.v1.z % self.s1), + ) + self.assertEqual( + self.v1.elementwise() > self.s1, + self.v1.x > self.s1 and self.v1.y > self.s1 and self.v1.z > self.s1, + ) + self.assertEqual( + self.v1.elementwise() < self.s1, + self.v1.x < self.s1 and self.v1.y < self.s1 and self.v1.z < self.s1, + ) + self.assertEqual( + self.v1.elementwise() == self.s1, + self.v1.x == self.s1 and self.v1.y == self.s1 and self.v1.z == self.s1, + ) + self.assertEqual( + self.v1.elementwise() != self.s1, + self.v1.x != self.s1 and self.v1.y != self.s1 and self.v1.z != self.s1, + ) + self.assertEqual( + self.v1.elementwise() >= self.s1, + self.v1.x >= self.s1 and self.v1.y >= self.s1 and self.v1.z >= self.s1, + ) + self.assertEqual( + self.v1.elementwise() <= self.s1, + self.v1.x <= self.s1 and self.v1.y <= self.s1 and self.v1.z <= self.s1, + ) # behaviour for "scalar op elementwise" self.assertEqual(5 + self.v1.elementwise(), Vector3(5, 5, 5) + self.v1) self.assertEqual(3.5 - self.v1.elementwise(), Vector3(3.5, 3.5, 3.5) - self.v1) - self.assertEqual(7.5 * self.v1.elementwise() , 7.5 * self.v1) - self.assertEqual(-3.5 / self.v1.elementwise(), (-3.5 / self.v1.x, -3.5 / self.v1.y, -3.5 / self.v1.z)) - self.assertEqual(-3.5 // self.v1.elementwise(), (-3.5 // self.v1.x, -3.5 // self.v1.y, -3.5 // self.v1.z)) - self.assertEqual(-3.5 ** self.v1.elementwise(), (-3.5 ** self.v1.x, -3.5 ** self.v1.y, -3.5 ** self.v1.z)) - self.assertEqual(3 % self.v1.elementwise(), (3 % self.v1.x, 3 % self.v1.y, 3 % self.v1.z)) - self.assertEqual(2 < self.v1.elementwise(), 2 < self.v1.x and 2 < self.v1.y and 2 < self.v1.z) - self.assertEqual(2 > self.v1.elementwise(), 2 > self.v1.x and 2 > self.v1.y and 2 > self.v1.z) - self.assertEqual(1 == self.v1.elementwise(), 1 == self.v1.x and 1 == self.v1.y and 1 == self.v1.z) - self.assertEqual(1 != self.v1.elementwise(), 1 != self.v1.x and 1 != self.v1.y and 1 != self.v1.z) - self.assertEqual(2 <= self.v1.elementwise(), 2 <= self.v1.x and 2 <= self.v1.y and 2 <= self.v1.z) - self.assertEqual(-7 >= self.v1.elementwise(), -7 >= self.v1.x and -7 >= self.v1.y and -7 >= self.v1.z) - self.assertEqual(-7 != self.v1.elementwise(), -7 != self.v1.x and -7 != self.v1.y and -7 != self.v1.z) + self.assertEqual(7.5 * self.v1.elementwise(), 7.5 * self.v1) + self.assertEqual( + -3.5 / self.v1.elementwise(), + (-3.5 / self.v1.x, -3.5 / self.v1.y, -3.5 / self.v1.z), + ) + self.assertEqual( + -3.5 // self.v1.elementwise(), + (-3.5 // self.v1.x, -3.5 // self.v1.y, -3.5 // self.v1.z), + ) + self.assertEqual( + -(3.5 ** self.v1.elementwise()), + (-(3.5**self.v1.x), -(3.5**self.v1.y), -(3.5**self.v1.z)), + ) + self.assertEqual( + 3 % self.v1.elementwise(), (3 % self.v1.x, 3 % self.v1.y, 3 % self.v1.z) + ) + self.assertEqual( + 2 < self.v1.elementwise(), 2 < self.v1.x and 2 < self.v1.y and 2 < self.v1.z + ) + self.assertEqual( + 2 > self.v1.elementwise(), 2 > self.v1.x and 2 > self.v1.y and 2 > self.v1.z + ) + self.assertEqual( + 1 == self.v1.elementwise(), + 1 == self.v1.x and 1 == self.v1.y and 1 == self.v1.z, + ) + self.assertEqual( + 1 != self.v1.elementwise(), + 1 != self.v1.x and 1 != self.v1.y and 1 != self.v1.z, + ) + self.assertEqual( + 2 <= self.v1.elementwise(), + 2 <= self.v1.x and 2 <= self.v1.y and 2 <= self.v1.z, + ) + self.assertEqual( + -7 >= self.v1.elementwise(), + -7 >= self.v1.x and -7 >= self.v1.y and -7 >= self.v1.z, + ) + self.assertEqual( + -7 != self.v1.elementwise(), + -7 != self.v1.x and -7 != self.v1.y and -7 != self.v1.z, + ) # behaviour for "elementwise op vector" self.assertEqual(type(self.v1.elementwise() * self.v2), type(self.v1)) self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) self.assertEqual(self.v1.elementwise() + self.v2, self.v1 + self.v2) self.assertEqual(self.v1.elementwise() - self.v2, self.v1 - self.v2) - self.assertEqual(self.v1.elementwise() * self.v2, (self.v1.x * self.v2.x, self.v1.y * self.v2.y, self.v1.z * self.v2.z)) - self.assertEqual(self.v1.elementwise() / self.v2, (self.v1.x / self.v2.x, self.v1.y / self.v2.y, self.v1.z / self.v2.z)) - self.assertEqual(self.v1.elementwise() // self.v2, (self.v1.x // self.v2.x, self.v1.y // self.v2.y, self.v1.z // self.v2.z)) - self.assertEqual(self.v1.elementwise() ** self.v2, (self.v1.x ** self.v2.x, self.v1.y ** self.v2.y, self.v1.z ** self.v2.z)) - self.assertEqual(self.v1.elementwise() % self.v2, (self.v1.x % self.v2.x, self.v1.y % self.v2.y, self.v1.z % self.v2.z)) - self.assertEqual(self.v1.elementwise() > self.v2, self.v1.x > self.v2.x and self.v1.y > self.v2.y and self.v1.z > self.v2.z) - self.assertEqual(self.v1.elementwise() < self.v2, self.v1.x < self.v2.x and self.v1.y < self.v2.y and self.v1.z < self.v2.z) - self.assertEqual(self.v1.elementwise() >= self.v2, self.v1.x >= self.v2.x and self.v1.y >= self.v2.y and self.v1.z >= self.v2.z) - self.assertEqual(self.v1.elementwise() <= self.v2, self.v1.x <= self.v2.x and self.v1.y <= self.v2.y and self.v1.z <= self.v2.z) - self.assertEqual(self.v1.elementwise() == self.v2, self.v1.x == self.v2.x and self.v1.y == self.v2.y and self.v1.z == self.v2.z) - self.assertEqual(self.v1.elementwise() != self.v2, self.v1.x != self.v2.x and self.v1.y != self.v2.y and self.v1.z != self.v2.z) + self.assertEqual( + self.v1.elementwise() * self.v2, + (self.v1.x * self.v2.x, self.v1.y * self.v2.y, self.v1.z * self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() / self.v2, + (self.v1.x / self.v2.x, self.v1.y / self.v2.y, self.v1.z / self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() // self.v2, + (self.v1.x // self.v2.x, self.v1.y // self.v2.y, self.v1.z // self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() ** self.v2, + (self.v1.x**self.v2.x, self.v1.y**self.v2.y, self.v1.z**self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() % self.v2, + (self.v1.x % self.v2.x, self.v1.y % self.v2.y, self.v1.z % self.v2.z), + ) + self.assertEqual( + self.v1.elementwise() > self.v2, + self.v1.x > self.v2.x and self.v1.y > self.v2.y and self.v1.z > self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() < self.v2, + self.v1.x < self.v2.x and self.v1.y < self.v2.y and self.v1.z < self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() >= self.v2, + self.v1.x >= self.v2.x + and self.v1.y >= self.v2.y + and self.v1.z >= self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() <= self.v2, + self.v1.x <= self.v2.x + and self.v1.y <= self.v2.y + and self.v1.z <= self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() == self.v2, + self.v1.x == self.v2.x + and self.v1.y == self.v2.y + and self.v1.z == self.v2.z, + ) + self.assertEqual( + self.v1.elementwise() != self.v2, + self.v1.x != self.v2.x + and self.v1.y != self.v2.y + and self.v1.z != self.v2.z, + ) # behaviour for "vector op elementwise" self.assertEqual(self.v2 + self.v1.elementwise(), self.v2 + self.v1) self.assertEqual(self.v2 - self.v1.elementwise(), self.v2 - self.v1) - self.assertEqual(self.v2 * self.v1.elementwise(), (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z)) - self.assertEqual(self.v2 / self.v1.elementwise(), (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z)) - self.assertEqual(self.v2 // self.v1.elementwise(), (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z)) - self.assertEqual(self.v2 ** self.v1.elementwise(), (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y, self.v2.z ** self.v1.z)) - self.assertEqual(self.v2 % self.v1.elementwise(), (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z)) - self.assertEqual(self.v2 < self.v1.elementwise(), self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z) - self.assertEqual(self.v2 > self.v1.elementwise(), self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z) - self.assertEqual(self.v2 <= self.v1.elementwise(), self.v2.x <= self.v1.x and self.v2.y <= self.v1.y and self.v2.z <= self.v1.z) - self.assertEqual(self.v2 >= self.v1.elementwise(), self.v2.x >= self.v1.x and self.v2.y >= self.v1.y and self.v2.z >= self.v1.z) - self.assertEqual(self.v2 == self.v1.elementwise(), self.v2.x == self.v1.x and self.v2.y == self.v1.y and self.v2.z == self.v1.z) - self.assertEqual(self.v2 != self.v1.elementwise(), self.v2.x != self.v1.x and self.v2.y != self.v1.y and self.v2.z != self.v1.z) + self.assertEqual( + self.v2 * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z), + ) + self.assertEqual( + self.v2 / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z), + ) + self.assertEqual( + self.v2 // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z), + ) + self.assertEqual( + self.v2 ** self.v1.elementwise(), + (self.v2.x**self.v1.x, self.v2.y**self.v1.y, self.v2.z**self.v1.z), + ) + self.assertEqual( + self.v2 % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z), + ) + self.assertEqual( + self.v2 < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z, + ) + self.assertEqual( + self.v2 > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z, + ) + self.assertEqual( + self.v2 <= self.v1.elementwise(), + self.v2.x <= self.v1.x + and self.v2.y <= self.v1.y + and self.v2.z <= self.v1.z, + ) + self.assertEqual( + self.v2 >= self.v1.elementwise(), + self.v2.x >= self.v1.x + and self.v2.y >= self.v1.y + and self.v2.z >= self.v1.z, + ) + self.assertEqual( + self.v2 == self.v1.elementwise(), + self.v2.x == self.v1.x + and self.v2.y == self.v1.y + and self.v2.z == self.v1.z, + ) + self.assertEqual( + self.v2 != self.v1.elementwise(), + self.v2.x != self.v1.x + and self.v2.y != self.v1.y + and self.v2.z != self.v1.z, + ) # behaviour for "elementwise op elementwise" - self.assertEqual(self.v2.elementwise() + self.v1.elementwise(), self.v2 + self.v1) - self.assertEqual(self.v2.elementwise() - self.v1.elementwise(), self.v2 - self.v1) - self.assertEqual(self.v2.elementwise() * self.v1.elementwise(), - (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z)) - self.assertEqual(self.v2.elementwise() / self.v1.elementwise(), - (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z)) - self.assertEqual(self.v2.elementwise() // self.v1.elementwise(), - (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z)) - self.assertEqual(self.v2.elementwise() ** self.v1.elementwise(), - (self.v2.x ** self.v1.x, self.v2.y ** self.v1.y, self.v2.z ** self.v1.z)) - self.assertEqual(self.v2.elementwise() % self.v1.elementwise(), - (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z)) - self.assertEqual(self.v2.elementwise() < self.v1.elementwise(), - self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z) - self.assertEqual(self.v2.elementwise() > self.v1.elementwise(), - self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z) - self.assertEqual(self.v2.elementwise() <= self.v1.elementwise(), - self.v2.x <= self.v1.x and self.v2.y <= self.v1.y and self.v2.z <= self.v1.z) - self.assertEqual(self.v2.elementwise() >= self.v1.elementwise(), - self.v2.x >= self.v1.x and self.v2.y >= self.v1.y and self.v2.z >= self.v1.z) - self.assertEqual(self.v2.elementwise() == self.v1.elementwise(), - self.v2.x == self.v1.x and self.v2.y == self.v1.y and self.v2.z == self.v1.z) - self.assertEqual(self.v2.elementwise() != self.v1.elementwise(), - self.v2.x != self.v1.x and self.v2.y != self.v1.y and self.v2.z != self.v1.z) + self.assertEqual( + self.v2.elementwise() + self.v1.elementwise(), self.v2 + self.v1 + ) + self.assertEqual( + self.v2.elementwise() - self.v1.elementwise(), self.v2 - self.v1 + ) + self.assertEqual( + self.v2.elementwise() * self.v1.elementwise(), + (self.v2.x * self.v1.x, self.v2.y * self.v1.y, self.v2.z * self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() / self.v1.elementwise(), + (self.v2.x / self.v1.x, self.v2.y / self.v1.y, self.v2.z / self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() // self.v1.elementwise(), + (self.v2.x // self.v1.x, self.v2.y // self.v1.y, self.v2.z // self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() ** self.v1.elementwise(), + (self.v2.x**self.v1.x, self.v2.y**self.v1.y, self.v2.z**self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() % self.v1.elementwise(), + (self.v2.x % self.v1.x, self.v2.y % self.v1.y, self.v2.z % self.v1.z), + ) + self.assertEqual( + self.v2.elementwise() < self.v1.elementwise(), + self.v2.x < self.v1.x and self.v2.y < self.v1.y and self.v2.z < self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() > self.v1.elementwise(), + self.v2.x > self.v1.x and self.v2.y > self.v1.y and self.v2.z > self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() <= self.v1.elementwise(), + self.v2.x <= self.v1.x + and self.v2.y <= self.v1.y + and self.v2.z <= self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() >= self.v1.elementwise(), + self.v2.x >= self.v1.x + and self.v2.y >= self.v1.y + and self.v2.z >= self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() == self.v1.elementwise(), + self.v2.x == self.v1.x + and self.v2.y == self.v1.y + and self.v2.z == self.v1.z, + ) + self.assertEqual( + self.v2.elementwise() != self.v1.elementwise(), + self.v2.x != self.v1.x + and self.v2.y != self.v1.y + and self.v2.z != self.v1.z, + ) # other behaviour - self.assertEqual(abs(self.v1.elementwise()), (abs(self.v1.x), abs(self.v1.y), abs(self.v1.z))) + self.assertEqual( + abs(self.v1.elementwise()), (abs(self.v1.x), abs(self.v1.y), abs(self.v1.z)) + ) self.assertEqual(-self.v1.elementwise(), -self.v1) self.assertEqual(+self.v1.elementwise(), +self.v1) self.assertEqual(bool(self.v1.elementwise()), bool(self.v1)) self.assertEqual(bool(Vector3().elementwise()), bool(Vector3())) self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1, 1)) - self.assertRaises(ValueError, lambda : pow(Vector3(-1, 0, 0).elementwise(), 1.2)) - self.assertRaises(ZeroDivisionError, lambda : self.zeroVec.elementwise() ** -1) - self.assertRaises(ZeroDivisionError, lambda : Vector3(1,1,1).elementwise() / 0) - self.assertRaises(ZeroDivisionError, lambda : Vector3(1,1,1).elementwise() // 0) - self.assertRaises(ZeroDivisionError, lambda : Vector3(1,1,1).elementwise() % 0) - self.assertRaises(ZeroDivisionError, lambda : Vector3(1,1,1).elementwise() / self.zeroVec) - self.assertRaises(ZeroDivisionError, lambda : Vector3(1,1,1).elementwise() // self.zeroVec) - self.assertRaises(ZeroDivisionError, lambda : Vector3(1,1,1).elementwise() % self.zeroVec) - self.assertRaises(ZeroDivisionError, lambda : 2 / self.zeroVec.elementwise()) - self.assertRaises(ZeroDivisionError, lambda : 2 // self.zeroVec.elementwise()) - self.assertRaises(ZeroDivisionError, lambda : 2 % self.zeroVec.elementwise()) - + self.assertRaises(ValueError, lambda: pow(Vector3(-1, 0, 0).elementwise(), 1.2)) + self.assertRaises(ZeroDivisionError, lambda: self.zeroVec.elementwise() ** -1) + self.assertRaises(ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() / 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() // 0 + ) + self.assertRaises(ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() % 0) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() / self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() // self.zeroVec + ) + self.assertRaises( + ZeroDivisionError, lambda: Vector3(1, 1, 1).elementwise() % self.zeroVec + ) + self.assertRaises(ZeroDivisionError, lambda: 2 / self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 // self.zeroVec.elementwise()) + self.assertRaises(ZeroDivisionError, lambda: 2 % self.zeroVec.elementwise()) def test_slerp(self): - self.assertRaises(ValueError, lambda : self.zeroVec.slerp(self.v1, .5)) - self.assertRaises(ValueError, lambda : self.v1.slerp(self.zeroVec, .5)) - self.assertRaises(ValueError, - lambda : self.zeroVec.slerp(self.zeroVec, .5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.v1, 0.5)) + self.assertRaises(ValueError, lambda: self.v1.slerp(self.zeroVec, 0.5)) + self.assertRaises(ValueError, lambda: self.zeroVec.slerp(self.zeroVec, 0.5)) steps = 10 angle_step = self.e1.angle_to(self.e2) / steps - for i, u in ((i, self.e1.slerp(self.e2, i/float(steps))) for i in range(steps+1)): + for i, u in ( + (i, self.e1.slerp(self.e2, i / float(steps))) for i in range(steps + 1) + ): self.assertAlmostEqual(u.length(), 1) self.assertAlmostEqual(self.e1.angle_to(u), i * angle_step) self.assertEqual(u, self.e2) @@ -1539,18 +2535,21 @@ class Vector3TypeTest(unittest.TestCase): v1 = Vector3(100, 0, 0) v2 = Vector3(0, 10, 7) radial_factor = v2.length() / v1.length() - for i, u in ((i, v1.slerp(v2, -i/float(steps))) for i in range(steps+1)): - self.assertAlmostEqual(u.length(), (v2.length() - v1.length()) * (float(i)/steps) + v1.length()) + for i, u in ((i, v1.slerp(v2, -i / float(steps))) for i in range(steps + 1)): + self.assertAlmostEqual( + u.length(), + (v2.length() - v1.length()) * (float(i) / steps) + v1.length(), + ) self.assertEqual(u, v2) - self.assertEqual(v1.slerp(v1, .5), v1) - self.assertEqual(v2.slerp(v2, .5), v2) - self.assertRaises(ValueError, lambda : v1.slerp(-v1, 0.5)) + self.assertEqual(v1.slerp(v1, 0.5), v1) + self.assertEqual(v2.slerp(v2, 0.5), v2) + self.assertRaises(ValueError, lambda: v1.slerp(-v1, 0.5)) def test_lerp(self): v1 = Vector3(0, 0, 0) v2 = Vector3(10, 10, 10) self.assertEqual(v1.lerp(v2, 0.5), (5, 5, 5)) - self.assertRaises(ValueError, lambda : v1.lerp(v2, 2.5)) + self.assertRaises(ValueError, lambda: v1.lerp(v2, 2.5)) v1 = Vector3(-10, -5, -20) v2 = Vector3(10, 10, -20) @@ -1560,53 +2559,372 @@ class Vector3TypeTest(unittest.TestCase): v = Vector3() v.from_spherical(self.v1.as_spherical()) self.assertEqual(self.v1, v) + self.assertEqual(self.v1, Vector3.from_spherical(self.v1.as_spherical())) self.assertEqual(self.e1.as_spherical(), (1, 90, 0)) self.assertEqual(self.e2.as_spherical(), (1, 90, 90)) self.assertEqual(self.e3.as_spherical(), (1, 0, 0)) self.assertEqual((2 * self.e2).as_spherical(), (2, 90, 90)) - self.assertRaises(TypeError, lambda : v.from_spherical((None, None, None))) - self.assertRaises(TypeError, lambda : v.from_spherical("abc")) - self.assertRaises(TypeError, lambda : v.from_spherical((None, 1, 2))) - self.assertRaises(TypeError, lambda : v.from_spherical((1, 2, 3, 4))) - self.assertRaises(TypeError, lambda : v.from_spherical((1, 2))) - self.assertRaises(TypeError, lambda : v.from_spherical(1, 2, 3)) - v.from_spherical((.5, 90, 90)) - self.assertEqual(v, .5 * self.e2) + self.assertRaises(TypeError, lambda: v.from_spherical((None, None, None))) + self.assertRaises(TypeError, lambda: v.from_spherical("abc")) + self.assertRaises(TypeError, lambda: v.from_spherical((None, 1, 2))) + self.assertRaises(TypeError, lambda: v.from_spherical((1, 2, 3, 4))) + self.assertRaises(TypeError, lambda: v.from_spherical((1, 2))) + self.assertRaises(TypeError, lambda: v.from_spherical(1, 2, 3)) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((None, None, None))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical("abc")) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((None, 1, 2))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((1, 2, 3, 4))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical((1, 2))) + self.assertRaises(TypeError, lambda: Vector3.from_spherical(1, 2, 3)) + v.from_spherical((0.5, 90, 90)) + self.assertEqual(v, 0.5 * self.e2) + self.assertEqual(Vector3.from_spherical((0.5, 90, 90)), 0.5 * self.e2) + self.assertEqual(Vector3.from_spherical((0.5, 90, 90)), v) def test_inplace_operators(self): - - v = Vector3(1,1,1) + v = Vector3(1, 1, 1) v *= 2 - self.assertEqual(v, (2.0,2.0,2.0)) + self.assertEqual(v, (2.0, 2.0, 2.0)) - v = Vector3(4,4,4) + v = Vector3(4, 4, 4) v /= 2 - self.assertEqual(v, (2.0,2.0,2.0)) + self.assertEqual(v, (2.0, 2.0, 2.0)) + v = Vector3(3.0, 3.0, 3.0) + v -= (1, 1, 1) + self.assertEqual(v, (2.0, 2.0, 2.0)) - v = Vector3(3.0,3.0,3.0) - v -= (1,1,1) - self.assertEqual(v, (2.0,2.0,2.0)) - - v = Vector3(3.0,3.0,3.0) - v += (1,1,1) - self.assertEqual(v, (4.0,4.0,4.0)) + v = Vector3(3.0, 3.0, 3.0) + v += (1, 1, 1) + self.assertEqual(v, (4.0, 4.0, 4.0)) def test_pickle(self): import pickle + v2 = Vector2(1, 2) v3 = Vector3(1, 2, 3) self.assertEqual(pickle.loads(pickle.dumps(v2)), v2) self.assertEqual(pickle.loads(pickle.dumps(v3)), v3) - def test_subclass_operation(self): class Vector(pygame.math.Vector3): pass + v = Vector(2.0, 2.0, 2.0) v *= 2 self.assertEqual(v, (4.0, 4.0, 4.0)) + def test_swizzle_constants(self): + """We can get constant values from a swizzle.""" + v = Vector2(7, 6) + self.assertEqual( + v.xy1, + (7.0, 6.0, 1.0), + ) + + def test_swizzle_four_constants(self): + """We can get 4 constant values from a swizzle.""" + v = Vector2(7, 6) + self.assertEqual( + v.xy01, + (7.0, 6.0, 0.0, 1.0), + ) + + def test_swizzle_oob(self): + """An out-of-bounds swizzle raises an AttributeError.""" + v = Vector2(7, 6) + with self.assertRaises(AttributeError): + v.xyz -if __name__ == '__main__': + @unittest.skipIf(IS_PYPY, "known pypy failure") + def test_swizzle_set_oob(self): + """An out-of-bounds swizzle set raises an AttributeError.""" + v = Vector2(7, 6) + with self.assertRaises(AttributeError): + v.xz = (1, 1) + + def test_project_v3_onto_x_axis(self): + """Project onto x-axis, e.g. get the component pointing in the x-axis direction.""" + # arrange + v = Vector3(2, 3, 4) + x_axis = Vector3(10, 0, 0) + + # act + actual = v.project(x_axis) + + # assert + self.assertEqual(v.x, actual.x) + self.assertEqual(0, actual.y) + self.assertEqual(0, actual.z) + + def test_project_v3_onto_y_axis(self): + """Project onto y-axis, e.g. get the component pointing in the y-axis direction.""" + # arrange + v = Vector3(2, 3, 4) + y_axis = Vector3(0, 100, 0) + + # act + actual = v.project(y_axis) + + # assert + self.assertEqual(0, actual.x) + self.assertEqual(v.y, actual.y) + self.assertEqual(0, actual.z) + + def test_project_v3_onto_z_axis(self): + """Project onto z-axis, e.g. get the component pointing in the z-axis direction.""" + # arrange + v = Vector3(2, 3, 4) + y_axis = Vector3(0, 0, 77) + + # act + actual = v.project(y_axis) + + # assert + self.assertEqual(0, actual.x) + self.assertEqual(0, actual.y) + self.assertEqual(v.z, actual.z) + + def test_project_v3_onto_other(self): + """Project onto other vector.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(3, 5, 7) + + # act + actual = v.project(other) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertAlmostEqual(expected.x, actual.x) + self.assertAlmostEqual(expected.y, actual.y) + self.assertAlmostEqual(expected.z, actual.z) + + def test_project_v3_onto_other_as_tuple(self): + """Project onto other tuple as vector.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(3, 5, 7) + + # act + actual = v.project(tuple(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertAlmostEqual(expected.x, actual.x) + self.assertAlmostEqual(expected.y, actual.y) + self.assertAlmostEqual(expected.z, actual.z) + + def test_project_v3_onto_other_as_list(self): + """Project onto other list as vector.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(3, 5, 7) + + # act + actual = v.project(list(other)) + + # assert + expected = v.dot(other) / other.dot(other) * other + self.assertAlmostEqual(expected.x, actual.x) + self.assertAlmostEqual(expected.y, actual.y) + self.assertAlmostEqual(expected.z, actual.z) + + def test_project_v3_raises_if_other_has_zero_length(self): + """Check if exception is raise when projected on vector has zero length.""" + # arrange + v = Vector3(2, 3, 4) + other = Vector3(0, 0, 0) + + # act / assert + self.assertRaises(ValueError, v.project, other) + + def test_project_v3_raises_if_other_is_not_iterable(self): + """Check if exception is raise when projected on vector is not iterable.""" + # arrange + v = Vector3(2, 3, 4) + other = 10 + + # act / assert + self.assertRaises(TypeError, v.project, other) + + def test_collection_abc(self): + v = Vector3(3, 4, 5) + self.assertTrue(isinstance(v, Collection)) + self.assertFalse(isinstance(v, Sequence)) + + def test_clamp_mag_v3_max(self): + v1 = Vector3(7, 2, 2) + v2 = v1.clamp_magnitude(5) + v3 = v1.clamp_magnitude(0, 5) + self.assertEqual(v2, v3) + + v1.clamp_magnitude_ip(5) + self.assertEqual(v1, v2) + + v1.clamp_magnitude_ip(0, 5) + self.assertEqual(v1, v2) + + expected_v2 = Vector3(4.635863249727653, 1.3245323570650438, 1.3245323570650438) + self.assertEqual(expected_v2, v2) + + def test_clamp_mag_v3_min(self): + v1 = Vector3(3, 1, 2) + v2 = v1.clamp_magnitude(5, 10) + v1.clamp_magnitude_ip(5, 10) + expected_v2 = Vector3(4.008918628686366, 1.3363062095621219, 2.6726124191242437) + self.assertEqual(expected_v2, v1) + self.assertEqual(expected_v2, v2) + + def test_clamp_mag_v3_no_change(self): + v1 = Vector3(1, 2, 3) + for args in ( + (1, 6), + (1.12, 5.55), + (0.93, 6.83), + (7.6,), + ): + with self.subTest(args=args): + v2 = v1.clamp_magnitude(*args) + v1.clamp_magnitude_ip(*args) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector3(1, 2, 3)) + + def test_clamp_mag_v3_edge_cases(self): + v1 = Vector3(1, 2, 1) + v2 = v1.clamp_magnitude(6, 6) + v1.clamp_magnitude_ip(6, 6) + self.assertEqual(v1, v2) + self.assertAlmostEqual(v1.length(), 6) + + v2 = v1.clamp_magnitude(0) + v1.clamp_magnitude_ip(0, 0) + self.assertEqual(v1, v2) + self.assertEqual(v1, Vector3()) + + def test_clamp_mag_v3_errors(self): + v1 = Vector3(1, 2, 2) + for invalid_args in ( + ("foo", "bar"), + (1, 2, 3), + (342.234, "test"), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(TypeError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(TypeError, v1.clamp_magnitude_ip, *invalid_args) + + for invalid_args in ( + (-1,), + (4, 3), # min > max + (-4, 10), + (-4, -2), + ): + with self.subTest(invalid_args=invalid_args): + self.assertRaises(ValueError, v1.clamp_magnitude, *invalid_args) + self.assertRaises(ValueError, v1.clamp_magnitude_ip, *invalid_args) + + # 0 vector + v2 = Vector3() + self.assertRaises(ValueError, v2.clamp_magnitude, 3) + self.assertRaises(ValueError, v2.clamp_magnitude_ip, 4) + + def test_subclassing_v3(self): + """Check if Vector3 is subclassable""" + v = Vector3(4, 2, 0) + + class TestVector(Vector3): + def supermariobrosiscool(self): + return 722 + + other = TestVector(4, 1, 0) + + self.assertEqual(other.supermariobrosiscool(), 722) + self.assertNotEqual(type(v), TestVector) + self.assertNotEqual(type(v), type(other.copy())) + self.assertEqual(TestVector, type(other.reflect(v))) + self.assertEqual(TestVector, type(other.lerp(v, 1))) + self.assertEqual(TestVector, type(other.slerp(v, 1))) + self.assertEqual(TestVector, type(other.rotate(5, v))) + self.assertEqual(TestVector, type(other.rotate_rad(5, v))) + self.assertEqual(TestVector, type(other.project(v))) + self.assertEqual(TestVector, type(other.move_towards(v, 5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(5))) + self.assertEqual(TestVector, type(other.clamp_magnitude(1, 5))) + self.assertEqual(TestVector, type(other.elementwise() + other)) + + other1 = TestVector(4, 2, 0) + + self.assertEqual(type(other + other1), TestVector) + self.assertEqual(type(other - other1), TestVector) + self.assertEqual(type(other * 3), TestVector) + self.assertEqual(type(other / 3), TestVector) + self.assertEqual(type(other.elementwise() ** 3), TestVector) + + def test_move_towards_basic(self): + expected = Vector3(7.93205057, 2006.38284641, 43.80780420) + origin = Vector3(7.22, 2004.0, 42.13) + target = Vector3(12.30, 2021.0, 54.1) + change_ip = origin.copy() + + change = origin.move_towards(target, 3) + change_ip.move_towards_ip(target, 3) + + self.assertEqual(change, expected) + self.assertEqual(change_ip, expected) + + def test_move_towards_max_distance(self): + expected = Vector3(12.30, 2021, 42.5) + origin = Vector3(7.22, 2004.0, 17.5) + change_ip = origin.copy() + + change = origin.move_towards(expected, 100) + change_ip.move_towards_ip(expected, 100) + + self.assertEqual(change, expected) + self.assertEqual(change_ip, expected) + + def test_move_nowhere(self): + origin = Vector3(7.22, 2004.0, 24.5) + target = Vector3(12.30, 2021.0, 3.2) + change_ip = origin.copy() + + change = origin.move_towards(target, 0) + change_ip.move_towards_ip(target, 0) + + self.assertEqual(change, origin) + self.assertEqual(change_ip, origin) + + def test_move_away(self): + expected = Vector3(6.74137906, 2002.39831577, 49.70890994) + origin = Vector3(7.22, 2004.0, 52.2) + target = Vector3(12.30, 2021.0, 78.64) + change_ip = origin.copy() + + change = origin.move_towards(target, -3) + change_ip.move_towards_ip(target, -3) + + self.assertEqual(change, expected) + self.assertEqual(change_ip, expected) + + def test_move_towards_self(self): + vec = Vector3(6.36, 2001.13, -123.14) + vec2 = vec.copy() + for dist in (-3.54, -1, 0, 0.234, 12): + self.assertEqual(vec.move_towards(vec2, dist), vec) + vec2.move_towards_ip(vec, dist) + self.assertEqual(vec, vec2) + + def test_move_towards_errors(self): + origin = Vector3(7.22, 2004.0, 4.1) + target = Vector3(12.30, 2021.0, -421.5) + + self.assertRaises(TypeError, origin.move_towards, target, 3, 2) + self.assertRaises(TypeError, origin.move_towards_ip, target, 3, 2) + self.assertRaises(TypeError, origin.move_towards, target, "a") + self.assertRaises(TypeError, origin.move_towards_ip, target, "b") + self.assertRaises(TypeError, origin.move_towards, "c", 3) + self.assertRaises(TypeError, origin.move_towards_ip, "d", 3) + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/midi_tags.py b/venv/Lib/site-packages/pygame/tests/midi_tags.py deleted file mode 100644 index c6c945490ffc08401e71b34b9f1382ddec4aba4d..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/midi_tags.py +++ /dev/null @@ -1 +0,0 @@ -__tags__ = ['interactive'] diff --git a/venv/Lib/site-packages/pygame/tests/midi_test.py b/venv/Lib/site-packages/pygame/tests/midi_test.py index b8d1b6affca846bbe61274b46e3ad9c00040ce04..f4189a235127e6c9b900b6a9353b8fa02c2d3bd7 100644 --- a/venv/Lib/site-packages/pygame/tests/midi_test.py +++ b/venv/Lib/site-packages/pygame/tests/midi_test.py @@ -1,17 +1,15 @@ import unittest -import os -import sys -import time + import pygame -import pygame.midi -import pygame.compat -from pygame.locals import * class MidiInputTest(unittest.TestCase): + __tags__ = ["interactive"] def setUp(self): + import pygame.midi + pygame.midi.init() in_id = pygame.midi.get_default_input_id() if in_id != -1: @@ -25,9 +23,6 @@ class MidiInputTest(unittest.TestCase): pygame.midi.quit() def test_Input(self): - """|tags: interactive| - """ - i = pygame.midi.get_default_input_id() if self.midi_input: self.assertEqual(self.midi_input.device_id, i) @@ -43,9 +38,8 @@ class MidiInputTest(unittest.TestCase): self.assertRaises(OverflowError, pygame.midi.Input, pow(2, 99)) def test_poll(self): - if not self.midi_input: - self.skipTest('No midi Input device') + self.skipTest("No midi Input device") self.assertFalse(self.midi_input.poll()) # TODO fake some incoming data @@ -56,9 +50,8 @@ class MidiInputTest(unittest.TestCase): self.midi_input = None def test_read(self): - if not self.midi_input: - self.skipTest('No midi Input device') + self.skipTest("No midi Input device") read = self.midi_input.read(5) self.assertEqual(read, []) @@ -71,7 +64,7 @@ class MidiInputTest(unittest.TestCase): def test_close(self): if not self.midi_input: - self.skipTest('No midi Input device') + self.skipTest("No midi Input device") self.assertIsNotNone(self.midi_input._input) self.midi_input.close() @@ -79,8 +72,11 @@ class MidiInputTest(unittest.TestCase): class MidiOutputTest(unittest.TestCase): + __tags__ = ["interactive"] def setUp(self): + import pygame.midi + pygame.midi.init() m_out_id = pygame.midi.get_default_output_id() if m_out_id != -1: @@ -94,8 +90,6 @@ class MidiOutputTest(unittest.TestCase): pygame.midi.quit() def test_Output(self): - """|tags: interactive| - """ i = pygame.midi.get_default_output_id() if self.midi_output: self.assertEqual(self.midi_output.device_id, i) @@ -107,13 +101,10 @@ class MidiOutputTest(unittest.TestCase): self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, i) self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, 9009) self.assertRaises(pygame.midi.MidiException, pygame.midi.Output, -1) - self.assertRaises(TypeError, pygame.midi.Output,"1234") - self.assertRaises(OverflowError, pygame.midi.Output, pow(2,99)) + self.assertRaises(TypeError, pygame.midi.Output, "1234") + self.assertRaises(OverflowError, pygame.midi.Output, pow(2, 99)) def test_note_off(self): - """|tags: interactive| - """ - if self.midi_output: out = self.midi_output out.note_on(5, 30, 0) @@ -126,9 +117,6 @@ class MidiOutputTest(unittest.TestCase): self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") def test_note_on(self): - """|tags: interactive| - """ - if self.midi_output: out = self.midi_output out.note_on(5, 30, 0) @@ -141,9 +129,8 @@ class MidiOutputTest(unittest.TestCase): self.assertEqual(str(cm.exception), "Channel not between 0 and 15.") def test_set_instrument(self): - if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") out = self.midi_output out.set_instrument(5) out.set_instrument(42, channel=2) @@ -162,19 +149,16 @@ class MidiOutputTest(unittest.TestCase): def test_write(self): if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") out = self.midi_output - out.write([[[0xc0, 0, 0], 20000]]) + out.write([[[0xC0, 0, 0], 20000]]) # is equivalent to - out.write([[[0xc0], 20000]]) + out.write([[[0xC0], 20000]]) # example from the docstring : # 1. choose program change 1 at time 20000 and # 2. send note 65 with velocity 100 500 ms later - out.write([ - [[0xc0, 0, 0], 20000], - [[0x90, 60, 100], 20500] - ]) + out.write([[[0xC0, 0, 0], 20000], [[0x90, 60, 100], 20500]]) out.write([]) verrry_long = [[[0x90, 60, i % 100], 20000 + 100 * i] for i in range(1024)] @@ -184,7 +168,7 @@ class MidiOutputTest(unittest.TestCase): self.assertRaises(IndexError, out.write, too_long) # test wrong data with self.assertRaises(TypeError) as cm: - out.write('Non sens ?') + out.write("Non sens ?") error_msg = "unsupported operand type(s) for &: 'str' and 'int'" self.assertEqual(str(cm.exception), error_msg) @@ -193,14 +177,12 @@ class MidiOutputTest(unittest.TestCase): self.assertEqual(str(cm.exception), error_msg) def test_write_short(self): - """|tags: interactive| - """ if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") out = self.midi_output # program change - out.write_short(0xc0) + out.write_short(0xC0) # put a note on, then off. out.write_short(0x90, 65, 100) out.write_short(0x80, 65, 100) @@ -208,16 +190,15 @@ class MidiOutputTest(unittest.TestCase): def test_write_sys_ex(self): if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") out = self.midi_output - out.write_sys_ex(pygame.midi.time(), - [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7]) + out.write_sys_ex(pygame.midi.time(), [0xF0, 0x7D, 0x10, 0x11, 0x12, 0x13, 0xF7]) def test_pitch_bend(self): # FIXME : pitch_bend in the code, but not in documentation if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") out = self.midi_output with self.assertRaises(ValueError) as cm: @@ -227,50 +208,50 @@ class MidiOutputTest(unittest.TestCase): out.pitch_bend(5, channel=16) with self.assertRaises(ValueError) as cm: out.pitch_bend(-10001, 1) - self.assertEqual(str(cm.exception), "Pitch bend value must be between " - "-8192 and +8191, not -10001.") + self.assertEqual( + str(cm.exception), + "Pitch bend value must be between " "-8192 and +8191, not -10001.", + ) with self.assertRaises(ValueError) as cm: out.pitch_bend(10665, 2) def test_close(self): if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") self.assertIsNotNone(self.midi_output._output) self.midi_output.close() self.assertIsNone(self.midi_output._output) def test_abort(self): if not self.midi_output: - self.skipTest('No midi device') + self.skipTest("No midi device") self.assertEqual(self.midi_output._aborted, 0) self.midi_output.abort() self.assertEqual(self.midi_output._aborted, 1) class MidiModuleTest(unittest.TestCase): + """Midi module tests that require midi hardware or midi.init(). + + See MidiModuleNonInteractiveTest for non-interactive module tests. + """ + + __tags__ = ["interactive"] def setUp(self): + import pygame.midi + pygame.midi.init() def tearDown(self): pygame.midi.quit() - def test_MidiException(self): - - def raiseit(): - raise pygame.midi.MidiException('Hello Midi param') - - with self.assertRaises(pygame.midi.MidiException) as cm: - raiseit() - self.assertEqual(cm.exception.parameter, 'Hello Midi param') - def test_get_count(self): c = pygame.midi.get_count() self.assertIsInstance(c, int) self.assertTrue(c >= 0) def test_get_default_input_id(self): - midin_id = pygame.midi.get_default_input_id() # if there is a not None return make sure it is an int. self.assertIsInstance(midin_id, int) @@ -279,7 +260,6 @@ class MidiModuleTest(unittest.TestCase): self.assertRaises(RuntimeError, pygame.midi.get_default_output_id) def test_get_default_output_id(self): - c = pygame.midi.get_default_output_id() self.assertIsInstance(c, int) self.assertTrue(c >= -1) @@ -287,7 +267,6 @@ class MidiModuleTest(unittest.TestCase): self.assertRaises(RuntimeError, pygame.midi.get_default_output_id) def test_get_device_info(self): - an_id = pygame.midi.get_default_output_id() if an_id != -1: interf, name, input, output, opened = pygame.midi.get_device_info(an_id) @@ -311,7 +290,6 @@ class MidiModuleTest(unittest.TestCase): self.assertIsNone(info) def test_init(self): - pygame.midi.quit() self.assertRaises(RuntimeError, pygame.midi.get_count) # initialising many times should be fine. @@ -322,27 +300,8 @@ class MidiModuleTest(unittest.TestCase): self.assertTrue(pygame.midi.get_init()) - def test_midis2events(self): - - midi_data = ([[0xc0, 0, 1, 2], 20000], - [[0x90, 60, 100, 'blablabla'], 20000] - ) - events = pygame.midi.midis2events(midi_data, 2) - self.assertEqual(len(events), 2) - - for eve in events: - # pygame.event.Event is a function, but ... - self.assertEqual(eve.__class__.__name__, 'Event') - self.assertEqual(eve.vice_id, 2) - # FIXME I don't know what we want for the Event.timestamp - # For now it accepts it accepts int as is: - self.assertIsInstance(eve.timestamp, int) - self.assertEqual(eve.timestamp, 20000) - self.assertEqual(events[1].data3, 'blablabla') - def test_quit(self): - - # It is safe to call this more than once. + # It is safe to call this more than once. pygame.midi.quit() pygame.midi.init() pygame.midi.quit() @@ -358,28 +317,147 @@ class MidiModuleTest(unittest.TestCase): self.assertTrue(pygame.midi.get_init()) def test_time(self): - mtime = pygame.midi.time() self.assertIsInstance(mtime, int) # should be close to 2-3... since the timer is just init'd. self.assertTrue(0 <= mtime < 100) - def test_conversions(self): - """ of frequencies to midi note numbers and ansi note names. +class MidiModuleNonInteractiveTest(unittest.TestCase): + """Midi module tests that do not require midi hardware or midi.init(). + + See MidiModuleTest for interactive module tests. + """ + + def setUp(self): + import pygame.midi + + def test_midiin(self): + """Ensures the MIDIIN event id exists in the midi module. + + The MIDIIN event id can be accessed via the midi module for backward + compatibility. """ - from pygame.midi import ( - frequency_to_midi, midi_to_frequency, midi_to_ansi_note + self.assertEqual(pygame.midi.MIDIIN, pygame.MIDIIN) + self.assertEqual(pygame.midi.MIDIIN, pygame.locals.MIDIIN) + + self.assertNotEqual(pygame.midi.MIDIIN, pygame.MIDIOUT) + self.assertNotEqual(pygame.midi.MIDIIN, pygame.locals.MIDIOUT) + + def test_midiout(self): + """Ensures the MIDIOUT event id exists in the midi module. + + The MIDIOUT event id can be accessed via the midi module for backward + compatibility. + """ + self.assertEqual(pygame.midi.MIDIOUT, pygame.MIDIOUT) + self.assertEqual(pygame.midi.MIDIOUT, pygame.locals.MIDIOUT) + + self.assertNotEqual(pygame.midi.MIDIOUT, pygame.MIDIIN) + self.assertNotEqual(pygame.midi.MIDIOUT, pygame.locals.MIDIIN) + + def test_MidiException(self): + """Ensures the MidiException is raised as expected.""" + + def raiseit(): + raise pygame.midi.MidiException("Hello Midi param") + + with self.assertRaises(pygame.midi.MidiException) as cm: + raiseit() + + self.assertEqual(cm.exception.parameter, "Hello Midi param") + + def test_midis2events(self): + """Ensures midi events are properly converted to pygame events.""" + # List/tuple indexes. + MIDI_DATA = 0 + MD_STATUS = 0 + MD_DATA1 = 1 + MD_DATA2 = 2 + MD_DATA3 = 3 + + TIMESTAMP = 1 + + # Midi events take the form of: + # ((status, data1, data2, data3), timestamp) + midi_events = ( + ((0xC0, 0, 1, 2), 20000), + ((0x90, 60, 1000, "string_data"), 20001), + (("0", "1", "2", "3"), "4"), ) + expected_num_events = len(midi_events) + + # Test different device ids. + for device_id in range(3): + pg_events = pygame.midi.midis2events(midi_events, device_id) + + self.assertEqual(len(pg_events), expected_num_events) + + for i, pg_event in enumerate(pg_events): + # Get the original midi data for comparison. + midi_event = midi_events[i] + midi_event_data = midi_event[MIDI_DATA] + + # Can't directly check event instance as pygame.event.Event is + # a function. + # self.assertIsInstance(pg_event, pygame.event.Event) + self.assertEqual(pg_event.__class__.__name__, "Event") + self.assertEqual(pg_event.type, pygame.MIDIIN) + self.assertEqual(pg_event.status, midi_event_data[MD_STATUS]) + self.assertEqual(pg_event.data1, midi_event_data[MD_DATA1]) + self.assertEqual(pg_event.data2, midi_event_data[MD_DATA2]) + self.assertEqual(pg_event.data3, midi_event_data[MD_DATA3]) + self.assertEqual(pg_event.timestamp, midi_event[TIMESTAMP]) + self.assertEqual(pg_event.vice_id, device_id) + + def test_midis2events__missing_event_data(self): + """Ensures midi events with missing values are handled properly.""" + midi_event_missing_data = ((0xC0, 0, 1), 20000) + midi_event_missing_timestamp = ((0xC0, 0, 1, 2),) + + for midi_event in (midi_event_missing_data, midi_event_missing_timestamp): + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event], 0) + + def test_midis2events__extra_event_data(self): + """Ensures midi events with extra values are handled properly.""" + midi_event_extra_data = ((0xC0, 0, 1, 2, "extra"), 20000) + midi_event_extra_timestamp = ((0xC0, 0, 1, 2), 20000, "extra") + + for midi_event in (midi_event_extra_data, midi_event_extra_timestamp): + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event], 0) + + def test_midis2events__extra_event_data_missing_timestamp(self): + """Ensures midi events with extra data and no timestamps are handled + properly. + """ + midi_event_extra_data_no_timestamp = ((0xC0, 0, 1, 2, "extra"),) + + with self.assertRaises(ValueError): + events = pygame.midi.midis2events([midi_event_extra_data_no_timestamp], 0) + + def test_conversions(self): + """of frequencies to midi note numbers and ansi note names.""" + from pygame.midi import frequency_to_midi, midi_to_frequency, midi_to_ansi_note + self.assertEqual(frequency_to_midi(27.5), 21) self.assertEqual(frequency_to_midi(36.7), 26) self.assertEqual(frequency_to_midi(4186.0), 108) self.assertEqual(midi_to_frequency(21), 27.5) self.assertEqual(midi_to_frequency(26), 36.7) self.assertEqual(midi_to_frequency(108), 4186.0) - self.assertEqual(midi_to_ansi_note(21), 'A0') - self.assertEqual(midi_to_ansi_note(102), 'F#7') - self.assertEqual(midi_to_ansi_note(108), 'C8') - -if __name__ == '__main__': + self.assertEqual(midi_to_ansi_note(21), "A0") + self.assertEqual(midi_to_ansi_note(71), "B4") + self.assertEqual(midi_to_ansi_note(82), "A#5") + self.assertEqual(midi_to_ansi_note(83), "B5") + self.assertEqual(midi_to_ansi_note(93), "A6") + self.assertEqual(midi_to_ansi_note(94), "A#6") + self.assertEqual(midi_to_ansi_note(95), "B6") + self.assertEqual(midi_to_ansi_note(96), "C7") + self.assertEqual(midi_to_ansi_note(102), "F#7") + self.assertEqual(midi_to_ansi_note(108), "C8") + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/mixer_music_tags.py b/venv/Lib/site-packages/pygame/tests/mixer_music_tags.py index a131d09a460f426887ade7da11a8e52f26dffaeb..30f68937ce3547b5659cba00893391c4444a122c 100644 --- a/venv/Lib/site-packages/pygame/tests/mixer_music_tags.py +++ b/venv/Lib/site-packages/pygame/tests/mixer_music_tags.py @@ -2,6 +2,6 @@ __tags__ = [] import pygame import sys -if 'pygame.mixer_music' not in sys.modules: - __tags__.extend(('ignore', 'subprocess_ignore')) +if "pygame.mixer_music" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/venv/Lib/site-packages/pygame/tests/mixer_music_test.py b/venv/Lib/site-packages/pygame/tests/mixer_music_test.py index e7ee6081b30714d41abb41d5e3ea2bd199360843..b62ae1ec262740f94b5cbae80eef13c6c6ab5dea 100644 --- a/venv/Lib/site-packages/pygame/tests/mixer_music_test.py +++ b/venv/Lib/site-packages/pygame/tests/mixer_music_test.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- - import os import sys +import platform import unittest +import time from pygame.tests.test_utils import example_path import pygame -from pygame.compat import as_unicode, unicode_, filesystem_encode class MixerMusicModuleTest(unittest.TestCase): @@ -25,235 +24,416 @@ class MixerMusicModuleTest(unittest.TestCase): if pygame.mixer.get_init() is None: pygame.mixer.init() - def test_load(self): + def test_load_mp3(self): "|tags:music|" - # __doc__ (as of 2008-07-13) for pygame.mixer_music.load: + self.music_load("mp3") - # pygame.mixer.music.load(filename): return None - # Load a music file for playback + def test_load_ogg(self): + "|tags:music|" + self.music_load("ogg") - data_fname = example_path('data') - formats = ['mp3', 'ogg', 'wav'] + def test_load_wav(self): + "|tags:music|" + self.music_load("wav") - for f in formats: - path = os.path.join(data_fname, 'house_lo.%s' % f) - if os.sep == '\\': - path = path.replace('\\', '\\\\') - umusfn = as_unicode(path) - bmusfn = filesystem_encode(umusfn) + def music_load(self, format): + data_fname = example_path("data") - pygame.mixer.music.load(umusfn) - pygame.mixer.music.load(bmusfn) + path = os.path.join(data_fname, f"house_lo.{format}") + if os.sep == "\\": + path = path.replace("\\", "\\\\") + umusfn = str(path) + bmusfn = umusfn.encode() + + pygame.mixer.music.load(umusfn) + pygame.mixer.music.load(bmusfn) def test_load_object(self): """test loading music from file-like objects.""" - formats = ['ogg', 'wav'] - data_fname = example_path('data') + formats = ["ogg", "wav"] + data_fname = example_path("data") for f in formats: - path = os.path.join(data_fname, 'house_lo.%s' % f) - if os.sep == '\\': - path = path.replace('\\', '\\\\') - bmusfn = filesystem_encode(path) + path = os.path.join(data_fname, f"house_lo.{f}") + if os.sep == "\\": + path = path.replace("\\", "\\\\") + bmusfn = path.encode() - with open(bmusfn, 'rb') as musf: + with open(bmusfn, "rb") as musf: pygame.mixer.music.load(musf) + def test_object_namehint(self): + """test loading & queuing music from file-like objects with namehint argument.""" + formats = ["wav", "ogg"] + data_fname = example_path("data") + for f in formats: + path = os.path.join(data_fname, f"house_lo.{f}") + if os.sep == "\\": + path = path.replace("\\", "\\\\") + bmusfn = path.encode() + + # these two "with open" blocks need to be separate, which is kinda weird + with open(bmusfn, "rb") as musf: + pygame.mixer.music.load(musf, f) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.queue(musf, f) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.load(musf, namehint=f) + + with open(bmusfn, "rb") as musf: + pygame.mixer.music.queue(musf, namehint=f) + def test_load_unicode(self): """test non-ASCII unicode path""" import shutil - ep = unicode_(example_path('data')) - temp_file = os.path.join(ep, u'你好.wav') - org_file = os.path.join(ep, u'house_lo.wav') + + ep = example_path("data") + temp_file = os.path.join(ep, "你好.wav") + org_file = os.path.join(ep, "house_lo.wav") try: - with open(temp_file, 'w') as f: + with open(temp_file, "w") as f: pass os.remove(temp_file) - except IOError: - raise unittest.SkipTest('the path cannot be opened') + except OSError: + raise unittest.SkipTest("the path cannot be opened") shutil.copy(org_file, temp_file) try: pygame.mixer.music.load(temp_file) - pygame.mixer.music.load(org_file) # unload + pygame.mixer.music.load(org_file) # unload finally: os.remove(temp_file) - def todo_test_queue(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer_music.queue: - - # This will load a music file and queue it. A queued music file will - # begin as soon as the current music naturally ends. If the current - # music is ever stopped or changed, the queued song will be lost. - # - # The following example will play music by Bach six times, then play - # music by Mozart once: - # - # pygame.mixer.music.load('bach.ogg') - # pygame.mixer.music.play(5) # Plays six times, not five! - # pygame.mixer.music.queue('mozart.ogg') - - self.fail() - - def todo_test_stop(self): + def test_unload(self): + import shutil + import tempfile + ep = example_path("data") + org_file = os.path.join(ep, "house_lo.wav") + tmpfd, tmppath = tempfile.mkstemp(".wav") + os.close(tmpfd) + shutil.copy(org_file, tmppath) + try: + pygame.mixer.music.load(tmppath) + pygame.mixer.music.unload() + finally: + os.remove(tmppath) + + def test_queue_mp3(self): + """Ensures queue() accepts mp3 files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.queue(filename) + + def test_queue_ogg(self): + """Ensures queue() accepts ogg files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.ogg")) + pygame.mixer.music.queue(filename) + + def test_queue_wav(self): + """Ensures queue() accepts wav files. + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.wav")) + pygame.mixer.music.queue(filename) + + def test_queue__multiple_calls(self): + """Ensures queue() can be called multiple times.""" + ogg_file = example_path(os.path.join("data", "house_lo.ogg")) + wav_file = example_path(os.path.join("data", "house_lo.wav")) + + pygame.mixer.music.queue(ogg_file) + pygame.mixer.music.queue(wav_file) + + def test_queue__arguments(self): + """Ensures queue() can be called with proper arguments.""" + wav_file = example_path(os.path.join("data", "house_lo.wav")) + + pygame.mixer.music.queue(wav_file, loops=2) + pygame.mixer.music.queue(wav_file, namehint="") + pygame.mixer.music.queue(wav_file, "") + pygame.mixer.music.queue(wav_file, "", 2) + + def test_queue__no_file(self): + """Ensures queue() correctly handles missing the file argument.""" + with self.assertRaises(TypeError): + pygame.mixer.music.queue() + + def test_queue__invalid_sound_type(self): + """Ensures queue() correctly handles invalid file types.""" + not_a_sound_file = example_path(os.path.join("data", "city.png")) + + with self.assertRaises(pygame.error): + pygame.mixer.music.queue(not_a_sound_file) + + def test_queue__invalid_filename(self): + """Ensures queue() correctly handles invalid filenames.""" + with self.assertRaises(pygame.error): + pygame.mixer.music.queue("") + + def test_music_pause__unpause(self): + """Ensure music has the correct position immediately after unpausing + + |tags:music| + """ + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + # Wait 0.05s, then pause + time.sleep(0.05) + pygame.mixer.music.pause() + # Wait 0.05s, get position, unpause, then get position again + time.sleep(0.05) + before_unpause = pygame.mixer.music.get_pos() + pygame.mixer.music.unpause() + after_unpause = pygame.mixer.music.get_pos() + + self.assertEqual(before_unpause, after_unpause) + + def test_stop(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.stop: - # Stops the music playback if it is currently playing. + # Stops the music playback if it is currently playing. + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() - self.fail() + pygame.mixer.music.stop() + self.assertEqual(pygame.mixer.music.get_busy(), False) def todo_test_rewind(self): - # __doc__ (as of 2008-08-02) for pygame.mixer_music.rewind: - # Resets playback of the current music to the beginning. + # Resets playback of the current music to the beginning. self.fail() def todo_test_get_pos(self): - # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_pos: - # This gets the number of milliseconds that the music has been playing - # for. The returned time only represents how long the music has been - # playing; it does not take into account any starting position - # offsets. - # - - self.fail() - - def todo_test_fadeout(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer_music.fadeout: - - # This will stop the music playback after it has been faded out over - # the specified time (measured in milliseconds). - # - # Note, that this function blocks until the music has faded out. + # This gets the number of milliseconds that the music has been playing + # for. The returned time only represents how long the music has been + # playing; it does not take into account any starting position + # offsets. + # self.fail() - def todo_test_play(self): - + # def test_fadeout(self): + # filename = example_path(os.path.join("data", "house_lo.mp3")) + # pygame.mixer.music.load(filename) + # pygame.mixer.music.play() + + # pygame.mixer.music.fadeout(50) + # time.sleep(0.3) + # self.assertEqual(pygame.mixer.music.get_busy(), False) + + @unittest.skipIf( + os.environ.get("SDL_AUDIODRIVER") == "disk", + 'disk audio driver "playback" writing to disk is slow', + ) + def test_play__start_time(self): + pygame.display.init() + + # music file is 7 seconds long + filename = example_path(os.path.join("data", "house_lo.ogg")) + pygame.mixer.music.load(filename) + start_time_in_seconds = 6.0 # 6 seconds + + music_finished = False + clock = pygame.time.Clock() + start_time_in_ms = clock.tick() + # should play the last 1 second + pygame.mixer.music.play(0, start=start_time_in_seconds) + running = True + while running: + pygame.event.pump() + + if not (pygame.mixer.music.get_busy() or music_finished): + music_finished = True + time_to_finish = (clock.tick() - start_time_in_ms) // 1000 + self.assertEqual(time_to_finish, 1) + running = False + + def test_play(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.play: - # This will play the loaded music stream. If the music is already - # playing it will be restarted. - # - # The loops argument controls the number of repeats a music will play. - # play(5) will cause the music to played once, then repeated five - # times, for a total of six. If the loops is -1 then the music will - # repeat indefinitely. - # - # The starting position argument controls where in the music the song - # starts playing. The starting position is dependent on the format of - # music playing. MP3 and OGG use the position as time (in seconds). - # MOD music it is the pattern order number. Passing a startpos will - # raise a NotImplementedError if it cannot set the start position - # - - self.fail() + # This will play the loaded music stream. If the music is already + # playing it will be restarted. + # + # The loops argument controls the number of repeats a music will play. + # play(5) will cause the music to played once, then repeated five + # times, for a total of six. If the loops is -1 then the music will + # repeat indefinitely. + # + # The starting position argument controls where in the music the song + # starts playing. The starting position is dependent on the format of + # music playing. MP3 and OGG use the position as time (in seconds). + # MOD music it is the pattern order number. Passing a startpos will + # raise a NotImplementedError if it cannot set the start position + # + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + + self.assertTrue(pygame.mixer.music.get_busy()) + + pygame.mixer.music.stop() def todo_test_load(self): - # __doc__ (as of 2008-08-02) for pygame.mixer_music.load: - # This will load a music file and prepare it for playback. If a music - # stream is already playing it will be stopped. This does not start - # the music playing. - # - # Music can only be loaded from filenames, not python file objects - # like the other pygame loading functions. - # + # This will load a music file and prepare it for playback. If a music + # stream is already playing it will be stopped. This does not start + # the music playing. + # + # Music can only be loaded from filenames, not python file objects + # like the other pygame loading functions. + # self.fail() - def todo_test_get_volume(self): - + def test_get_volume(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_volume: - # Returns the current volume for the mixer. The value will be between - # 0.0 and 1.0. - # + # Returns the current volume for the mixer. The value will be between + # 0.0 and 1.0. + # + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() - self.fail() + vol = pygame.mixer.music.get_volume() + self.assertGreaterEqual(vol, 0) + self.assertLessEqual(vol, 1) - def todo_test_set_endevent(self): + pygame.mixer.music.stop() + def todo_test_set_endevent(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_endevent: - # This causes Pygame to signal (by means of the event queue) when the - # music is done playing. The argument determines the type of event - # that will be queued. - # - # The event will be queued every time the music finishes, not just the - # first time. To stop the event from being queued, call this method - # with no argument. - # + # This causes Pygame to signal (by means of the event queue) when the + # music is done playing. The argument determines the type of event + # that will be queued. + # + # The event will be queued every time the music finishes, not just the + # first time. To stop the event from being queued, call this method + # with no argument. + # self.fail() - def todo_test_pause(self): - + def test_pause(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.pause: - # Temporarily stop playback of the music stream. It can be resumed - # with the pygame.mixer.music.unpause() function. - # - - self.fail() - - def todo_test_get_busy(self): - + # Temporarily stop playback of the music stream. It can be resumed + # with the pygame.mixer.music.unpause() function. + # + self.music_load("ogg") + self.assertFalse(pygame.mixer.music.get_busy()) + pygame.mixer.music.play() + self.assertTrue(pygame.mixer.music.get_busy()) + pygame.mixer.music.pause() + self.assertFalse(pygame.mixer.music.get_busy()) + + def test_get_busy(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_busy: - # Returns True when the music stream is actively playing. When the - # music is idle this returns False. - # + # Returns True when the music stream is actively playing. When the + # music is idle this returns False. + # - self.fail() + self.music_load("ogg") + self.assertFalse(pygame.mixer.music.get_busy()) + pygame.mixer.music.play() + self.assertTrue(pygame.mixer.music.get_busy()) + pygame.mixer.music.pause() + self.assertFalse(pygame.mixer.music.get_busy()) def todo_test_get_endevent(self): - # __doc__ (as of 2008-08-02) for pygame.mixer_music.get_endevent: - # Returns the event type to be sent every time the music finishes - # playback. If there is no endevent the function returns - # pygame.NOEVENT. - # + # Returns the event type to be sent every time the music finishes + # playback. If there is no endevent the function returns + # pygame.NOEVENT. + # self.fail() - def todo_test_unpause(self): - + def test_unpause(self): # __doc__ (as of 2008-08-02) for pygame.mixer_music.unpause: - # This will resume the playback of a music stream after it has been paused. + # This will resume the playback of a music stream after it has been paused. + + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() + self.assertTrue(pygame.mixer.music.get_busy()) + time.sleep(0.1) + pygame.mixer.music.pause() + self.assertFalse(pygame.mixer.music.get_busy()) + before = pygame.mixer.music.get_pos() + pygame.mixer.music.unpause() + after = pygame.mixer.music.get_pos() + self.assertTrue(pygame.mixer.music.get_busy()) + # It could rarely be that it is +/- 1 different + # But mostly, after should equal before. + self.assertTrue(before - 1 <= after <= before + 1) + + pygame.mixer.music.stop() + + def test_set_volume(self): + # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_volume: - self.fail() + # Set the volume of the music playback. The value argument is between + # 0.0 and 1.0. When new music is loaded the volume is reset. + # + filename = example_path(os.path.join("data", "house_lo.mp3")) + pygame.mixer.music.load(filename) + pygame.mixer.music.play() - def todo_test_set_volume(self): + pygame.mixer.music.set_volume(0.5) + vol = pygame.mixer.music.get_volume() + self.assertEqual(vol, 0.5) - # __doc__ (as of 2008-08-02) for pygame.mixer_music.set_volume: + pygame.mixer.music.stop() - # Set the volume of the music playback. The value argument is between - # 0.0 and 1.0. When new music is loaded the volume is reset. - # + def todo_test_set_pos(self): + # __doc__ (as of 2010-24-05) for pygame.mixer_music.set_pos: + + # This sets the position in the music file where playback will start. The + # meaning of "pos", a float (or a number that can be converted to a float), + # depends on the music format. Newer versions of SDL_mixer have better + # positioning support than earlier. An SDLError is raised if a particular + # format does not support positioning. + # self.fail() - def todo_test_set_pos(self): + def test_init(self): + """issue #955. unload music whenever mixer.quit() is called""" + import tempfile + import shutil - # __doc__ (as of 2010-24-05) for pygame.mixer_music.set_pos: + testfile = example_path(os.path.join("data", "house_lo.wav")) + tempcopy = os.path.join(tempfile.gettempdir(), "tempfile.wav") - #This sets the position in the music file where playback will start. The - # meaning of "pos", a float (or a number that can be converted to a float), - # depends on the music format. Newer versions of SDL_mixer have better - # positioning support than earlier. An SDLError is raised if a particular - # format does not support positioning. - # + for i in range(10): + pygame.mixer.init() + try: + shutil.copy2(testfile, tempcopy) + pygame.mixer.music.load(tempcopy) + pygame.mixer.quit() + finally: + os.remove(tempcopy) - self.fail() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/mixer_tags.py b/venv/Lib/site-packages/pygame/tests/mixer_tags.py index 7cba721f0a8b576616cc4d0808544973f16a6d93..06a9de2a73e1cf955c07a912e5a943eaf4eb3298 100644 --- a/venv/Lib/site-packages/pygame/tests/mixer_tags.py +++ b/venv/Lib/site-packages/pygame/tests/mixer_tags.py @@ -2,6 +2,6 @@ __tags__ = [] import pygame import sys -if 'pygame.mixer' not in sys.modules: - __tags__.extend(('ignore', 'subprocess_ignore')) +if "pygame.mixer" not in sys.modules: + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/venv/Lib/site-packages/pygame/tests/mixer_test.py b/venv/Lib/site-packages/pygame/tests/mixer_test.py index e00926c5fb3a1c4f36c925ef89365e009512fa2b..a85e01ac76e8581e923b38504e119f5f15db572c 100644 --- a/venv/Lib/site-packages/pygame/tests/mixer_test.py +++ b/venv/Lib/site-packages/pygame/tests/mixer_test.py @@ -1,46 +1,49 @@ -# -*- coding: utf8 -*- - import sys import os import unittest +import pathlib import platform +import time -from pygame.tests.test_utils import example_path, AssertRaisesRegexMixin +from pygame.tests.test_utils import example_path import pygame from pygame import mixer -from pygame.compat import unicode_, as_bytes, bytes_ - -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() ################################### CONSTANTS ################################## FREQUENCIES = [11025, 22050, 44100, 48000] -SIZES = [-16, -8, 8, 16] -if pygame.get_sdl_version()[0] >= 2: - SIZES.append(32) - -CHANNELS = [1, 2] -BUFFERS = [3024] - -CONFIGS = [{'frequency' : f, 'size' : s, 'channels': c} - for f in FREQUENCIES - for s in SIZES - for c in CHANNELS] +SIZES = [-16, -8, 8, 16] # fixme +# size 32 failed in test_get_init__returns_exact_values_used_for_init +CHANNELS = [1, 2] +BUFFERS = [3024] + +CONFIGS = [ + {"frequency": f, "size": s, "channels": c} + for f in FREQUENCIES + for s in SIZES + for c in CHANNELS +] # Using all CONFIGS fails on a Mac; probably older SDL_mixer; we could do: # if platform.system() == 'Darwin': # But using all CONFIGS is very slow (> 10 sec for example) # And probably, we don't need to be so exhaustive, hence: -CONFIG = {'frequency' : 22050, 'size' : -16, 'channels' : 2} # base config -if pygame.get_sdl_version()[0] >= 2: - CONFIG = {'frequency' : 44100, 'size' : 32, 'channels' : 2} # base config +CONFIG = {"frequency": 44100, "size": 32, "channels": 2, "allowedchanges": 0} -############################## MODULE LEVEL TESTS ############################## -class MixerModuleTest(unittest.TestCase): +class InvalidBool: + """To help test invalid bool values.""" + + __bool__ = None + +############################## MODULE LEVEL TESTS ############################# + + +class MixerModuleTest(unittest.TestCase): def tearDown(self): mixer.quit() mixer.pre_init(0, 0, 0, 0) @@ -50,10 +53,10 @@ class MixerModuleTest(unittest.TestCase): mixer.init(**CONFIG) mixer_conf = mixer.get_init() - self.assertEqual(mixer_conf[0], CONFIG['frequency']) + self.assertEqual(mixer_conf[0], CONFIG["frequency"]) # Not all "sizes" are supported on all systems, hence "abs". - self.assertEqual(abs(mixer_conf[1]), abs(CONFIG['size'])) - self.assertEqual(mixer_conf[2], CONFIG['channels']) + self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) + self.assertGreaterEqual(mixer_conf[2], CONFIG["channels"]) def test_pre_init__keyword_args(self): # note: this test used to loop over all CONFIGS, but it's very slow.. @@ -62,18 +65,20 @@ class MixerModuleTest(unittest.TestCase): mixer_conf = mixer.get_init() - self.assertEqual(mixer_conf[0], CONFIG['frequency']) + self.assertEqual(mixer_conf[0], CONFIG["frequency"]) # Not all "sizes" are supported on all systems, hence "abs". - self.assertEqual(abs(mixer_conf[1]), abs(CONFIG['size'])) - self.assertEqual(mixer_conf[2], CONFIG['channels']) + self.assertEqual(abs(mixer_conf[1]), abs(CONFIG["size"])) + self.assertGreaterEqual(mixer_conf[2], CONFIG["channels"]) def test_pre_init__zero_values(self): # Ensure that argument values of 0 are replaced with # default values. No way to check buffer size though. - mixer.pre_init(44100, -8, 1) # Non default values - mixer.pre_init(0, 0, 0) # Should reset to default values - mixer.init() - self.assertEqual(mixer.get_init(), (22050, -16, 2)) + mixer.pre_init(22050, -8, 1) # Non default values + mixer.pre_init(0, 0, 0) # Should reset to default values + mixer.init(allowedchanges=0) + self.assertEqual(mixer.get_init()[0], 44100) + self.assertEqual(mixer.get_init()[1], -16) + self.assertGreaterEqual(mixer.get_init()[2], 2) def test_init__zero_values(self): # Ensure that argument values of 0 are replaced with @@ -82,24 +87,24 @@ class MixerModuleTest(unittest.TestCase): mixer.init(0, 0, 0) self.assertEqual(mixer.get_init(), (44100, 8, 1)) - @unittest.skip('SDL_mixer bug') - def test_get_init__returns_exact_values_used_for_init(self): - # fix in 1.9 - I think it's a SDL_mixer bug. - - # TODO: When this bug is fixed, testing through every combination - # will be too slow so adjust as necessary, at the moment it - # breaks the loop after first failure + # def test_get_init__returns_exact_values_used_for_init(self): + # # TODO: size 32 fails in this test (maybe SDL_mixer bug) - for init_conf in CONFIGS: - frequency, size, channels - if (frequency, size) == (22050, 16): - continue - mixer.init(frequency, size, channels) + # # TODO: 2) When you start the mixer, you request the settings. + # # But it can be that a sound system doesn’t support what you request… + # # and it gives you back something close to what you request but not equal. + # # So, you can’t test for equality. + # # See allowedchanges - mixer_conf = mixer.get_init() + # for init_conf in CONFIGS: + # frequency, size, channels = init_conf.values() + # if (frequency, size) == (22050, 16): + # continue + # mixer.init(frequency, size, channels) - self.assertEqual(init_conf, mixer_conf) - mixer.quit() + # mixer_conf = mixer.get_init() + # self.assertEqual(tuple(init_conf.values()), mixer_conf) + # mixer.quit() def test_get_init__returns_None_if_mixer_not_initialized(self): self.assertIsNone(mixer.get_init()) @@ -117,29 +122,35 @@ class MixerModuleTest(unittest.TestCase): self.assertEqual(mixer.get_num_channels(), i) def test_quit(self): - """ get_num_channels() Should throw pygame.error if uninitialized - after mixer.quit() """ + """get_num_channels() Should throw pygame.error if uninitialized + after mixer.quit()""" mixer.init() mixer.quit() self.assertRaises(pygame.error, mixer.get_num_channels) + # TODO: FIXME: appveyor and pypy (on linux) fails here sometimes. + @unittest.skipIf(sys.platform.startswith("win"), "See github issue 892.") + @unittest.skipIf(IS_PYPY, "random errors here with pypy") def test_sound_args(self): def get_bytes(snd): return snd.get_raw() + mixer.init() - sample = as_bytes('\x00\xff') * 24 - wave_path = example_path(os.path.join('data', 'house_lo.wav')) - uwave_path = unicode_(wave_path) + sample = b"\x00\xff" * 24 + wave_path = example_path(os.path.join("data", "house_lo.wav")) + uwave_path = str(wave_path) bwave_path = uwave_path.encode(sys.getfilesystemencoding()) snd = mixer.Sound(file=wave_path) self.assertTrue(snd.get_length() > 0.5) snd_bytes = get_bytes(snd) self.assertTrue(len(snd_bytes) > 1000) + self.assertEqual(get_bytes(mixer.Sound(wave_path)), snd_bytes) + self.assertEqual(get_bytes(mixer.Sound(file=uwave_path)), snd_bytes) self.assertEqual(get_bytes(mixer.Sound(uwave_path)), snd_bytes) - arg_emsg = 'Sound takes either 1 positional or 1 keyword argument' + arg_emsg = "Sound takes either 1 positional or 1 keyword argument" with self.assertRaises(TypeError) as cm: mixer.Sound() @@ -156,30 +167,28 @@ class MixerModuleTest(unittest.TestCase): with self.assertRaises(TypeError) as cm: mixer.Sound(foobar=sample) - self.assertEqual(str(cm.exception), - "Unrecognized keyword argument 'foobar'") + self.assertEqual(str(cm.exception), "Unrecognized keyword argument 'foobar'") snd = mixer.Sound(wave_path, **{}) self.assertEqual(get_bytes(snd), snd_bytes) - snd = mixer.Sound(*[], **{'file': wave_path}) + snd = mixer.Sound(*[], **{"file": wave_path}) with self.assertRaises(TypeError) as cm: mixer.Sound([]) - self.assertEqual(str(cm.exception), - 'Unrecognized argument (type list)') + self.assertEqual(str(cm.exception), "Unrecognized argument (type list)") with self.assertRaises(TypeError) as cm: snd = mixer.Sound(buffer=[]) - emsg = 'Expected object with buffer interface: got a list' + emsg = "Expected object with buffer interface: got a list" self.assertEqual(str(cm.exception), emsg) - ufake_path = unicode_('12345678') + ufake_path = "12345678" self.assertRaises(IOError, mixer.Sound, ufake_path) - self.assertRaises(IOError, mixer.Sound, '12345678') + self.assertRaises(IOError, mixer.Sound, "12345678") with self.assertRaises(TypeError) as cm: - mixer.Sound(buffer=unicode_('something')) - emsg = 'Unicode object not allowed as buffer object' + mixer.Sound(buffer="something") + emsg = "Unicode object not allowed as buffer object" self.assertEqual(str(cm.exception), emsg) self.assertEqual(get_bytes(mixer.Sound(buffer=sample)), sample) if type(sample) != str: @@ -203,15 +212,16 @@ class MixerModuleTest(unittest.TestCase): """test non-ASCII unicode path""" mixer.init() import shutil - ep = unicode_(example_path('data')) - temp_file = os.path.join(ep, u'你好.wav') - org_file = os.path.join(ep, u'house_lo.wav') + + ep = example_path("data") + temp_file = os.path.join(ep, "你好.wav") + org_file = os.path.join(ep, "house_lo.wav") shutil.copy(org_file, temp_file) try: - with open(temp_file, 'rb') as f: + with open(temp_file, "rb") as f: pass - except IOError: - raise unittest.SkipTest('the path cannot be opened') + except OSError: + raise unittest.SkipTest("the path cannot be opened") try: sound = mixer.Sound(temp_file) @@ -219,22 +229,31 @@ class MixerModuleTest(unittest.TestCase): finally: os.remove(temp_file) - @unittest.skipIf(os.environ.get('SDL_AUDIODRIVER') == 'disk', - 'this test fails without real sound card') + @unittest.skipIf( + os.environ.get("SDL_AUDIODRIVER") == "disk", + "this test fails without real sound card", + ) def test_array_keyword(self): try: - from numpy import (array, arange, zeros, - int8, uint8, - int16, uint16, - int32, uint32) + from numpy import ( + array, + arange, + zeros, + int8, + uint8, + int16, + uint16, + int32, + uint32, + ) except ImportError: - self.skipTest('requires numpy') + self.skipTest("requires numpy") freq = 22050 format_list = [-8, 8, -16, 16] channels_list = [1, 2] - a_lists = dict((f, []) for f in format_list) + a_lists = {f: [] for f in format_list} a32u_mono = arange(0, 256, 1, uint32) a16u_mono = a32u_mono.astype(uint16) a8u_mono = a32u_mono.astype(uint8) @@ -250,22 +269,20 @@ class MixerModuleTest(unittest.TestCase): if format < 0: a_lists[format].extend(as_list_mono) a32u_stereo = zeros([a32u_mono.shape[0], 2], uint32) - a32u_stereo[:,0] = a32u_mono - a32u_stereo[:,1] = 255 - a32u_mono + a32u_stereo[:, 0] = a32u_mono + a32u_stereo[:, 1] = 255 - a32u_mono a16u_stereo = a32u_stereo.astype(uint16) a8u_stereo = a32u_stereo.astype(uint8) - au_list_stereo = [(2, a) - for a in [a8u_stereo, a16u_stereo, a32u_stereo]] + au_list_stereo = [(2, a) for a in [a8u_stereo, a16u_stereo, a32u_stereo]] for format in format_list: if format > 0: a_lists[format].extend(au_list_stereo) a32s_stereo = zeros([a32s_mono.shape[0], 2], int32) - a32s_stereo[:,0] = a32s_mono - a32s_stereo[:,1] = -1 - a32s_mono + a32s_stereo[:, 0] = a32s_mono + a32s_stereo[:, 1] = -1 - a32s_mono a16s_stereo = a32s_stereo.astype(int16) a8s_stereo = a32s_stereo.astype(int8) - as_list_stereo = [(2, a) - for a in [a8s_stereo, a16s_stereo, a32s_stereo]] + as_list_stereo = [(2, a) for a in [a8s_stereo, a16s_stereo, a32s_stereo]] for format in format_list: if format < 0: a_lists[format].extend(as_list_stereo) @@ -295,43 +312,44 @@ class MixerModuleTest(unittest.TestCase): except ValueError: if not test_pass: return - self.fail("Raised ValueError: Format %i, dtype %s" % - (format, a.dtype)) + self.fail("Raised ValueError: Format %i, dtype %s" % (format, a.dtype)) if not test_pass: - self.fail("Did not raise ValueError: Format %i, dtype %s" % - (format, a.dtype)) + self.fail( + "Did not raise ValueError: Format %i, dtype %s" % (format, a.dtype) + ) a2 = array(snd) a3 = a.astype(a2.dtype) lshift = abs(format) - 8 * a.itemsize if lshift >= 0: # This is asymmetric with respect to downcasting. a3 <<= lshift - self.assertTrue(all_(a2 == a3), - "Format %i, dtype %s" % (format, a.dtype)) + self.assertTrue(all_(a2 == a3), "Format %i, dtype %s" % (format, a.dtype)) def _test_array_interface_fail(self, a): self.assertRaises(ValueError, mixer.Sound, array=a) def test_array_interface(self): mixer.init(22050, -16, 1, allowedchanges=0) - snd = mixer.Sound(buffer=as_bytes('\x00\x7f') * 20) + snd = mixer.Sound(buffer=b"\x00\x7f" * 20) d = snd.__array_interface__ self.assertTrue(isinstance(d, dict)) if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: - typestr = '') if is_lil_endian else ('>', '<') + fsys, frev = ("<", ">") if is_lil_endian else (">", "<") shape = (10, channels)[:ndim] - strides = (channels * itemsize, itemsize)[2 - ndim:] - exp = Exporter(shape, format=frev + 'i') + strides = (channels * itemsize, itemsize)[2 - ndim :] + exp = Exporter(shape, format=frev + "i") snd = mixer.Sound(array=exp) buflen = len(exp) * itemsize * channels imp = Importer(snd, buftools.PyBUF_SIMPLE) @@ -439,111 +464,185 @@ class MixerModuleTest(unittest.TestCase): self.assertTrue(imp.format is None) self.assertEqual(imp.strides, strides) else: - self.assertRaises(BufferError, Importer, snd, - buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, snd, buftools.PyBUF_F_CONTIGUOUS) - def todo_test_fadeout(self): + def test_fadeout(self): + """Ensure pygame.mixer.fadeout() stops playback after fading out the sound.""" + if mixer.get_init() is None: + mixer.init() + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + channel = pygame.mixer.find_channel() + channel.play(sound) + fadeout_time = 200 # milliseconds + channel.fadeout(fadeout_time) + pygame.time.wait(fadeout_time + 30) - # __doc__ (as of 2008-08-02) for pygame.mixer.fadeout: + # Ensure the channel is no longer busy + self.assertFalse(channel.get_busy()) - # pygame.mixer.fadeout(time): return None - # fade out the volume on all sounds before stopping - # - # This will fade out the volume on all active channels over the time - # argument in milliseconds. After the sound is muted the playback will - # stop. - # + def test_find_channel(self): + # __doc__ (as of 2008-08-02) for pygame.mixer.find_channel: - self.fail() + # pygame.mixer.find_channel(force=False): return Channel + # find an unused channel + mixer.init() - def todo_test_find_channel(self): + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) - # __doc__ (as of 2008-08-02) for pygame.mixer.find_channel: + num_channels = mixer.get_num_channels() - # pygame.mixer.find_channel(force=False): return Channel - # find an unused channel - # - # This will find and return an inactive Channel object. If there are - # no inactive Channels this function will return None. If there are no - # inactive channels and the force argument is True, this will find the - # Channel with the longest running Sound and return it. - # - # If the mixer has reserved channels from pygame.mixer.set_reserved() - # then those channels will not be returned here. - # + if num_channels > 0: + found_channel = mixer.find_channel() + self.assertIsNotNone(found_channel) - self.fail() + # try playing on all channels + channels = [] + for channel_id in range(0, num_channels): + channel = mixer.Channel(channel_id) + channel.play(sound) + channels.append(channel) - def todo_test_get_busy(self): + # should fail without being forceful + found_channel = mixer.find_channel() + self.assertIsNone(found_channel) - # __doc__ (as of 2008-08-02) for pygame.mixer.get_busy: + # try forcing without keyword + found_channel = mixer.find_channel(True) + self.assertIsNotNone(found_channel) - # pygame.mixer.get_busy(): return bool - # test if any sound is being mixed - # - # Returns True if the mixer is busy mixing any channels. If the mixer - # is idle then this return False. - # + # try forcing with keyword + found_channel = mixer.find_channel(force=True) + self.assertIsNotNone(found_channel) - self.fail() + for channel in channels: + channel.stop() + found_channel = mixer.find_channel() + self.assertIsNotNone(found_channel) - def todo_test_pause(self): + @unittest.expectedFailure + def test_pause(self): + """Ensure pygame.mixer.pause() temporarily stops playback of all sound channels.""" + if mixer.get_init() is None: + mixer.init() + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel = mixer.find_channel() + channel.play(sound) - # __doc__ (as of 2008-08-02) for pygame.mixer.pause: + mixer.pause() - # pygame.mixer.pause(): return None - # temporarily stop playback of all sound channels - # - # This will temporarily stop all playback on the active mixer - # channels. The playback can later be resumed with - # pygame.mixer.unpause() - # + # TODO: this currently fails? + # Ensure the channel is paused + self.assertFalse(channel.get_busy()) - self.fail() + mixer.unpause() - def todo_test_set_reserved(self): + # Ensure the channel is no longer paused + self.assertTrue(channel.get_busy()) - # __doc__ (as of 2008-08-02) for pygame.mixer.set_reserved: + def test_set_reserved(self): + """Ensure pygame.mixer.set_reserved() reserves the given number of channels.""" - # pygame.mixer.set_reserved(count): return None - # reserve channels from being automatically used - # - # The mixer can reserve any number of channels that will not be - # automatically selected for playback by Sounds. If sounds are - # currently playing on the reserved channels they will not be stopped. - # - # This allows the application to reserve a specific number of channels - # for important sounds that must not be dropped or have a guaranteed - # channel to play on. - # + # pygame.mixer.set_reserved(count): return count + mixer.init() + default_num_channels = mixer.get_num_channels() - self.fail() + # try reserving all the channels + result = mixer.set_reserved(default_num_channels) + self.assertEqual(result, default_num_channels) - def todo_test_stop(self): + # try reserving all the channels + 1 + result = mixer.set_reserved(default_num_channels + 1) + # should still be default + self.assertEqual(result, default_num_channels) - # __doc__ (as of 2008-08-02) for pygame.mixer.stop: + # try unreserving all + result = mixer.set_reserved(0) + # should still be default + self.assertEqual(result, 0) - # pygame.mixer.stop(): return None - # stop playback of all sound channels - # - # This will stop all playback of all active mixer channels. + # try reserving half + result = mixer.set_reserved(int(default_num_channels / 2)) + # should still be default + self.assertEqual(result, int(default_num_channels / 2)) - self.fail() + def test_stop(self): + """Stops playback of all active sound channels.""" + if mixer.get_init() is None: + mixer.init() + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + channel = pygame.mixer.Channel(0) + channel.play(sound) + pygame.mixer.stop() + for i in range(pygame.mixer.get_num_channels()): + self.assertFalse(pygame.mixer.Channel(i).get_busy()) - def todo_test_unpause(self): + def test_get_sdl_mixer_version(self): + """Ensures get_sdl_mixer_version works correctly with no args.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int - # __doc__ (as of 2008-08-02) for pygame.mixer.unpause: + version = pygame.mixer.get_sdl_mixer_version() - # pygame.mixer.unpause(): return None - # resume paused playback of sound channels - # - # This will resume all active sound channels after they have been paused. + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__args(self): + """Ensures get_sdl_mixer_version works correctly using args.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + for value in (True, False): + version = pygame.mixer.get_sdl_mixer_version(value) + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__kwargs(self): + """Ensures get_sdl_mixer_version works correctly using kwargs.""" + expected_length = 3 + expected_type = tuple + expected_item_type = int + + for value in (True, False): + version = pygame.mixer.get_sdl_mixer_version(linked=value) + + self.assertIsInstance(version, expected_type) + self.assertEqual(len(version), expected_length) + + for item in version: + self.assertIsInstance(item, expected_item_type) + + def test_get_sdl_mixer_version__invalid_args_kwargs(self): + """Ensures get_sdl_mixer_version handles invalid args and kwargs.""" + invalid_bool = InvalidBool() + + with self.assertRaises(TypeError): + version = pygame.mixer.get_sdl_mixer_version(invalid_bool) + + with self.assertRaises(TypeError): + version = pygame.mixer.get_sdl_mixer_version(linked=invalid_bool) + + def test_get_sdl_mixer_version__linked_equals_compiled(self): + """Ensures get_sdl_mixer_version's linked/compiled versions are equal.""" + linked_version = pygame.mixer.get_sdl_mixer_version(linked=True) + complied_version = pygame.mixer.get_sdl_mixer_version(linked=False) + + self.assertTupleEqual(linked_version, complied_version) - self.fail() ############################## CHANNEL CLASS TESTS ############################# -class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): + +class ChannelTypeTest(unittest.TestCase): @classmethod def setUpClass(cls): # Initializing the mixer is slow, so minimize the times it is called. @@ -564,7 +663,7 @@ class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): channel = mixer.Channel(0) self.assertIsInstance(channel, mixer.ChannelType) - self.assertEqual(channel.__class__.__name__, 'Channel') + self.assertEqual(channel.__class__.__name__, "Channel") def test_channel__without_arg(self): """Ensure exception for Channel() creation with no argument.""" @@ -580,21 +679,22 @@ class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): """Ensure exception for Channel() creation with non-init mixer.""" mixer.quit() - with self.assertRaisesRegex(pygame.error, 'mixer not initialized'): + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): mixer.Channel(0) - def todo_test_fadeout(self): + def test_fadeout(self): + """Ensure Channel.fadeout() stops playback after fading out.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.fadeout: + fadeout_time = 1000 + channel.fadeout(fadeout_time) - # Channel.fadeout(time): return None - # stop playback after fading channel out - # - # Stop playback of a channel after fading out the sound over the given - # time argument in milliseconds. - # + # Wait for the fadeout to complete. + pygame.time.wait(fadeout_time + 100) - self.fail() + self.assertFalse(channel.get_busy()) def test_get_busy(self): """Ensure an idle channel's busy state is correct.""" @@ -605,50 +705,70 @@ class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(busy, expected_busy) - def todo_test_get_busy__active(self): + def test_get_busy__active(self): """Ensure an active channel's busy state is correct.""" - self.fail() + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) - def todo_test_get_endevent(self): + self.assertTrue(channel.get_busy()) + def todo_test_get_endevent(self): # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_endevent: - # Channel.get_endevent(): return type - # get the event a channel sends when playback stops - # - # Returns the event type to be sent every time the Channel finishes - # playback of a Sound. If there is no endevent the function returns - # pygame.NOEVENT. - # + # Channel.get_endevent(): return type + # get the event a channel sends when playback stops + # + # Returns the event type to be sent every time the Channel finishes + # playback of a Sound. If there is no endevent the function returns + # pygame.NOEVENT. + # self.fail() - def todo_test_get_queue(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_queue: - - # Channel.get_queue(): return Sound - # return any Sound that is queued - # - # If a Sound is already queued on this channel it will be returned. - # Once the queued sound begins playback it will no longer be on the - # queue. - # - - self.fail() - - def todo_test_get_sound(self): + def test_get_queue(self): + """Ensure Channel.get_queue() returns any queued Sound.""" + channel = mixer.Channel(0) + frequency, format, channels = mixer.get_init() + sound_length_in_ms = 200 + sound_length_in_ms_2 = 400 + bytes_per_ms = int((frequency / 1000) * channels * (abs(format) // 8)) + sound1 = mixer.Sound(b"\x00" * int(sound_length_in_ms * bytes_per_ms)) + sound2 = mixer.Sound(b"\x00" * (int(sound_length_in_ms_2 * bytes_per_ms))) + + channel.play(sound1) + channel.queue(sound2) + + # Ensure the second queued sound is returned. + self.assertEqual(channel.get_queue().get_length(), sound2.get_length()) + # TODO: should sound1.stop() clear it from the queue too? Currently it doesn't. + pygame.time.wait(sound_length_in_ms + 100) + + # TODO: I think here there should be nothing queued. + # Because the sound should be off the queue. Currently it doesn't do this. + # self.assertIsNone(channel.get_queue()) + + # the second sound is now playing + self.assertEqual(channel.get_sound().get_length(), sound2.get_length()) + pygame.time.wait((sound_length_in_ms_2) + 100) + + # Now there is nothing on the queue. + self.assertIsNone(channel.get_queue()) + + def test_get_sound(self): + """Ensure Channel.get_sound() returns the currently playing Sound.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.get_sound: + # Ensure the correct Sound object is returned. + got_sound = channel.get_sound() + self.assertEqual(got_sound, sound) - # Channel.get_sound(): return Sound - # get the currently playing Sound - # - # Return the actual Sound object currently playing on this channel. If - # the channel is idle None is returned. - # - - self.fail() + # Stop the sound and ensure None is returned. + channel.stop() + got_sound = channel.get_sound() + self.assertIsNone(got_sound) def test_get_volume(self): """Ensure a channel's volume can be retrieved.""" @@ -659,153 +779,234 @@ class ChannelTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertAlmostEqual(volume, expected_volume) - def todo_test_get_volume__while_playing(self): - """Ensure a channel's volume can be retrieved while playing.""" - self.fail() - - def todo_test_pause(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.pause: - - # Channel.pause(): return None - # temporarily stop playback of a channel - # - # Temporarily stop the playback of sound on a channel. It can be - # resumed at a later time with Channel.unpause() - # + def test_pause_unpause(self): + """ + Test if the Channel can be paused and unpaused. + """ + if mixer.get_init() is None: + mixer.init() + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + channel = sound.play() + channel.pause() + self.assertTrue( + channel.get_busy(), msg="Channel should be paused but it's not." + ) + channel.unpause() + self.assertTrue( + channel.get_busy(), msg="Channel should be unpaused but it's not." + ) + sound.stop() - self.fail() + def test_pause_unpause__before_init(self): + """ + Ensure exception for Channel.pause() with non-init mixer. + """ + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel = sound.play() + mixer.quit() - def todo_test_play(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.play: - - # Channel.play(Sound, loops=0, maxtime=0, fade_ms=0): return None - # play a Sound on a specific Channel - # - # This will begin playback of a Sound on a specific Channel. If the - # Channel is currently playing any other Sound it will be stopped. - # - # The loops argument has the same meaning as in Sound.play(): it is - # the number of times to repeat the sound after the first time. If it - # is 3, the sound will be played 4 times (the first time, then three - # more). If loops is -1 then the playback will repeat indefinitely. - # - # As in Sound.play(), the maxtime argument can be used to stop - # playback of the Sound after a given number of milliseconds. - # - # As in Sound.play(), the fade_ms argument can be used fade in the sound. + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + channel.pause() - self.fail() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + channel.unpause() def todo_test_queue(self): - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.queue: - # Channel.queue(Sound): return None - # queue a Sound object to follow the current - # - # When a Sound is queued on a Channel, it will begin playing - # immediately after the current Sound is finished. Each channel can - # only have a single Sound queued at a time. The queued Sound will - # only play if the current playback finished automatically. It is - # cleared on any other call to Channel.stop() or Channel.play(). - # - # If there is no sound actively playing on the Channel then the Sound - # will begin playing immediately. - # + # Channel.queue(Sound): return None + # queue a Sound object to follow the current + # + # When a Sound is queued on a Channel, it will begin playing + # immediately after the current Sound is finished. Each channel can + # only have a single Sound queued at a time. The queued Sound will + # only play if the current playback finished automatically. It is + # cleared on any other call to Channel.stop() or Channel.play(). + # + # If there is no sound actively playing on the Channel then the Sound + # will begin playing immediately. + # self.fail() - def todo_test_set_endevent(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.set_endevent: - - # Channel.set_endevent(): return None - # Channel.set_endevent(type): return None - # have the channel send an event when playback stops - # - # When an endevent is set for a channel, it will send an event to the - # pygame queue every time a sound finishes playing on that channel - # (not just the first time). Use pygame.event.get() to retrieve the - # endevent once it's sent. - # - # Note that if you called Sound.play(n) or Channel.play(sound,n), the - # end event is sent only once: after the sound has been played "n+1" - # times (see the documentation of Sound.play). - # - # If Channel.stop() or Channel.play() is called while the sound was - # still playing, the event will be posted immediately. - # - # The type argument will be the event id sent to the queue. This can - # be any valid event type, but a good choice would be a value between - # pygame.locals.USEREVENT and pygame.locals.NUMEVENTS. If no type - # argument is given then the Channel will stop sending endevents. - # + def test_stop(self): + # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.stop: - self.fail() + # Channel.stop(): return None + # stop playback on a Channel + # + # Stop sound playback on a channel. After playback is stopped the + # channel becomes available for new Sounds to play on it. + # - def todo_test_set_volume(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.set_volume: - - # Channel.set_volume(value): return None - # Channel.set_volume(left, right): return None - # set the volume of a playing channel - # - # Set the volume (loudness) of a playing sound. When a channel starts - # to play its volume value is reset. This only affects the current - # sound. The value argument is between 0.0 and 1.0. - # - # If one argument is passed, it will be the volume of both speakers. - # If two arguments are passed and the mixer is in stereo mode, the - # first argument will be the volume of the left speaker and the second - # will be the volume of the right speaker. (If the second argument is - # None, the first argument will be the volume of both speakers.) - # - # If the channel is playing a Sound on which set_volume() has also - # been called, both calls are taken into account. For example: - # - # sound = pygame.mixer.Sound("s.wav") - # channel = s.play() # Sound plays at full volume by default - # sound.set_volume(0.9) # Now plays at 90% of full volume. - # sound.set_volume(0.6) # Now plays at 60% (previous value replaced). - # channel.set_volume(0.5) # Now plays at 30% (0.6 * 0.5). + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + + # simple check + channel.play(sound) + channel.stop() + self.assertFalse(channel.get_busy()) + # check that queued sounds also stop + channel.queue(sound) + channel.stop() + self.assertFalse(channel.get_busy()) + # check that new sounds can be played + channel.play(sound) + self.assertTrue(channel.get_busy()) + + +class ChannelSetVolumeTest(unittest.TestCase): + def setUp(self): + mixer.init() + self.channel = pygame.mixer.Channel(0) + self.sound = pygame.mixer.Sound(example_path("data/boom.wav")) - self.fail() + def tearDown(self): + mixer.quit() - def todo_test_stop(self): + def test_set_volume_with_one_argument(self): + self.channel.play(self.sound) + self.channel.set_volume(0.5) + self.assertEqual(self.channel.get_volume(), 0.5) - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.stop: + @unittest.expectedFailure + def test_set_volume_with_two_arguments(self): + # TODO: why doesn't this work? Seems to ignore stereo setting. + # https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Channel.set_volume + self.channel.play(self.sound) + self.channel.set_volume(0.3, 0.7) + self.assertEqual(self.channel.get_volume(), (0.3, 0.7)) - # Channel.stop(): return None - # stop playback on a Channel - # - # Stop sound playback on a channel. After playback is stopped the - # channel becomes available for new Sounds to play on it. - # - self.fail() +class ChannelEndEventTest(unittest.TestCase): + def setUp(self): + pygame.display.init() + pygame.display.set_mode((40, 40)) + if mixer.get_init() is None: + mixer.init() - def todo_test_unpause(self): + def tearDown(self): + pygame.display.quit() + mixer.quit() - # __doc__ (as of 2008-08-02) for pygame.mixer.Channel.unpause: + def test_get_endevent(self): + """Ensure Channel.get_endevent() returns the correct event type.""" + channel = mixer.Channel(0) + sound = mixer.Sound(example_path("data/house_lo.wav")) + channel.play(sound) - # Channel.unpause(): return None - # resume pause playback of a channel - # - # Resume the playback on a paused channel. + # Set the end event for the channel. + END_EVENT = pygame.USEREVENT + 1 + channel.set_endevent(END_EVENT) + got_end_event = channel.get_endevent() + self.assertEqual(got_end_event, END_EVENT) + + # Wait for the sound to finish playing. + channel.stop() + while channel.get_busy(): + pygame.time.wait(10) + + # Check that the end event was sent. + events = pygame.event.get(got_end_event) + self.assertTrue(len(events) > 0) - self.fail() ############################### SOUND CLASS TESTS ############################## -class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): - @classmethod - def setUpClass(cls): - # Initializing the mixer is slow, so minimize the times it is called. + +class TestSoundPlay(unittest.TestCase): + def setUp(self): mixer.init() + self.filename = example_path(os.path.join("data", "house_lo.wav")) + self.sound = mixer.Sound(file=self.filename) + def tearDown(self): + mixer.quit() + + def test_play_once(self): + """Test playing a sound once.""" + channel = self.sound.play() + self.assertIsInstance(channel, pygame.mixer.Channel) + self.assertTrue(channel.get_busy()) + + def test_play_multiple_times(self): + """Test playing a sound multiple times.""" + + # create a sound 100ms long + frequency, format, channels = mixer.get_init() + sound_length_in_ms = 100 + bytes_per_ms = int((frequency / 1000) * channels * (abs(format) // 8)) + sound = mixer.Sound(b"\x00" * int(sound_length_in_ms * bytes_per_ms)) + + self.assertAlmostEqual( + sound.get_length(), sound_length_in_ms / 1000.0, places=2 + ) + + num_loops = 5 + channel = sound.play(loops=num_loops) + self.assertIsInstance(channel, pygame.mixer.Channel) + + # the sound should be playing + pygame.time.wait((sound_length_in_ms * num_loops) - 100) + self.assertTrue(channel.get_busy()) + + # the sound should not be playing anymore + pygame.time.wait(sound_length_in_ms + 200) + self.assertFalse(channel.get_busy()) + + def test_play_indefinitely(self): + """Test playing a sound indefinitely.""" + frequency, format, channels = mixer.get_init() + sound_length_in_ms = 100 + bytes_per_ms = int((frequency / 1000) * channels * (abs(format) // 8)) + sound = mixer.Sound(b"\x00" * int(sound_length_in_ms * bytes_per_ms)) + + channel = sound.play(loops=-1) + self.assertIsInstance(channel, pygame.mixer.Channel) + + # we can't wait forever... so we wait 2 loops + for _ in range(2): + self.assertTrue(channel.get_busy()) + pygame.time.wait(sound_length_in_ms) + + def test_play_with_maxtime(self): + """Test playing a sound with maxtime.""" + channel = self.sound.play(maxtime=200) + self.assertIsInstance(channel, pygame.mixer.Channel) + self.assertTrue(channel.get_busy()) + pygame.time.wait(200 + 50) + self.assertFalse(channel.get_busy()) + + def test_play_with_fade_ms(self): + """Test playing a sound with fade_ms.""" + channel = self.sound.play(fade_ms=500) + self.assertIsInstance(channel, pygame.mixer.Channel) + self.assertTrue(channel.get_busy()) + pygame.time.wait(250) + + self.assertGreater(channel.get_volume(), 0.3) + self.assertLess(channel.get_volume(), 0.80) + + pygame.time.wait(300) + self.assertEqual(channel.get_volume(), 1.0) + + def test_play_with_invalid_loops(self): + """Test playing a sound with invalid loops.""" + with self.assertRaises(TypeError): + self.sound.play(loops="invalid") + + def test_play_with_invalid_maxtime(self): + """Test playing a sound with invalid maxtime.""" + with self.assertRaises(TypeError): + self.sound.play(maxtime="invalid") + + def test_play_with_invalid_fade_ms(self): + """Test playing a sound with invalid fade_ms.""" + with self.assertRaises(TypeError): + self.sound.play(fade_ms="invalid") + + +class SoundTypeTest(unittest.TestCase): @classmethod def tearDownClass(cls): mixer.quit() @@ -820,7 +1021,7 @@ class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): # and test_array_keyword() for additional testing of Sound() creation. def test_sound(self): """Ensure Sound() creation with a filename works.""" - filename = example_path(os.path.join('data', 'house_lo.wav')) + filename = example_path(os.path.join("data", "house_lo.wav")) sound1 = mixer.Sound(filename) sound2 = mixer.Sound(file=filename) @@ -829,7 +1030,7 @@ class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_sound__from_file_object(self): """Ensure Sound() creation with a file object works.""" - filename = example_path(os.path.join('data', 'house_lo.wav')) + filename = example_path(os.path.join("data", "house_lo.wav")) # Using 'with' ensures the file is closed even if test fails. with open(filename, "rb") as file_obj: @@ -839,13 +1040,21 @@ class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_sound__from_sound_object(self): """Ensure Sound() creation with a Sound() object works.""" - filename = example_path(os.path.join('data', 'house_lo.wav')) + filename = example_path(os.path.join("data", "house_lo.wav")) sound_obj = mixer.Sound(file=filename) sound = mixer.Sound(sound_obj) self.assertIsInstance(sound, mixer.Sound) + def test_sound__from_pathlib(self): + """Ensure Sound() creation with a pathlib.Path object works.""" + path = pathlib.Path(example_path(os.path.join("data", "house_lo.wav"))) + sound1 = mixer.Sound(path) + sound2 = mixer.Sound(file=path) + self.assertIsInstance(sound1, mixer.Sound) + self.assertIsInstance(sound2, mixer.Sound) + def todo_test_sound__from_buffer(self): """Ensure Sound() creation with a buffer works.""" self.fail() @@ -862,168 +1071,369 @@ class SoundTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_sound__before_init(self): """Ensure exception raised for Sound() creation with non-init mixer.""" mixer.quit() - filename = example_path(os.path.join('data', 'house_lo.wav')) + filename = example_path(os.path.join("data", "house_lo.wav")) - with self.assertRaisesRegex(pygame.error, 'mixer not initialized'): + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): mixer.Sound(file=filename) - @unittest.skipIf(IS_PYPY, 'pypy skip') + @unittest.skipIf(IS_PYPY, "pypy skip") def test_samples_address(self): """Test the _samples_address getter.""" - from ctypes import pythonapi, c_void_p, py_object - try: - Bytes_FromString = pythonapi.PyBytes_FromString # python 3 - except: - Bytes_FromString = pythonapi.PyString_FromString # python 2 - - Bytes_FromString.restype = c_void_p - Bytes_FromString.argtypes = [py_object] - samples = as_bytes('abcdefgh') # keep byte size a multiple of 4 - sample_bytes = Bytes_FromString(samples) - - snd = mixer.Sound(buffer=samples) - - self.assertNotEqual(snd._samples_address, sample_bytes) + from ctypes import pythonapi, c_void_p, py_object - def todo_test_fadeout(self): + Bytes_FromString = pythonapi.PyBytes_FromString - # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.fadeout: + Bytes_FromString.restype = c_void_p + Bytes_FromString.argtypes = [py_object] + samples = b"abcdefgh" # keep byte size a multiple of 4 + sample_bytes = Bytes_FromString(samples) - # Sound.fadeout(time): return None - # stop sound playback after fading out - # - # This will stop playback of the sound after fading it out over the - # time argument in milliseconds. The Sound will fade and stop on all - # actively playing channels. - # + snd = mixer.Sound(buffer=samples) - self.fail() - - def todo_test_get_length(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.get_length: - - # Sound.get_length(): return seconds - # get the length of the Sound - # - # Return the length of this Sound in seconds. + self.assertNotEqual(snd._samples_address, sample_bytes) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + snd._samples_address - self.fail() + def test_get_length(self): + """Tests if get_length returns a correct length.""" + try: + for size in SIZES: + pygame.mixer.quit() + pygame.mixer.init(size=size) + filename = example_path(os.path.join("data", "punch.wav")) + sound = mixer.Sound(file=filename) + # The sound data is in the mixer output format. So dividing the + # length of the raw sound data by the mixer settings gives + # the expected length of the sound. + sound_bytes = sound.get_raw() + mix_freq, mix_bits, mix_channels = pygame.mixer.get_init() + mix_bytes = abs(mix_bits) / 8 + expected_length = ( + float(len(sound_bytes)) / mix_freq / mix_bytes / mix_channels + ) + self.assertAlmostEqual(expected_length, sound.get_length()) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_length() def test_get_num_channels(self): - """Ensure correct number of channels.""" - expected_channels = 0 - filename = example_path(os.path.join('data', 'house_lo.wav')) - sound = mixer.Sound(file=filename) - - num_channels = sound.get_num_channels() - - self.assertEqual(num_channels, expected_channels) - - def todo_test_get_num_channels__while_playing(self): - """Ensure correct number of channels while playing.""" - self.fail() + """ + Tests if Sound.get_num_channels returns the correct number + of channels playing a specific sound. + """ + try: + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + self.assertEqual(sound.get_num_channels(), 0) + sound.play() + self.assertEqual(sound.get_num_channels(), 1) + sound.play() + self.assertEqual(sound.get_num_channels(), 2) + sound.stop() + self.assertEqual(sound.get_num_channels(), 0) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_num_channels() def test_get_volume(self): """Ensure a sound's volume can be retrieved.""" - expected_volume = 1.0 # default - filename = example_path(os.path.join('data', 'house_lo.wav')) - sound = mixer.Sound(file=filename) + try: + expected_volume = 1.0 # default + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) - volume = sound.get_volume() + volume = sound.get_volume() - self.assertAlmostEqual(volume, expected_volume) + self.assertAlmostEqual(volume, expected_volume) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_volume() - def todo_test_get_volume__while_playing(self): + def test_get_volume__while_playing(self): """Ensure a sound's volume can be retrieved while playing.""" - self.fail() + try: + expected_volume = 1.0 # default + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + sound.play(-1) - def todo_test_play(self): - - # __doc__ (as of 2008-08-02) for pygame.mixer.Sound.play: - - # Sound.play(loops=0, maxtime=0, fade_ms=0): return Channel - # begin sound playback - # - # Begin playback of the Sound (i.e., on the computer's speakers) on an - # available Channel. This will forcibly select a Channel, so playback - # may cut off a currently playing sound if necessary. - # - # The loops argument controls how many times the sample will be - # repeated after being played the first time. A value of 5 means that - # the sound will be played once, then repeated five times, and so is - # played a total of six times. The default value (zero) means the - # Sound is not repeated, and so is only played once. If loops is set - # to -1 the Sound will loop indefinitely (though you can still call - # stop() to stop it). - # - # The maxtime argument can be used to stop playback after a given - # number of milliseconds. - # - # The fade_ms argument will make the sound start playing at 0 volume - # and fade up to full volume over the time given. The sample may end - # before the fade-in is complete. - # - # This returns the Channel object for the channel that was selected. + volume = sound.get_volume() - self.fail() + self.assertAlmostEqual(volume, expected_volume) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.get_volume() def test_set_volume(self): """Ensure a sound's volume can be set.""" - float_delta = 1.0 / 128 # SDL volume range is 0 to 128 - filename = example_path(os.path.join('data', 'house_lo.wav')) - sound = mixer.Sound(file=filename) - current_volume = sound.get_volume() - - # (volume_set_value : expected_volume) - volumes = ((-1, current_volume), # value < 0 won't change volume - (0, 0.0), - (0.01, 0.01), - (0.1, 0.1), - (0.5, 0.5), - (0.9, 0.9), - (0.99, 0.99), - (1, 1.0), - (1.1, 1.0), - (2.0, 1.0)) - - for volume_set_value, expected_volume in volumes: - sound.set_volume(volume_set_value) - - self.assertAlmostEqual(sound.get_volume(), expected_volume, - delta=float_delta) - - def todo_test_set_volume__while_playing(self): + try: + float_delta = 1.0 / 128 # SDL volume range is 0 to 128 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + current_volume = sound.get_volume() + + # (volume_set_value : expected_volume) + volumes = ( + (-1, current_volume), # value < 0 won't change volume + (0, 0.0), + (0.01, 0.01), + (0.1, 0.1), + (0.5, 0.5), + (0.9, 0.9), + (0.99, 0.99), + (1, 1.0), + (1.1, 1.0), + (2.0, 1.0), + ) + + for volume_set_value, expected_volume in volumes: + sound.set_volume(volume_set_value) + + self.assertAlmostEqual( + sound.get_volume(), expected_volume, delta=float_delta + ) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.set_volume(1) + + def test_set_volume__while_playing(self): """Ensure a sound's volume can be set while playing.""" - self.fail() + try: + float_delta = 1.0 / 128 # SDL volume range is 0 to 128 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + current_volume = sound.get_volume() + + # (volume_set_value : expected_volume) + volumes = ( + (-1, current_volume), # value < 0 won't change volume + (0, 0.0), + (0.01, 0.01), + (0.1, 0.1), + (0.5, 0.5), + (0.9, 0.9), + (0.99, 0.99), + (1, 1.0), + (1.1, 1.0), + (2.0, 1.0), + ) + + sound.play(loops=-1) + for volume_set_value, expected_volume in volumes: + sound.set_volume(volume_set_value) + + self.assertAlmostEqual( + sound.get_volume(), expected_volume, delta=float_delta + ) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.set_volume(1) def test_stop(self): """Ensure stop can be called while not playing a sound.""" - expected_channels = 0 - filename = example_path(os.path.join('data', 'house_lo.wav')) - sound = mixer.Sound(file=filename) + try: + expected_channels = 0 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) - sound.stop() + sound.stop() - self.assertEqual(sound.get_num_channels(), expected_channels) + self.assertEqual(sound.get_num_channels(), expected_channels) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.stop() - def todo_test_stop__while_playing(self): + def test_stop__while_playing(self): """Ensure stop stops a playing sound.""" - self.fail() + try: + expected_channels = 0 + filename = example_path(os.path.join("data", "house_lo.wav")) + sound = mixer.Sound(file=filename) + + sound.play(-1) + sound.stop() + + self.assertEqual(sound.get_num_channels(), expected_channels) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + sound.stop() def test_get_raw(self): """Ensure get_raw returns the correct bytestring.""" - samples = as_bytes('abcdefgh') # keep byte size a multiple of 4 - snd = mixer.Sound(buffer=samples) + try: + samples = b"abcdefgh" # keep byte size a multiple of 4 + snd = mixer.Sound(buffer=samples) + + raw = snd.get_raw() + + self.assertIsInstance(raw, bytes) + self.assertEqual(raw, samples) + finally: + pygame.mixer.quit() + with self.assertRaisesRegex(pygame.error, "mixer not initialized"): + snd.get_raw() + + def test_correct_subclassing(self): + class CorrectSublass(mixer.Sound): + def __init__(self, file): + super().__init__(file=file) + + filename = example_path(os.path.join("data", "house_lo.wav")) + correct = CorrectSublass(filename) + + try: + correct.get_volume() + except Exception: + self.fail("This should not raise an exception.") + + def test_incorrect_subclassing(self): + class IncorrectSuclass(mixer.Sound): + def __init__(self): + pass + + incorrect = IncorrectSuclass() - raw = snd.get_raw() + self.assertRaises(RuntimeError, incorrect.get_volume) - self.assertIsInstance(raw, bytes_) - self.assertEqual(raw, samples) + +class TestSoundFadeout(unittest.TestCase): + def setUp(self): + if mixer.get_init() is None: + pygame.mixer.init() + + def tearDown(self): + pygame.mixer.quit() + + def test_fadeout_with_valid_time(self): + """Tests if fadeout stops sound playback after fading it out over the time argument in milliseconds.""" + filename = example_path(os.path.join("data", "punch.wav")) + sound = mixer.Sound(file=filename) + channel = sound.play() + channel.fadeout(1000) + pygame.time.wait(2000) + self.assertFalse(channel.get_busy()) + + # TODO: this fails. + # def test_fadeout_with_zero_time(self): + # """Tests if fadeout stops sound playback immediately when time argument is zero.""" + # filename = example_path(os.path.join("data", "punch.wav")) + # sound = mixer.Sound(file=filename) + # channel = sound.play() + # channel.fadeout(0) + # self.assertFalse(channel.get_busy()) + + # TODO: this fails. + # def test_fadeout_with_negative_time(self): + # """Tests if fadeout stops sound playback immediately when time argument is negative.""" + # filename = example_path(os.path.join("data", "punch.wav")) + # sound = mixer.Sound(file=filename) + # channel = sound.play() + # channel.fadeout(-1000) + # self.assertFalse(channel.get_busy()) + + # TODO: What should happen here? + # def test_fadeout_with_large_time(self): + # """Tests if fadeout stops sound playback after fading it out over the time argument in milliseconds, even if time is larger than the sound length.""" + # filename = example_path(os.path.join("data", "punch.wav")) + # sound = mixer.Sound(file=filename) + # channel = sound.play() + # channel.fadeout(...?) + # pygame.time.wait(...?) + # self.assertFalse(channel.get_busy()) + + +class TestGetBusy(unittest.TestCase): + """Test pygame.mixer.get_busy. + + |tags:slow| + """ + + def setUp(self): + pygame.mixer.init() + + def tearDown(self): + pygame.mixer.quit() + + def test_no_sound_playing(self): + """ + Test that get_busy returns False when no sound is playing. + """ + self.assertFalse(pygame.mixer.get_busy()) + + def test_one_sound_playing(self): + """ + Test that get_busy returns True when one sound is playing. + """ + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound.play() + time.sleep(0.2) + self.assertTrue(pygame.mixer.get_busy()) + sound.stop() + + def test_multiple_sounds_playing(self): + """ + Test that get_busy returns True when multiple sounds are playing. + """ + sound1 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound2 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound1.play() + sound2.play() + time.sleep(0.2) + self.assertTrue(pygame.mixer.get_busy()) + sound1.stop() + sound2.stop() + + def test_all_sounds_stopped(self): + """ + Test that get_busy returns False when all sounds are stopped. + """ + sound1 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound2 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound1.play() + sound2.play() + time.sleep(0.2) + sound1.stop() + sound2.stop() + time.sleep(0.2) + self.assertFalse(pygame.mixer.get_busy()) + + def test_all_sounds_stopped_with_fadeout(self): + """ + Test that get_busy returns False when all sounds are stopped with + fadeout. + """ + sound1 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound2 = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound1.play() + sound2.play() + time.sleep(0.2) + sound1.fadeout(100) + sound2.fadeout(100) + time.sleep(0.3) + self.assertFalse(pygame.mixer.get_busy()) + + def test_sound_fading_out(self): + """Tests that get_busy() returns True when a sound is fading out""" + sound = pygame.mixer.Sound(example_path("data/house_lo.wav")) + sound.play(fade_ms=1000) + time.sleep(1.1) + self.assertTrue(pygame.mixer.get_busy()) + sound.stop() ##################################### MAIN ##################################### -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/mouse_test.py b/venv/Lib/site-packages/pygame/tests/mouse_test.py index 8215cc80aa652960f6882df4e5a006dfcd1d0cd4..dc3d578989661b6549a58b89bc17cf9bb6d16270 100644 --- a/venv/Lib/site-packages/pygame/tests/mouse_test.py +++ b/venv/Lib/site-packages/pygame/tests/mouse_test.py @@ -1,150 +1,348 @@ import unittest +import os +import platform +import warnings +import pygame -class MouseModuleTest(unittest.TestCase): - def todo_test_get_cursor(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.get_cursor: - - # pygame.mouse.get_cursor(): return (size, hotspot, xormasks, andmasks) - # get the image for the system mouse cursor - # - # Get the information about the mouse system cursor. The return value - # is the same data as the arguments passed into - # pygame.mouse.set_cursor(). - # - - self.fail() - - def todo_test_get_focused(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.get_focused: - - # pygame.mouse.get_focused(): return bool - # check if the display is receiving mouse input - # - # Returns true when pygame is receiving mouse input events (or, in - # windowing terminology, is "active" or has the "focus"). - # - # This method is most useful when working in a window. By contrast, in - # full-screen mode, this method always returns true. - # - # Note: under MS Windows, the window that has the mouse focus also has - # the keyboard focus. But under X-Windows, one window can receive - # mouse events and another receive keyboard events. - # pygame.mouse.get_focused() indicates whether the pygame window - # receives mouse events. - # - - self.fail() - - def todo_test_get_pos(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.get_pos: - - # pygame.mouse.get_pos(): return (x, y) - # get the mouse cursor position - # - # Returns the X and Y position of the mouse cursor. The position is - # relative the the top-left corner of the display. The cursor position - # can be located outside of the display window, but is always - # constrained to the screen. - # - - self.fail() - - def todo_test_get_pressed(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.get_pressed: - - # pygame.moouse.get_pressed(): return (button1, button2, button3) - # get the state of the mouse buttons - # - # Returns a sequence of booleans representing the state of all the - # mouse buttons. A true value means the mouse is currently being - # pressed at the time of the call. - # - # Note, to get all of the mouse events it is better to use either - # pygame.event.wait() or pygame.event.get() and check all of those events - # to see if they are MOUSEBUTTONDOWN, MOUSEBUTTONUP, or MOUSEMOTION. - # Note, that on X11 some XServers use middle button emulation. When - # you click both buttons 1 and 3 at the same time a 2 button event can - # be emitted. - # - # Note, remember to call pygame.event.get() before this function. - # Otherwise it will not work. - # - - self.fail() - - def todo_test_get_rel(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.get_rel: - - # pygame.mouse.get_rel(): return (x, y) - # get the amount of mouse movement - # - # Returns the amount of movement in X and Y since the previous call to - # this function. The relative movement of the mouse cursor is - # constrained to the edges of the screen, but see the virtual input - # mouse mode for a way around this. Virtual input mode is described - # at the top of the page. - # - - self.fail() - - def todo_test_set_cursor(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.set_cursor: - - # pygame.mouse.set_cursor(size, hotspot, xormasks, andmasks): return None - # set the image for the system mouse cursor - # - # When the mouse cursor is visible, it will be displayed as a black - # and white bitmap using the given bitmask arrays. The size is a - # sequence containing the cursor width and height. Hotspot is a - # sequence containing the cursor hotspot position. xormasks is a - # sequence of bytes containing the cursor xor data masks. Lastly is - # andmasks, a sequence of bytes containting the cursor bitmask data. - # - # Width must be a multiple of 8, and the mask arrays must be the - # correct size for the given width and height. Otherwise an exception - # is raised. - # - # See the pygame.cursor module for help creating default and custom - # masks for the system cursor. - # - - self.fail() - - def todo_test_set_pos(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.set_pos: - - # pygame.mouse.set_pos([x, y]): return None - # set the mouse cursor position - # - # Set the current mouse position to arguments given. If the mouse - # cursor is visible it will jump to the new coordinates. Moving the - # mouse will generate a new pygaqme.MOUSEMOTION event. - # - - self.fail() - - def todo_test_set_visible(self): - - # __doc__ (as of 2008-08-02) for pygame.mouse.set_visible: - - # pygame.mouse.set_visible(bool): return bool - # hide or show the mouse cursor - # - # If the bool argument is true, the mouse cursor will be visible. This - # will return the previous visible state of the cursor. - # - - self.fail() +DARWIN = "Darwin" in platform.platform() + + +class MouseTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + # The display needs to be initialized for mouse functions. + pygame.display.init() + + @classmethod + def tearDownClass(cls): + pygame.display.quit() + + +class MouseModuleInteractiveTest(MouseTests): + __tags__ = ["interactive"] + + def test_set_pos(self): + """Ensures set_pos works correctly. + Requires tester to move the mouse to be on the window. + """ + pygame.display.set_mode((500, 500)) + pygame.event.get() # Pump event queue to make window get focus on macos. + + if not pygame.mouse.get_focused(): + # The window needs to be focused for the mouse.set_pos to work on macos. + return + clock = pygame.time.Clock() + + expected_pos = ((10, 0), (0, 0), (499, 0), (499, 499), (341, 143), (94, 49)) + + for x, y in expected_pos: + pygame.mouse.set_pos(x, y) + pygame.event.get() + found_pos = pygame.mouse.get_pos() + + clock.tick() + time_passed = 0.0 + ready_to_test = False + + while not ready_to_test and time_passed <= 1000.0: # Avoid endless loop + time_passed += clock.tick() + for event in pygame.event.get(): + if event.type == pygame.MOUSEMOTION: + ready_to_test = True + + self.assertEqual(found_pos, (x, y)) + + +class MouseModuleTest(MouseTests): + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER", "") == "dummy", + "Cursors not supported on headless test machines", + ) + def test_get_cursor(self): + """Ensures get_cursor works correctly.""" + + # error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.display.quit() + pygame.mouse.get_cursor() + + pygame.display.init() + + size = (8, 8) + hotspot = (0, 0) + xormask = (0, 96, 120, 126, 112, 96, 0, 0) + andmask = (224, 240, 254, 255, 254, 240, 96, 0) + + expected_length = 4 + expected_cursor = pygame.cursors.Cursor(size, hotspot, xormask, andmask) + pygame.mouse.set_cursor(expected_cursor) + + try: + cursor = pygame.mouse.get_cursor() + + self.assertIsInstance(cursor, pygame.cursors.Cursor) + self.assertEqual(len(cursor), expected_length) + + for info in cursor: + self.assertIsInstance(info, tuple) + + pygame.mouse.set_cursor(size, hotspot, xormask, andmask) + self.assertEqual(pygame.mouse.get_cursor(), expected_cursor) + + # SDLError should be raised when the mouse cursor is NULL + except pygame.error: + with self.assertRaises(pygame.error): + pygame.mouse.get_cursor() + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER", "") == "dummy", + "mouse.set_system_cursor only available in SDL2", + ) + def test_set_system_cursor(self): + """Ensures set_system_cursor works correctly.""" + + with warnings.catch_warnings(record=True) as w: + """From Pygame 2.0.1, set_system_cursor() should raise a deprecation warning""" + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + + # Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.display.quit() + pygame.mouse.set_system_cursor(pygame.SYSTEM_CURSOR_HAND) + + pygame.display.init() + + # TypeError raised when PyArg_ParseTuple fails to parse parameters + with self.assertRaises(TypeError): + pygame.mouse.set_system_cursor("b") + with self.assertRaises(TypeError): + pygame.mouse.set_system_cursor(None) + with self.assertRaises(TypeError): + pygame.mouse.set_system_cursor((8, 8), (0, 0)) + + # Right type, invalid value + with self.assertRaises(pygame.error): + pygame.mouse.set_system_cursor(2000) + + # Working as intended + self.assertEqual( + pygame.mouse.set_system_cursor(pygame.SYSTEM_CURSOR_ARROW), None + ) + + # Making sure the warnings are working properly + self.assertEqual(len(w), 6) + self.assertTrue( + all([issubclass(warn.category, DeprecationWarning) for warn in w]) + ) + + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER", "") == "dummy", + "Cursors not supported on headless test machines", + ) + def test_set_cursor(self): + """Ensures set_cursor works correctly.""" + + # Bitmap cursor information + size = (8, 8) + hotspot = (0, 0) + xormask = (0, 126, 64, 64, 32, 16, 0, 0) + andmask = (254, 255, 254, 112, 56, 28, 12, 0) + bitmap_cursor = pygame.cursors.Cursor(size, hotspot, xormask, andmask) + + # System cursor information + constant = pygame.SYSTEM_CURSOR_ARROW + system_cursor = pygame.cursors.Cursor(constant) + + # Color cursor information (also uses hotspot variable from Bitmap cursor info) + surface = pygame.Surface((10, 10)) + color_cursor = pygame.cursors.Cursor(hotspot, surface) + + pygame.display.quit() + + # Bitmap: Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.mouse.set_cursor(bitmap_cursor) + + # System: Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.mouse.set_cursor(system_cursor) + + # Color: Error should be raised when the display is uninitialized + with self.assertRaises(pygame.error): + pygame.mouse.set_cursor(color_cursor) + + pygame.display.init() + + # Bitmap: TypeError raised when PyArg_ParseTuple fails to parse parameters + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(("w", "h"), hotspot, xormask, andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, ("0", "0"), xormask, andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, ("x", "y", "z"), xormask, andmask) + + # Bitmap: TypeError raised when either mask is not a sequence + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, 12345678, andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, xormask, 12345678) + + # Bitmap: TypeError raised when element of mask is not an integer + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, "00000000", andmask) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(size, hotspot, xormask, (2, [0], 4, 0, 0, 8, 0, 1)) + + # Bitmap: ValueError raised when width not divisible by 8 + with self.assertRaises(ValueError): + pygame.mouse.set_cursor((3, 8), hotspot, xormask, andmask) + + # Bitmap: ValueError raised when length of either mask != width * height / 8 + with self.assertRaises(ValueError): + pygame.mouse.set_cursor((16, 2), hotspot, (128, 64, 32), andmask) + with self.assertRaises(ValueError): + pygame.mouse.set_cursor((16, 2), hotspot, xormask, (192, 96, 48, 0, 1)) + + # Bitmap: Working as intended + self.assertEqual( + pygame.mouse.set_cursor((16, 1), hotspot, (8, 0), (0, 192)), None + ) + pygame.mouse.set_cursor(size, hotspot, xormask, andmask) + self.assertEqual(pygame.mouse.get_cursor(), bitmap_cursor) + + # Bitmap: Working as intended + lists + masks with no references + pygame.mouse.set_cursor(size, hotspot, list(xormask), list(andmask)) + self.assertEqual(pygame.mouse.get_cursor(), bitmap_cursor) + + # System: TypeError raised when constant is invalid + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(-50021232) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor("yellow") + + # System: Working as intended + self.assertEqual(pygame.mouse.set_cursor(constant), None) + pygame.mouse.set_cursor(constant) + self.assertEqual(pygame.mouse.get_cursor(), system_cursor) + pygame.mouse.set_cursor(system_cursor) + self.assertEqual(pygame.mouse.get_cursor(), system_cursor) + + # Color: TypeError raised with invalid parameters + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(("x", "y"), surface) + with self.assertRaises(TypeError): + pygame.mouse.set_cursor(hotspot, "not_a_surface") + + # Color: Working as intended + self.assertEqual(pygame.mouse.set_cursor(hotspot, surface), None) + pygame.mouse.set_cursor(hotspot, surface) + self.assertEqual(pygame.mouse.get_cursor(), color_cursor) + pygame.mouse.set_cursor(color_cursor) + self.assertEqual(pygame.mouse.get_cursor(), color_cursor) + + # Color: Working as intended + Surface with no references is returned okay + pygame.mouse.set_cursor((0, 0), pygame.Surface((20, 20))) + cursor = pygame.mouse.get_cursor() + self.assertEqual(cursor.type, "color") + self.assertEqual(cursor.data[0], (0, 0)) + self.assertEqual(cursor.data[1].get_size(), (20, 20)) + + def test_get_focused(self): + """Ensures get_focused returns the correct type.""" + focused = pygame.mouse.get_focused() + + self.assertIsInstance(focused, int) + + def test_get_pressed(self): + """Ensures get_pressed returns the correct types.""" + expected_length = 3 + buttons_pressed = pygame.mouse.get_pressed() + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + expected_length = 5 + buttons_pressed = pygame.mouse.get_pressed(num_buttons=5) + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + expected_length = 3 + buttons_pressed = pygame.mouse.get_pressed(3) + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + expected_length = 5 + buttons_pressed = pygame.mouse.get_pressed(5) + self.assertIsInstance(buttons_pressed, tuple) + self.assertEqual(len(buttons_pressed), expected_length) + for value in buttons_pressed: + self.assertIsInstance(value, bool) + + with self.assertRaises(ValueError): + pygame.mouse.get_pressed(4) + + def test_get_pos(self): + """Ensures get_pos returns the correct types.""" + expected_length = 2 + + pos = pygame.mouse.get_pos() + + self.assertIsInstance(pos, tuple) + self.assertEqual(len(pos), expected_length) + for value in pos: + self.assertIsInstance(value, int) + + def test_set_pos__invalid_pos(self): + """Ensures set_pos handles invalid positions correctly.""" + for invalid_pos in ((1,), [1, 2, 3], 1, "1", (1, "1"), []): + with self.assertRaises(TypeError): + pygame.mouse.set_pos(invalid_pos) + + def test_get_rel(self): + """Ensures get_rel returns the correct types.""" + expected_length = 2 + + rel = pygame.mouse.get_rel() + + self.assertIsInstance(rel, tuple) + self.assertEqual(len(rel), expected_length) + for value in rel: + self.assertIsInstance(value, int) + + def test_get_visible(self): + """Ensures get_visible works correctly.""" + for expected_value in (False, True): + pygame.mouse.set_visible(expected_value) + + visible = pygame.mouse.get_visible() + + self.assertEqual(visible, expected_value) + + def test_set_visible(self): + """Ensures set_visible returns the correct values.""" + # Set to a known state. + pygame.mouse.set_visible(True) + + for expected_visible in (False, True): + prev_visible = pygame.mouse.set_visible(expected_visible) + + self.assertEqual(prev_visible, not expected_visible) + + def test_set_visible__invalid_value(self): + """Ensures set_visible handles invalid positions correctly.""" + for invalid_value in ((1,), [1, 2, 3], 1.1, "1", (1, "1"), []): + with self.assertRaises(TypeError): + prev_visible = pygame.mouse.set_visible(invalid_value) + ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/overlay_tags.py b/venv/Lib/site-packages/pygame/tests/overlay_tags.py deleted file mode 100644 index a92aa6a5240b21f9670c7616bcf65d5eab86b20e..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/overlay_tags.py +++ /dev/null @@ -1,2 +0,0 @@ -# Overlay support was removed in SDL 2 -__tags__ = ['SDL2_ignore'] diff --git a/venv/Lib/site-packages/pygame/tests/overlay_test.py b/venv/Lib/site-packages/pygame/tests/overlay_test.py deleted file mode 100644 index d5c17996747581ee37dc500d3f13a54394613a44..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/overlay_test.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest - - -class OverlayTypeTest(unittest.TestCase): - def todo_test_display(self): - - # __doc__ (as of 2008-08-02) for pygame.overlay.overlay.display: - - # Overlay.display((y, u, v)): return None - # Overlay.display(): return None - # set the overlay pixel data - - self.fail() - - def todo_test_get_hardware(self): - - # __doc__ (as of 2008-08-02) for pygame.overlay.overlay.get_hardware: - - # Overlay.get_hardware(rect): return int - # test if the Overlay is hardware accelerated - - self.fail() - - def todo_test_set_location(self): - - # __doc__ (as of 2008-08-02) for pygame.overlay.overlay.set_location: - - # Overlay.set_location(rect): return None - # control where the overlay is displayed - - self.fail() - -################################################################################ - -if __name__ == '__main__': - unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/pixelarray_test.py b/venv/Lib/site-packages/pygame/tests/pixelarray_test.py index f877ed2e53075e7720472168454331a7a951aec8..7b1cf4207958dc59773835f628f1c589c85cde54 100644 --- a/venv/Lib/site-packages/pygame/tests/pixelarray_test.py +++ b/venv/Lib/site-packages/pygame/tests/pixelarray_test.py @@ -1,13 +1,10 @@ -import sys -import platform -try: - reduce -except NameError: - from functools import reduce -import operator -import weakref import gc +import operator +import platform +import sys import unittest +import weakref +from functools import reduce from pygame.tests.test_utils import SurfaceSubclass @@ -17,133 +14,323 @@ except NameError: pass import pygame -from pygame.compat import xrange_ -PY3 = sys.version_info >= (3, 0, 0) -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() + + +class TestMixin: + def assert_surfaces_equal(self, s1, s2, msg=None): + """Checks if two surfaces are equal in size and color.""" + w, h = s1.get_size() + + self.assertTupleEqual((w, h), s2.get_size(), msg) + + msg = "" if msg is None else f"{msg}, " + msg += f"size: ({w}, {h})" -class TestMixin (object): - def assert_surfaces_equal (self, s1, s2): - # Assumes the surfaces are the same size. - w, h = s1.get_size () - for x in range (w): - for y in range (h): - self.assertEqual (s1.get_at ((x, y)), s2.get_at ((x, y)), - "size: (%i, %i), position: (%i, %i)" % - (w, h, x, y)) + for x in range(w): + for y in range(h): + self.assertEqual( + s1.get_at((x, y)), + s2.get_at((x, y)), + f"{msg}, position: ({x}, {y})", + ) -class PixelArrayTypeTest (unittest.TestCase, TestMixin): + def assert_surface_filled(self, surface, expected_color, msg=None): + """Checks if the surface is filled with the given color.""" + width, height = surface.get_size() + + surface.lock() # Lock for possible speed up. + for pos in ((x, y) for y in range(height) for x in range(width)): + self.assertEqual(surface.get_at(pos), expected_color, msg) + surface.unlock() + + +@unittest.skipIf(IS_PYPY, "pypy having issues") +class PixelArrayTypeTest(unittest.TestCase, TestMixin): def test_compare(self): # __doc__ (as of 2008-06-25) for pygame.pixelarray.PixelArray.compare: - # PixelArray.compare (array, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray - # Compares the PixelArray with another one. + # PixelArray.compare (array, distance=0, weights=(0.299, 0.587, 0.114)): Return PixelArray + # Compares the PixelArray with another one. w = 10 h = 20 size = w, h - sf = pygame.Surface (size, 0, 32) - ar = pygame.PixelArray (sf) - sf2 = pygame.Surface (size, 0, 32) - self.assertRaises (TypeError, ar.compare, sf2) - ar2 = pygame.PixelArray (sf2) - ar3 = ar.compare (ar2) - self.assertTrue(isinstance (ar3, pygame.PixelArray)) - self.assertEqual (ar3.shape, size) - sf2.fill (pygame.Color ('white')) - self.assert_surfaces_equal (sf2, ar3.surface) + sf = pygame.Surface(size, 0, 32) + ar = pygame.PixelArray(sf) + sf2 = pygame.Surface(size, 0, 32) + self.assertRaises(TypeError, ar.compare, sf2) + ar2 = pygame.PixelArray(sf2) + ar3 = ar.compare(ar2) + self.assertTrue(isinstance(ar3, pygame.PixelArray)) + self.assertEqual(ar3.shape, size) + sf2.fill(pygame.Color("white")) + self.assert_surfaces_equal(sf2, ar3.surface) del ar3 - r = pygame.Rect (2, 5, 6, 13) - sf.fill (pygame.Color ('blue'), r) - sf2.fill (pygame.Color ('red')) - sf2.fill (pygame.Color ('blue'), r) - ar3 = ar.compare (ar2) - sf.fill (pygame.Color ('white'), r) - self.assert_surfaces_equal (sf, ar3.surface) + r = pygame.Rect(2, 5, 6, 13) + sf.fill(pygame.Color("blue"), r) + sf2.fill(pygame.Color("red")) + sf2.fill(pygame.Color("blue"), r) + ar3 = ar.compare(ar2) + sf.fill(pygame.Color("white"), r) + self.assert_surfaces_equal(sf, ar3.surface) # FINISH ME! # Test other bit depths, slices, and distance != 0. - def test_close(self): - """ does not crash when it is deleted. + def test_compare__same_colors_within_distance(self): + """Ensures compare works correctly with same colored surfaces.""" + size = (3, 5) + pixelarray_result_color = pygame.Color("white") + surface_color = (127, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_color = surf_a.get_at((0, 0)) + + pixelarray_a = pygame.PixelArray(surf_a) + pixelarray_b = pygame.PixelArray(surf_a.copy()) + + for distance in (0.0, 0.01, 0.1, 1.0): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_compare__different_colors_within_distance(self): + """Ensures compare works correctly with different colored surfaces + and the color difference is within the given distance. + """ + size = (3, 5) + pixelarray_result_color = pygame.Color("white") + surface_a_color = (127, 127, 127, 255) + surface_b_color = (128, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_a_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_a_color = surf_a.get_at((0, 0)) + pixelarray_a = pygame.PixelArray(surf_a) + + surf_b = expected_pixelarray_surface.copy() + surf_b.fill(surface_b_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_b_color = surf_b.get_at((0, 0)) + pixelarray_b = pygame.PixelArray(surf_b) + + for distance in (0.2, 0.3, 0.5, 1.0): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_a_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_b_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_compare__different_colors_not_within_distance(self): + """Ensures compare works correctly with different colored surfaces + and the color difference is not within the given distance. """ - s = pygame.Surface((10,10)) + size = (3, 5) + pixelarray_result_color = pygame.Color("black") + surface_a_color = (127, 127, 127, 255) + surface_b_color = (128, 127, 127, 255) + + for depth in (8, 16, 24, 32): + expected_pixelarray_surface = pygame.Surface(size, depth=depth) + expected_pixelarray_surface.fill(pixelarray_result_color) + + # Copy the surface to ensure same dimensions/formatting. + surf_a = expected_pixelarray_surface.copy() + surf_a.fill(surface_a_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_a_color = surf_a.get_at((0, 0)) + pixelarray_a = pygame.PixelArray(surf_a) + + surf_b = expected_pixelarray_surface.copy() + surf_b.fill(surface_b_color) + # For non-32 bit depths, the actual color can be different from what + # was filled. + expected_surface_b_color = surf_b.get_at((0, 0)) + pixelarray_b = pygame.PixelArray(surf_b) + + for distance in (0.0, 0.00001, 0.0001, 0.001): + pixelarray_result = pixelarray_a.compare( + pixelarray_b, distance=distance + ) + + # Ensure the resulting pixelarray is correct and that the original + # surfaces were not changed. + self.assert_surfaces_equal( + pixelarray_result.surface, + expected_pixelarray_surface, + (depth, distance), + ) + self.assert_surface_filled( + pixelarray_a.surface, expected_surface_a_color, (depth, distance) + ) + self.assert_surface_filled( + pixelarray_b.surface, expected_surface_b_color, (depth, distance) + ) + + pixelarray_a.close() + pixelarray_b.close() + pixelarray_result.close() + + def test_close(self): + """does not crash when it is deleted.""" + s = pygame.Surface((10, 10)) a = pygame.PixelArray(s) a.close() del a def test_close_raises(self): - """ when you try to do an operation after it is closed. - """ - s = pygame.Surface((10,10)) + """when you try to do an operation after it is closed.""" + s = pygame.Surface((10, 10)) a = pygame.PixelArray(s) a.close() - def do_operation(): + + def access_after(): a[:] - self.assertRaises (ValueError, do_operation) - def do_operation2(): + self.assertRaises(ValueError, access_after) + + def assign_all_after(): a[:] = 1 - self.assertRaises (ValueError, do_operation2) - def do_operation3(): + self.assertRaises(ValueError, assign_all_after) + + def make_surface_after(): a.make_surface() - self.assertRaises (ValueError, do_operation3) - def do_operation4(): + self.assertRaises(ValueError, make_surface_after) + + def iter_after(): for x in a: pass - self.assertRaises (ValueError, do_operation4) + + self.assertRaises(ValueError, iter_after) + + def close_after(): + a.close() + + self.assertRaises(ValueError, close_after) + + def surface_after(): + a.surface + + self.assertRaises(ValueError, surface_after) + + def itemsize_after(): + a.itemsize + + self.assertRaises(ValueError, itemsize_after) + + def transpose_after(): + a.transpose() + + self.assertRaises(ValueError, transpose_after) def test_context_manager(self): - """ closes properly. - """ - s = pygame.Surface((10,10)) + """closes properly.""" + s = pygame.Surface((10, 10)) with pygame.PixelArray(s) as a: a[:] - def test_pixel_array (self): + # Test pixel array write... will also catch refcount issues and + # segfault + with pygame.PixelArray(s) as a: + a[:] = pygame.Color("deepskyblue") + + def test_pixel_array(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 20), 0, bpp) - sf.fill ((0, 0, 0)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) - self.assertEqual (ar._pixels_address, sf._pixels_address) + self.assertEqual(ar._pixels_address, sf._pixels_address) - if sf.mustlock (): - self.assertTrue (sf.get_locked ()) + if sf.mustlock(): + self.assertTrue(sf.get_locked()) - self.assertEqual (len (ar), 10) + self.assertEqual(len(ar), 10) del ar - if sf.mustlock (): - self.assertFalse (sf.get_locked ()) + if sf.mustlock(): + self.assertFalse(sf.get_locked()) - def test_as_class (self): + def test_as_class(self): # Check general new-style class freatures. - sf = pygame.Surface ((2, 3), 0, 32) - ar = pygame.PixelArray (sf) - self.assertRaises (AttributeError, getattr, ar, 'nonnative') - ar.nonnative = 'value' - self.assertEqual (ar.nonnative, 'value') - r = weakref.ref (ar) - self.assertTrue (r() is ar) + sf = pygame.Surface((2, 3), 0, 32) + ar = pygame.PixelArray(sf) + self.assertRaises(AttributeError, getattr, ar, "nonnative") + ar.nonnative = "value" + self.assertEqual(ar.nonnative, "value") + r = weakref.ref(ar) + self.assertTrue(r() is ar) del ar - gc.collect () - self.assertTrue (r() is None) + gc.collect() + self.assertTrue(r() is None) - class C (pygame.PixelArray): - def __str__ (self): + class C(pygame.PixelArray): + def __str__(self): return "string (%i, %i)" % self.shape - ar = C (sf) - self.assertEqual (str (ar), "string (2, 3)") - r = weakref.ref (ar) - self.assertTrue (r() is ar) + ar = C(sf) + self.assertEqual(str(ar), "string (2, 3)") + r = weakref.ref(ar) + self.assertTrue(r() is ar) del ar - gc.collect () - self.assertTrue (r() is None) + gc.collect() + self.assertTrue(r() is None) def test_pixelarray__subclassed_surface(self): """Ensure the PixelArray constructor accepts subclassed surfaces.""" @@ -153,26 +340,27 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): self.assertIsInstance(pixelarray, pygame.PixelArray) # Sequence interfaces - def test_get_column (self): + def test_get_column(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((6, 8), 0, bpp) - sf.fill ((0, 0, 255)) - val = sf.map_rgb ((0, 0, 255)) - ar = pygame.PixelArray (sf) - - ar2 = ar.__getitem__ (1) - self.assertEqual (len(ar2), 8) - self.assertEqual (ar2.__getitem__ (0), val) - self.assertEqual (ar2.__getitem__ (1), val) - self.assertEqual (ar2.__getitem__ (2), val) - - ar2 = ar.__getitem__ (-1) - self.assertEqual (len(ar2), 8) - self.assertEqual (ar2.__getitem__ (0), val) - self.assertEqual (ar2.__getitem__ (1), val) - self.assertEqual (ar2.__getitem__ (2), val) - - def test_get_pixel (self): + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 255)) + val = sf.map_rgb((0, 0, 255)) + ar = pygame.PixelArray(sf) + + ar2 = ar.__getitem__(1) + self.assertEqual(len(ar2), 8) + self.assertEqual(ar2.__getitem__(0), val) + self.assertEqual(ar2.__getitem__(1), val) + self.assertEqual(ar2.__getitem__(2), val) + + ar2 = ar.__getitem__(-1) + self.assertEqual(len(ar2), 8) + self.assertEqual(ar2.__getitem__(0), val) + self.assertEqual(ar2.__getitem__(1), val) + self.assertEqual(ar2.__getitem__(2), val) + + @unittest.skipIf(IS_PYPY, "pypy malloc abort") + def test_get_pixel(self): w = 10 h = 20 size = w, h @@ -180,155 +368,186 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): fg_color_y = (0, 0, 128) fg_color_x = (0, 0, 11) for bpp in (8, 16, 24, 32): - sf = pygame.Surface (size, 0, bpp) - mapped_bg_color = sf.map_rgb (bg_color) - mapped_fg_color_y = sf.map_rgb (fg_color_y) - mapped_fg_color_x = sf.map_rgb (fg_color_x) - self.assertNotEqual (mapped_fg_color_y, mapped_bg_color, - "Unusable test colors for bpp %i" % (bpp,)) - self.assertNotEqual (mapped_fg_color_x, mapped_bg_color, - "Unusable test colors for bpp %i" % (bpp,)) - self.assertNotEqual (mapped_fg_color_y, mapped_fg_color_x, - "Unusable test colors for bpp %i" % (bpp,)) - sf.fill (bg_color) - - ar = pygame.PixelArray (sf) - - ar_y = ar.__getitem__ (1) - for y in xrange_ (h): - ar2 = ar_y.__getitem__ (y) - self.assertEqual (ar2, mapped_bg_color, - "ar[1][%i] == %i, mapped_bg_color == %i" % - (y, ar2, mapped_bg_color)) - - sf.set_at ((1, y), fg_color_y) - ar2 = ar_y.__getitem__ (y) - self.assertEqual (ar2, mapped_fg_color_y, - "ar[1][%i] == %i, mapped_fg_color_y == %i" % - (y, ar2, mapped_fg_color_y)) - - sf.set_at ((1, 1), bg_color) - for x in xrange_ (w): - ar2 = ar.__getitem__ (x).__getitem__ (1) - self.assertEqual (ar2, mapped_bg_color, - "ar[%i][1] = %i, mapped_bg_color = %i" % - (x, ar2, mapped_bg_color)) - sf.set_at ((x, 1), fg_color_x) - ar2 = ar.__getitem__ (x).__getitem__ (1) - self.assertEqual (ar2, mapped_fg_color_x, - "ar[%i][1] = %i, mapped_fg_color_x = %i" % - (x, ar2, mapped_fg_color_x)) - - ar2 = ar.__getitem__ (0).__getitem__ (0) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (1).__getitem__ (0) - self.assertEqual (ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (-4).__getitem__ (1) - self.assertEqual (ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (-4).__getitem__ (5) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (-4).__getitem__ (0) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (-w + 1).__getitem__ (0) - self.assertEqual (ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (-w).__getitem__ (0) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (5).__getitem__ (-4) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (5).__getitem__ (-h + 1) - self.assertEqual (ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (5).__getitem__ (-h) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (0).__getitem__ (-h + 1) - self.assertEqual (ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) - - ar2 = ar.__getitem__ (0).__getitem__ (-h) - self.assertEqual (ar2, mapped_bg_color, "bpp = %i" % (bpp,)) - - def test_set_pixel (self): + sf = pygame.Surface(size, 0, bpp) + mapped_bg_color = sf.map_rgb(bg_color) + mapped_fg_color_y = sf.map_rgb(fg_color_y) + mapped_fg_color_x = sf.map_rgb(fg_color_x) + self.assertNotEqual( + mapped_fg_color_y, + mapped_bg_color, + "Unusable test colors for bpp %i" % (bpp,), + ) + self.assertNotEqual( + mapped_fg_color_x, + mapped_bg_color, + "Unusable test colors for bpp %i" % (bpp,), + ) + self.assertNotEqual( + mapped_fg_color_y, + mapped_fg_color_x, + "Unusable test colors for bpp %i" % (bpp,), + ) + sf.fill(bg_color) + + ar = pygame.PixelArray(sf) + + ar_y = ar.__getitem__(1) + for y in range(h): + ar2 = ar_y.__getitem__(y) + self.assertEqual( + ar2, + mapped_bg_color, + "ar[1][%i] == %i, mapped_bg_color == %i" + % (y, ar2, mapped_bg_color), + ) + + sf.set_at((1, y), fg_color_y) + ar2 = ar_y.__getitem__(y) + self.assertEqual( + ar2, + mapped_fg_color_y, + "ar[1][%i] == %i, mapped_fg_color_y == %i" + % (y, ar2, mapped_fg_color_y), + ) + + sf.set_at((1, 1), bg_color) + for x in range(w): + ar2 = ar.__getitem__(x).__getitem__(1) + self.assertEqual( + ar2, + mapped_bg_color, + "ar[%i][1] = %i, mapped_bg_color = %i" % (x, ar2, mapped_bg_color), + ) + sf.set_at((x, 1), fg_color_x) + ar2 = ar.__getitem__(x).__getitem__(1) + self.assertEqual( + ar2, + mapped_fg_color_x, + "ar[%i][1] = %i, mapped_fg_color_x = %i" + % (x, ar2, mapped_fg_color_x), + ) + + ar2 = ar.__getitem__(0).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(1).__getitem__(0) + self.assertEqual(ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(5) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-4).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-w + 1).__getitem__(0) + self.assertEqual(ar2, mapped_fg_color_y, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(-w).__getitem__(0) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-4) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-h + 1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(5).__getitem__(-h) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(0).__getitem__(-h + 1) + self.assertEqual(ar2, mapped_fg_color_x, "bpp = %i" % (bpp,)) + + ar2 = ar.__getitem__(0).__getitem__(-h) + self.assertEqual(ar2, mapped_bg_color, "bpp = %i" % (bpp,)) + + def test_set_pixel(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 20), 0, bpp) - sf.fill ((0, 0, 0)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) - ar.__getitem__ (0).__setitem__ (0, (0, 255, 0)) - self.assertEqual (ar[0][0], sf.map_rgb ((0, 255, 0))) + ar.__getitem__(0).__setitem__(0, (0, 255, 0)) + self.assertEqual(ar[0][0], sf.map_rgb((0, 255, 0))) - ar.__getitem__ (1).__setitem__ (1, (128, 128, 128)) - self.assertEqual (ar[1][1], sf.map_rgb ((128, 128, 128))) + ar.__getitem__(1).__setitem__(1, (128, 128, 128)) + self.assertEqual(ar[1][1], sf.map_rgb((128, 128, 128))) - ar.__getitem__(-1).__setitem__ (-1, (128, 128, 128)) - self.assertEqual (ar[9][19], sf.map_rgb ((128, 128, 128))) + ar.__getitem__(-1).__setitem__(-1, (128, 128, 128)) + self.assertEqual(ar[9][19], sf.map_rgb((128, 128, 128))) - ar.__getitem__ (-2).__setitem__ (-2, (128, 128, 128)) - self.assertEqual (ar[8][-2], sf.map_rgb ((128, 128, 128))) + ar.__getitem__(-2).__setitem__(-2, (128, 128, 128)) + self.assertEqual(ar[8][-2], sf.map_rgb((128, 128, 128))) - def test_set_column (self): + def test_set_column(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((6, 8), 0, bpp) - sf.fill ((0, 0, 0)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) - sf2 = pygame.Surface ((6, 8), 0, bpp) - sf2.fill ((0, 255, 255)) - ar2 = pygame.PixelArray (sf2) + sf2 = pygame.Surface((6, 8), 0, bpp) + sf2.fill((0, 255, 255)) + ar2 = pygame.PixelArray(sf2) # Test single value assignment - ar.__setitem__ (2, (128, 128, 128)) - self.assertEqual (ar[2][0], sf.map_rgb ((128, 128, 128))) - self.assertEqual (ar[2][1], sf.map_rgb ((128, 128, 128))) + ar.__setitem__(2, (128, 128, 128)) + self.assertEqual(ar[2][0], sf.map_rgb((128, 128, 128))) + self.assertEqual(ar[2][1], sf.map_rgb((128, 128, 128))) - ar.__setitem__ (-1, (0, 255, 255)) - self.assertEqual (ar[5][0], sf.map_rgb ((0, 255, 255))) - self.assertEqual (ar[-1][1], sf.map_rgb ((0, 255, 255))) + ar.__setitem__(-1, (0, 255, 255)) + self.assertEqual(ar[5][0], sf.map_rgb((0, 255, 255))) + self.assertEqual(ar[-1][1], sf.map_rgb((0, 255, 255))) - ar.__setitem__ (-2, (255, 255, 0)) - self.assertEqual (ar[4][0], sf.map_rgb ((255, 255, 0))) - self.assertEqual (ar[-2][1], sf.map_rgb ((255, 255, 0))) + ar.__setitem__(-2, (255, 255, 0)) + self.assertEqual(ar[4][0], sf.map_rgb((255, 255, 0))) + self.assertEqual(ar[-2][1], sf.map_rgb((255, 255, 0))) # Test list assignment. - ar.__setitem__ (0, [(255, 255, 255)] * 8) - self.assertEqual (ar[0][0], sf.map_rgb ((255, 255, 255))) - self.assertEqual (ar[0][1], sf.map_rgb ((255, 255, 255))) + ar.__setitem__(0, [(255, 255, 255)] * 8) + self.assertEqual(ar[0][0], sf.map_rgb((255, 255, 255))) + self.assertEqual(ar[0][1], sf.map_rgb((255, 255, 255))) # Test tuple assignment. # Changed in Pygame 1.9.2 - Raises an exception. - self.assertRaises (ValueError, ar.__setitem__, 1, - ((204, 0, 204), (17, 17, 17), (204, 0, 204), - (17, 17, 17), (204, 0, 204), (17, 17, 17), - (204, 0, 204), (17, 17, 17))) + self.assertRaises( + ValueError, + ar.__setitem__, + 1, + ( + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + (204, 0, 204), + (17, 17, 17), + ), + ) # Test pixel array assignment. - ar.__setitem__ (1, ar2.__getitem__ (3)) - self.assertEqual (ar[1][0], sf.map_rgb ((0, 255, 255))) - self.assertEqual (ar[1][1], sf.map_rgb ((0, 255, 255))) + ar.__setitem__(1, ar2.__getitem__(3)) + self.assertEqual(ar[1][0], sf.map_rgb((0, 255, 255))) + self.assertEqual(ar[1][1], sf.map_rgb((0, 255, 255))) - def test_get_slice (self): + def test_get_slice(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 20), 0, bpp) - sf.fill ((0, 0, 0)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) - self.assertEqual (len (ar[0:2]), 2) - self.assertEqual (len (ar[3:7][3]), 20) + self.assertEqual(len(ar[0:2]), 2) + self.assertEqual(len(ar[3:7][3]), 20) - self.assertEqual (ar[0:0], None) - self.assertEqual (ar[5:5], None) - self.assertEqual (ar[9:9], None) + self.assertEqual(ar[0:0], None) + self.assertEqual(ar[5:5], None) + self.assertEqual(ar[9:9], None) # Has to resolve to ar[7:8] - self.assertEqual (len (ar[-3:-2]), 1) # 2D - self.assertEqual (len (ar[-3:-2][0]), 20) # 1D + self.assertEqual(len(ar[-3:-2]), 1) # 2D + self.assertEqual(len(ar[-3:-2][0]), 20) # 1D # Try assignments. @@ -337,29 +556,29 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): # 1D assignment ar[3][3:7] = (10, 10, 10) - self.assertEqual (ar[3][5], sf.map_rgb ((10, 10, 10))) - self.assertEqual (ar[3][6], sf.map_rgb ((10, 10, 10))) + self.assertEqual(ar[3][5], sf.map_rgb((10, 10, 10))) + self.assertEqual(ar[3][6], sf.map_rgb((10, 10, 10))) - @unittest.skipIf(IS_PYPY, 'skipping for PyPy (segfaults on mac pypy3 6.0.0)') - def test_contains (self): + @unittest.skipIf(IS_PYPY, "skipping for PyPy (segfaults on mac pypy3 6.0.0)") + def test_contains(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 20), 0, bpp) - sf.fill ((0, 0, 0)) - sf.set_at ((8, 8), (255, 255, 255)) + sf = pygame.Surface((10, 20), 0, bpp) + sf.fill((0, 0, 0)) + sf.set_at((8, 8), (255, 255, 255)) - ar = pygame.PixelArray (sf) - self.assertTrue ((0, 0, 0) in ar) - self.assertTrue ((255, 255, 255) in ar) - self.assertFalse ((255, 255, 0) in ar) - self.assertFalse (0x0000ff in ar) + ar = pygame.PixelArray(sf) + self.assertTrue((0, 0, 0) in ar) + self.assertTrue((255, 255, 255) in ar) + self.assertFalse((255, 255, 0) in ar) + self.assertFalse(0x0000FF in ar) # Test sliced array - self.assertTrue ((0, 0, 0) in ar[8]) - self.assertTrue ((255, 255, 255) in ar[8]) - self.assertFalse ((255, 255, 0) in ar[8]) - self.assertFalse (0x0000ff in ar[8]) + self.assertTrue((0, 0, 0) in ar[8]) + self.assertTrue((255, 255, 255) in ar[8]) + self.assertFalse((255, 255, 0) in ar[8]) + self.assertFalse(0x0000FF in ar[8]) - def test_get_surface (self): + def test_get_surface(self): for bpp in (8, 16, 24, 32): sf = pygame.Surface((10, 20), 0, bpp) sf.fill((0, 0, 0)) @@ -377,126 +596,139 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): self.assertIsInstance(surface, pygame.Surface) self.assertIsInstance(surface, SurfaceSubclass) - def test_set_slice (self): + def test_set_slice(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((6, 8), 0, bpp) - sf.fill ((0, 0, 0)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((0, 0, 0)) + ar = pygame.PixelArray(sf) # Test single value assignment - val = sf.map_rgb ((128, 128, 128)) + val = sf.map_rgb((128, 128, 128)) ar[0:2] = val - self.assertEqual (ar[0][0], val) - self.assertEqual (ar[0][1], val) - self.assertEqual (ar[1][0], val) - self.assertEqual (ar[1][1], val) + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[0][1], val) + self.assertEqual(ar[1][0], val) + self.assertEqual(ar[1][1], val) - val = sf.map_rgb ((0, 255, 255)) + val = sf.map_rgb((0, 255, 255)) ar[-3:-1] = val - self.assertEqual (ar[3][0], val) - self.assertEqual (ar[-2][1], val) + self.assertEqual(ar[3][0], val) + self.assertEqual(ar[-2][1], val) - val = sf.map_rgb ((255, 255, 255)) + val = sf.map_rgb((255, 255, 255)) ar[-3:] = (255, 255, 255) - self.assertEqual (ar[4][0], val) - self.assertEqual (ar[-1][1], val) + self.assertEqual(ar[4][0], val) + self.assertEqual(ar[-1][1], val) # Test array size mismatch. # Changed in ver. 1.9.2 # (was "Test list assignment, this is a vertical assignment.") - val = sf.map_rgb ((0, 255, 0)) - self.assertRaises (ValueError, ar.__setitem__, slice (2, 4), - [val] * 8) + val = sf.map_rgb((0, 255, 0)) + self.assertRaises(ValueError, ar.__setitem__, slice(2, 4), [val] * 8) # And the horizontal assignment. - val = sf.map_rgb ((255, 0, 0)) - val2 = sf.map_rgb ((128, 0, 255)) + val = sf.map_rgb((255, 0, 0)) + val2 = sf.map_rgb((128, 0, 255)) ar[0:2] = [val, val2] - self.assertEqual (ar[0][0], val) - self.assertEqual (ar[1][0], val2) - self.assertEqual (ar[0][1], val) - self.assertEqual (ar[1][1], val2) - self.assertEqual (ar[0][4], val) - self.assertEqual (ar[1][4], val2) - self.assertEqual (ar[0][5], val) - self.assertEqual (ar[1][5], val2) + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[1][0], val2) + self.assertEqual(ar[0][1], val) + self.assertEqual(ar[1][1], val2) + self.assertEqual(ar[0][4], val) + self.assertEqual(ar[1][4], val2) + self.assertEqual(ar[0][5], val) + self.assertEqual(ar[1][5], val2) # Test pixelarray assignment. ar[:] = (0, 0, 0) - sf2 = pygame.Surface ((6, 8), 0, bpp) - sf2.fill ((255, 0, 255)) + sf2 = pygame.Surface((6, 8), 0, bpp) + sf2.fill((255, 0, 255)) - val = sf.map_rgb ((255, 0, 255)) - ar2 = pygame.PixelArray (sf2) + val = sf.map_rgb((255, 0, 255)) + ar2 = pygame.PixelArray(sf2) ar[:] = ar2[:] - self.assertEqual (ar[0][0], val) - self.assertEqual (ar[5][7], val) + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[5][7], val) # Ensure p1 ... pn are freed for array[...] = [p1, ..., pn] # Bug fix: reference counting. - if hasattr(sys, 'getrefcount'): + if hasattr(sys, "getrefcount"): + class Int(int): """Unique int instances""" + pass - sf = pygame.Surface ((5, 2), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((5, 2), 0, 32) + ar = pygame.PixelArray(sf) pixel_list = [Int(i) for i in range(ar.shape[0])] - refcnts_before = [sys.getrefcount (i) for i in pixel_list] + refcnts_before = [sys.getrefcount(i) for i in pixel_list] ar[...] = pixel_list - refcnts_after = [sys.getrefcount (i) for i in pixel_list] - gc.collect () - self.assertEqual (refcnts_after, refcnts_before) + refcnts_after = [sys.getrefcount(i) for i in pixel_list] + gc.collect() + self.assertEqual(refcnts_after, refcnts_before) - def test_subscript (self): + def test_subscript(self): # By default we do not need to work with any special __***__ # methods as map subscripts are the first looked up by the # object system. for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((6, 8), 0, bpp) - sf.set_at ((1, 3), (0, 255, 0)) - sf.set_at ((0, 0), (0, 255, 0)) - sf.set_at ((4, 4), (0, 255, 0)) - val = sf.map_rgb ((0, 255, 0)) + sf = pygame.Surface((6, 8), 0, bpp) + sf.set_at((1, 3), (0, 255, 0)) + sf.set_at((0, 0), (0, 255, 0)) + sf.set_at((4, 4), (0, 255, 0)) + val = sf.map_rgb((0, 255, 0)) - ar = pygame.PixelArray (sf) + ar = pygame.PixelArray(sf) # Test single value requests. - self.assertEqual (ar[1,3], val) - self.assertEqual (ar[0,0], val) - self.assertEqual (ar[4,4], val) - self.assertEqual (ar[1][3], val) - self.assertEqual (ar[0][0], val) - self.assertEqual (ar[4][4], val) + self.assertEqual(ar[1, 3], val) + self.assertEqual(ar[0, 0], val) + self.assertEqual(ar[4, 4], val) + self.assertEqual(ar[1][3], val) + self.assertEqual(ar[0][0], val) + self.assertEqual(ar[4][4], val) # Test ellipse working. - self.assertEqual (len (ar[...,...]), 6) - self.assertEqual (len (ar[1,...]), 8) - self.assertEqual (len (ar[...,3]), 6) + self.assertEqual(len(ar[..., ...]), 6) + self.assertEqual(len(ar[1, ...]), 8) + self.assertEqual(len(ar[..., 3]), 6) # Test simple slicing - self.assertEqual (len (ar[:,:]), 6) - self.assertEqual (len (ar[:,]), 6) - self.assertEqual (len (ar[1,:]), 8) - self.assertEqual (len (ar[:,2]), 6) + self.assertEqual(len(ar[:, :]), 6) + self.assertEqual( + len(ar[:,]), + 6, + ) + self.assertEqual(len(ar[1, :]), 8) + self.assertEqual(len(ar[:, 2]), 6) # Empty slices - self.assertEqual (ar[4:4,], None) - self.assertEqual (ar[4:4,...], None) - self.assertEqual (ar[4:4,2:2], None) - self.assertEqual (ar[4:4,1:4], None) - self.assertEqual (ar[4:4:2,], None) - self.assertEqual (ar[4:4:-2,], None) - self.assertEqual (ar[4:4:1,...], None) - self.assertEqual (ar[4:4:-1,...], None) - self.assertEqual (ar[4:4:1,2:2], None) - self.assertEqual (ar[4:4:-1,1:4], None) - self.assertEqual (ar[...,4:4], None) - self.assertEqual (ar[1:4,4:4], None) - self.assertEqual (ar[...,4:4:1], None) - self.assertEqual (ar[...,4:4:-1], None) - self.assertEqual (ar[2:2,4:4:1], None) - self.assertEqual (ar[1:4,4:4:-1], None) + self.assertEqual( + ar[4:4,], + None, + ) + self.assertEqual(ar[4:4, ...], None) + self.assertEqual(ar[4:4, 2:2], None) + self.assertEqual(ar[4:4, 1:4], None) + self.assertEqual( + ar[4:4:2,], + None, + ) + self.assertEqual( + ar[4:4:-2,], + None, + ) + self.assertEqual(ar[4:4:1, ...], None) + self.assertEqual(ar[4:4:-1, ...], None) + self.assertEqual(ar[4:4:1, 2:2], None) + self.assertEqual(ar[4:4:-1, 1:4], None) + self.assertEqual(ar[..., 4:4], None) + self.assertEqual(ar[1:4, 4:4], None) + self.assertEqual(ar[..., 4:4:1], None) + self.assertEqual(ar[..., 4:4:-1], None) + self.assertEqual(ar[2:2, 4:4:1], None) + self.assertEqual(ar[1:4, 4:4:-1], None) # Test advanced slicing ar[0] = 0 @@ -507,80 +739,80 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): ar[5] = 5 # We should receive something like [0,2,4] - self.assertEqual (ar[::2,1][0], 0) - self.assertEqual (ar[::2,1][1], 2) - self.assertEqual (ar[::2,1][2], 4) + self.assertEqual(ar[::2, 1][0], 0) + self.assertEqual(ar[::2, 1][1], 2) + self.assertEqual(ar[::2, 1][2], 4) # We should receive something like [2,2,2] - self.assertEqual (ar[2,::2][0], 2) - self.assertEqual (ar[2,::2][1], 2) - self.assertEqual (ar[2,::2][2], 2) + self.assertEqual(ar[2, ::2][0], 2) + self.assertEqual(ar[2, ::2][1], 2) + self.assertEqual(ar[2, ::2][2], 2) # Should create a 3x3 array of [0,2,4] - ar2 = ar[::2,::2] - self.assertEqual (len (ar2), 3) - self.assertEqual (ar2[0][0], 0) - self.assertEqual (ar2[0][1], 0) - self.assertEqual (ar2[0][2], 0) - self.assertEqual (ar2[2][0], 4) - self.assertEqual (ar2[2][1], 4) - self.assertEqual (ar2[2][2], 4) - self.assertEqual (ar2[1][0], 2) - self.assertEqual (ar2[2][0], 4) - self.assertEqual (ar2[1][1], 2) + ar2 = ar[::2, ::2] + self.assertEqual(len(ar2), 3) + self.assertEqual(ar2[0][0], 0) + self.assertEqual(ar2[0][1], 0) + self.assertEqual(ar2[0][2], 0) + self.assertEqual(ar2[2][0], 4) + self.assertEqual(ar2[2][1], 4) + self.assertEqual(ar2[2][2], 4) + self.assertEqual(ar2[1][0], 2) + self.assertEqual(ar2[2][0], 4) + self.assertEqual(ar2[1][1], 2) # Should create a reversed 3x8 array over X of [1,2,3] -> [3,2,1] ar2 = ar[3:0:-1] - self.assertEqual (len (ar2), 3) - self.assertEqual (ar2[0][0], 3) - self.assertEqual (ar2[0][1], 3) - self.assertEqual (ar2[0][2], 3) - self.assertEqual (ar2[0][7], 3) - self.assertEqual (ar2[2][0], 1) - self.assertEqual (ar2[2][1], 1) - self.assertEqual (ar2[2][2], 1) - self.assertEqual (ar2[2][7], 1) - self.assertEqual (ar2[1][0], 2) - self.assertEqual (ar2[1][1], 2) + self.assertEqual(len(ar2), 3) + self.assertEqual(ar2[0][0], 3) + self.assertEqual(ar2[0][1], 3) + self.assertEqual(ar2[0][2], 3) + self.assertEqual(ar2[0][7], 3) + self.assertEqual(ar2[2][0], 1) + self.assertEqual(ar2[2][1], 1) + self.assertEqual(ar2[2][2], 1) + self.assertEqual(ar2[2][7], 1) + self.assertEqual(ar2[1][0], 2) + self.assertEqual(ar2[1][1], 2) # Should completely reverse the array over X -> [5,4,3,2,1,0] ar2 = ar[::-1] - self.assertEqual (len (ar2), 6) - self.assertEqual (ar2[0][0], 5) - self.assertEqual (ar2[0][1], 5) - self.assertEqual (ar2[0][3], 5) - self.assertEqual (ar2[0][-1], 5) - self.assertEqual (ar2[1][0], 4) - self.assertEqual (ar2[1][1], 4) - self.assertEqual (ar2[1][3], 4) - self.assertEqual (ar2[1][-1], 4) - self.assertEqual (ar2[-1][-1], 0) - self.assertEqual (ar2[-2][-2], 1) - self.assertEqual (ar2[-3][-1], 2) + self.assertEqual(len(ar2), 6) + self.assertEqual(ar2[0][0], 5) + self.assertEqual(ar2[0][1], 5) + self.assertEqual(ar2[0][3], 5) + self.assertEqual(ar2[0][-1], 5) + self.assertEqual(ar2[1][0], 4) + self.assertEqual(ar2[1][1], 4) + self.assertEqual(ar2[1][3], 4) + self.assertEqual(ar2[1][-1], 4) + self.assertEqual(ar2[-1][-1], 0) + self.assertEqual(ar2[-2][-2], 1) + self.assertEqual(ar2[-3][-1], 2) # Test advanced slicing ar[:] = 0 - ar2 = ar[:,1] + ar2 = ar[:, 1] ar2[:] = [99] * len(ar2) - self.assertEqual (ar2[0], 99) - self.assertEqual (ar2[-1], 99) - self.assertEqual (ar2[-2], 99) - self.assertEqual (ar2[2], 99) - self.assertEqual (ar[0,1], 99) - self.assertEqual (ar[1,1], 99) - self.assertEqual (ar[2,1], 99) - self.assertEqual (ar[-1,1], 99) - self.assertEqual (ar[-2,1], 99) + self.assertEqual(ar2[0], 99) + self.assertEqual(ar2[-1], 99) + self.assertEqual(ar2[-2], 99) + self.assertEqual(ar2[2], 99) + self.assertEqual(ar[0, 1], 99) + self.assertEqual(ar[1, 1], 99) + self.assertEqual(ar[2, 1], 99) + self.assertEqual(ar[-1, 1], 99) + self.assertEqual(ar[-2, 1], 99) # Cases where a 2d array should have a dimension of length 1. - ar2 = ar[1:2,:] - self.assertEqual (ar2.shape, (1, ar.shape[1])) - ar2 = ar[:,1:2] - self.assertEqual (ar2.shape, (ar.shape[0], 1)) - sf2 = pygame.Surface ((1, 5), 0, 32) - ar2 = pygame.PixelArray (sf2) - self.assertEqual (ar2.shape, sf2.get_size ()) - sf2 = pygame.Surface ((7, 1), 0, 32) - ar2 = pygame.PixelArray (sf2) - self.assertEqual (ar2.shape, sf2.get_size ()) + ar2 = ar[1:2, :] + self.assertEqual(ar2.shape, (1, ar.shape[1])) + ar2 = ar[:, 1:2] + self.assertEqual(ar2.shape, (ar.shape[0], 1)) + sf2 = pygame.Surface((1, 5), 0, 32) + ar2 = pygame.PixelArray(sf2) + self.assertEqual(ar2.shape, sf2.get_size()) + sf2 = pygame.Surface((7, 1), 0, 32) + ar2 = pygame.PixelArray(sf2) + self.assertEqual(ar2.shape, sf2.get_size()) # Array has a single ellipsis subscript: the identity operator ar2 = ar[...] @@ -588,134 +820,161 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): # Ensure x and y are freed for p = array[x, y] # Bug fix: reference counting - if hasattr(sys, 'getrefcount'): + if hasattr(sys, "getrefcount"): + class Int(int): """Unique int instances""" + pass - sf = pygame.Surface ((2, 2), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) x, y = Int(0), Int(1) - rx_before, ry_before = sys.getrefcount (x), sys.getrefcount (y) + rx_before, ry_before = sys.getrefcount(x), sys.getrefcount(y) p = ar[x, y] - rx_after, ry_after = sys.getrefcount (x), sys.getrefcount (y) - self.assertEqual (rx_after, rx_before) - self.assertEqual (ry_after, ry_before) + rx_after, ry_after = sys.getrefcount(x), sys.getrefcount(y) + self.assertEqual(rx_after, rx_before) + self.assertEqual(ry_after, ry_before) - def test_ass_subscript (self): + def test_ass_subscript(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((6, 8), 0, bpp) - sf.fill ((255, 255, 255)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((6, 8), 0, bpp) + sf.fill((255, 255, 255)) + ar = pygame.PixelArray(sf) # Test ellipse working - ar[...,...] = (0, 0, 0) - self.assertEqual (ar[0,0], 0) - self.assertEqual (ar[1,0], 0) - self.assertEqual (ar[-1,-1], 0) + ar[..., ...] = (0, 0, 0) + self.assertEqual(ar[0, 0], 0) + self.assertEqual(ar[1, 0], 0) + self.assertEqual(ar[-1, -1], 0) ar[...,] = (0, 0, 255) - self.assertEqual (ar[0,0], sf.map_rgb ((0, 0, 255))) - self.assertEqual (ar[1,0], sf.map_rgb ((0, 0, 255))) - self.assertEqual (ar[-1,-1], sf.map_rgb ((0, 0, 255))) - ar[:,...] = (255, 0, 0) - self.assertEqual (ar[0,0], sf.map_rgb ((255, 0, 0))) - self.assertEqual (ar[1,0], sf.map_rgb ((255, 0, 0))) - self.assertEqual (ar[-1,-1], sf.map_rgb ((255, 0, 0))) + self.assertEqual(ar[0, 0], sf.map_rgb((0, 0, 255))) + self.assertEqual(ar[1, 0], sf.map_rgb((0, 0, 255))) + self.assertEqual(ar[-1, -1], sf.map_rgb((0, 0, 255))) + ar[:, ...] = (255, 0, 0) + self.assertEqual(ar[0, 0], sf.map_rgb((255, 0, 0))) + self.assertEqual(ar[1, 0], sf.map_rgb((255, 0, 0))) + self.assertEqual(ar[-1, -1], sf.map_rgb((255, 0, 0))) ar[...] = (0, 255, 0) - self.assertEqual (ar[0,0], sf.map_rgb ((0, 255, 0))) - self.assertEqual (ar[1,0], sf.map_rgb ((0, 255, 0))) - self.assertEqual (ar[-1,-1], sf.map_rgb ((0, 255, 0))) + self.assertEqual(ar[0, 0], sf.map_rgb((0, 255, 0))) + self.assertEqual(ar[1, 0], sf.map_rgb((0, 255, 0))) + self.assertEqual(ar[-1, -1], sf.map_rgb((0, 255, 0))) # Ensure x and y are freed for array[x, y] = p # Bug fix: reference counting - if hasattr(sys, 'getrefcount'): + if hasattr(sys, "getrefcount"): + class Int(int): """Unique int instances""" + pass - sf = pygame.Surface ((2, 2), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) x, y = Int(0), Int(1) - rx_before, ry_before = sys.getrefcount (x), sys.getrefcount (y) + rx_before, ry_before = sys.getrefcount(x), sys.getrefcount(y) ar[x, y] = 0 - rx_after, ry_after = sys.getrefcount (x), sys.getrefcount (y) - self.assertEqual (rx_after, rx_before) - self.assertEqual (ry_after, ry_before) + rx_after, ry_after = sys.getrefcount(x), sys.getrefcount(y) + self.assertEqual(rx_after, rx_before) + self.assertEqual(ry_after, ry_before) def test_pixels_field(self): for bpp in [1, 2, 3, 4]: - sf = pygame.Surface ((11, 7), 0, bpp * 8) - ar = pygame.PixelArray (sf) - ar2 = ar[1:,:] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - ar.itemsize) - ar2 = ar[:,1:] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - ar.strides[1]) - ar2 = ar[::-1,:] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - (ar.shape[0] - 1) * ar.itemsize) - ar2 = ar[::-2,:] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - (ar.shape[0] - 1) * ar.itemsize) - ar2 = ar[:,::-1] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - (ar.shape[1] - 1) * ar.strides[1]) - ar3 = ar2[::-1,:] - self.assertEqual (ar3._pixels_address - ar._pixels_address, - (ar.shape[0] - 1) * ar.strides[0] + - (ar.shape[1] - 1) * ar.strides[1]) - ar2 = ar[:,::-2] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - (ar.shape[1] - 1) * ar.strides[1]) - ar2 = ar[2::,3::] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - ar.strides[0] * 2 + ar.strides[1] * 3) - ar2 = ar[2::2,3::4] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - ar.strides[0] * 2 + ar.strides[1] * 3) - ar2 = ar[9:2:-1,:] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - ar.strides[0] * 9) - ar2 = ar[:,5:2:-1] - self.assertEqual (ar2._pixels_address - ar._pixels_address, - ar.strides[1] * 5) + sf = pygame.Surface((11, 7), 0, bpp * 8) + ar = pygame.PixelArray(sf) + ar2 = ar[1:, :] + self.assertEqual(ar2._pixels_address - ar._pixels_address, ar.itemsize) + ar2 = ar[:, 1:] + self.assertEqual(ar2._pixels_address - ar._pixels_address, ar.strides[1]) + ar2 = ar[::-1, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.itemsize, + ) + ar2 = ar[::-2, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.itemsize, + ) + ar2 = ar[:, ::-1] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[1] - 1) * ar.strides[1], + ) + ar3 = ar2[::-1, :] + self.assertEqual( + ar3._pixels_address - ar._pixels_address, + (ar.shape[0] - 1) * ar.strides[0] + (ar.shape[1] - 1) * ar.strides[1], + ) + ar2 = ar[:, ::-2] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + (ar.shape[1] - 1) * ar.strides[1], + ) + ar2 = ar[2::, 3::] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + ar.strides[0] * 2 + ar.strides[1] * 3, + ) + ar2 = ar[2::2, 3::4] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, + ar.strides[0] * 2 + ar.strides[1] * 3, + ) + ar2 = ar[9:2:-1, :] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, ar.strides[0] * 9 + ) + ar2 = ar[:, 5:2:-1] + self.assertEqual( + ar2._pixels_address - ar._pixels_address, ar.strides[1] * 5 + ) ##? ar2 = ar[:,9:2:-1] - def test_make_surface (self): - bg_color = pygame.Color (255, 255, 255) - fg_color = pygame.Color (128, 100, 0) + def test_make_surface(self): + bg_color = pygame.Color(255, 255, 255) + fg_color = pygame.Color(128, 100, 0) for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 20), 0, bpp) - bg_color_adj = sf.unmap_rgb (sf.map_rgb (bg_color)) - fg_color_adj = sf.unmap_rgb (sf.map_rgb (fg_color)) - sf.fill (bg_color_adj) - sf.fill (fg_color_adj, (2, 5, 4, 11)) - ar = pygame.PixelArray (sf) - newsf = ar[::2,::2].make_surface () - rect = newsf.get_rect () - self.assertEqual (rect.width, 5) - self.assertEqual (rect.height, 10) - for p in [(0, 2), (0, 3), (1, 2), - (2, 2), (3, 2), (3, 3), - (0, 7), (0, 8), (1, 8), - (2, 8), (3, 8), (3, 7)]: - self.assertEqual (newsf.get_at (p), bg_color_adj) + sf = pygame.Surface((10, 20), 0, bpp) + bg_color_adj = sf.unmap_rgb(sf.map_rgb(bg_color)) + fg_color_adj = sf.unmap_rgb(sf.map_rgb(fg_color)) + sf.fill(bg_color_adj) + sf.fill(fg_color_adj, (2, 5, 4, 11)) + ar = pygame.PixelArray(sf) + newsf = ar[::2, ::2].make_surface() + rect = newsf.get_rect() + self.assertEqual(rect.width, 5) + self.assertEqual(rect.height, 10) + for p in [ + (0, 2), + (0, 3), + (1, 2), + (2, 2), + (3, 2), + (3, 3), + (0, 7), + (0, 8), + (1, 8), + (2, 8), + (3, 8), + (3, 7), + ]: + self.assertEqual(newsf.get_at(p), bg_color_adj) for p in [(1, 3), (2, 3), (1, 5), (2, 5), (1, 7), (2, 7)]: - self.assertEqual (newsf.get_at (p), fg_color_adj) + self.assertEqual(newsf.get_at(p), fg_color_adj) # Bug when array width is not a multiple of the slice step. w = 17 lst = list(range(w)) w_slice = len(lst[::2]) h = 3 - sf = pygame.Surface ((w, h), 0, 32) - ar = pygame.PixelArray (sf) - ar2 = ar[::2,:] - sf2 = ar2.make_surface () - w2, h2 = sf2.get_size () - self.assertEqual (w2, w_slice) - self.assertEqual (h2, h) + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + ar2 = ar[::2, :] + sf2 = ar2.make_surface() + w2, h2 = sf2.get_size() + self.assertEqual(w2, w_slice) + self.assertEqual(h2, h) # Bug when array height is not a multiple of the slice step. # This can hang the Python interpreter. @@ -723,21 +982,22 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): lst = list(range(h)) h_slice = len(lst[::2]) w = 3 - sf = pygame.Surface ((w, h), 0, 32) - ar = pygame.PixelArray (sf) - ar2 = ar[:,::2] - sf2 = ar2.make_surface () # Hangs here. - w2, h2 = sf2.get_size () - self.assertEqual (w2, w) - self.assertEqual (h2, h_slice) + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) + ar2 = ar[:, ::2] + sf2 = ar2.make_surface() # Hangs here. + w2, h2 = sf2.get_size() + self.assertEqual(w2, w) + self.assertEqual(h2, h_slice) def test_make_surface__subclassed_surface(self): """Ensure make_surface can handle subclassed surfaces.""" expected_size = (3, 5) expected_flags = 0 expected_depth = 32 - original_surface = SurfaceSubclass(expected_size, expected_flags, - expected_depth) + original_surface = SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) pixelarray = pygame.PixelArray(original_surface) surface = pixelarray.make_surface() @@ -749,121 +1009,129 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): self.assertEqual(surface.get_flags(), expected_flags) self.assertEqual(surface.get_bitsize(), expected_depth) - def test_iter (self): + def test_iter(self): for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((5, 10), 0, bpp) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((5, 10), 0, bpp) + ar = pygame.PixelArray(sf) iterations = 0 for col in ar: - self.assertEqual (len (col), 10) + self.assertEqual(len(col), 10) iterations += 1 - self.assertEqual (iterations, 5) + self.assertEqual(iterations, 5) - def test_replace (self): - #print "replace start" + def test_replace(self): + # print("replace start") for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 10), 0, bpp) - sf.fill ((255, 0, 0)) - rval = sf.map_rgb ((0, 0, 255)) - oval = sf.map_rgb ((255, 0, 0)) - ar = pygame.PixelArray (sf) - ar[::2].replace ((255, 0, 0), (0, 0, 255)) - self.assertEqual (ar[0][0], rval) - self.assertEqual (ar[1][0], oval) - self.assertEqual (ar[2][3], rval) - self.assertEqual (ar[3][6], oval) - self.assertEqual (ar[8][9], rval) - self.assertEqual (ar[9][9], oval) - - ar[::2].replace ((0, 0, 255), (255, 0, 0), weights=(10, 20, 50)) - self.assertEqual (ar[0][0], oval) - self.assertEqual (ar[2][3], oval) - self.assertEqual (ar[3][6], oval) - self.assertEqual (ar[8][9], oval) - self.assertEqual (ar[9][9], oval) - #print "replace end" - - def test_extract (self): - #print "extract start" + sf = pygame.Surface((10, 10), 0, bpp) + sf.fill((255, 0, 0)) + rval = sf.map_rgb((0, 0, 255)) + oval = sf.map_rgb((255, 0, 0)) + ar = pygame.PixelArray(sf) + ar[::2].replace((255, 0, 0), (0, 0, 255)) + self.assertEqual(ar[0][0], rval) + self.assertEqual(ar[1][0], oval) + self.assertEqual(ar[2][3], rval) + self.assertEqual(ar[3][6], oval) + self.assertEqual(ar[8][9], rval) + self.assertEqual(ar[9][9], oval) + + ar[::2].replace((0, 0, 255), (255, 0, 0), weights=(10, 20, 50)) + self.assertEqual(ar[0][0], oval) + self.assertEqual(ar[2][3], oval) + self.assertEqual(ar[3][6], oval) + self.assertEqual(ar[8][9], oval) + self.assertEqual(ar[9][9], oval) + # print("replace end") + + def test_extract(self): + # print("extract start") for bpp in (8, 16, 24, 32): - sf = pygame.Surface ((10, 10), 0, bpp) - sf.fill ((0, 0, 255)) - sf.fill ((255, 0, 0), (2, 2, 6, 6)) - - white = sf.map_rgb ((255, 255, 255)) - black = sf.map_rgb ((0, 0, 0)) - - ar = pygame.PixelArray (sf) - newar = ar.extract ((255, 0, 0)) - - self.assertEqual (newar[0][0], black) - self.assertEqual (newar[1][0], black) - self.assertEqual (newar[2][3], white) - self.assertEqual (newar[3][6], white) - self.assertEqual (newar[8][9], black) - self.assertEqual (newar[9][9], black) - - newar = ar.extract ((255, 0, 0), weights=(10, 0.1, 50)) - self.assertEqual (newar[0][0], black) - self.assertEqual (newar[1][0], black) - self.assertEqual (newar[2][3], white) - self.assertEqual (newar[3][6], white) - self.assertEqual (newar[8][9], black) - self.assertEqual (newar[9][9], black) - #print "extract end" - - def test_2dslice_assignment (self): + sf = pygame.Surface((10, 10), 0, bpp) + sf.fill((0, 0, 255)) + sf.fill((255, 0, 0), (2, 2, 6, 6)) + + white = sf.map_rgb((255, 255, 255)) + black = sf.map_rgb((0, 0, 0)) + + ar = pygame.PixelArray(sf) + newar = ar.extract((255, 0, 0)) + + self.assertEqual(newar[0][0], black) + self.assertEqual(newar[1][0], black) + self.assertEqual(newar[2][3], white) + self.assertEqual(newar[3][6], white) + self.assertEqual(newar[8][9], black) + self.assertEqual(newar[9][9], black) + + newar = ar.extract((255, 0, 0), weights=(10, 0.1, 50)) + self.assertEqual(newar[0][0], black) + self.assertEqual(newar[1][0], black) + self.assertEqual(newar[2][3], white) + self.assertEqual(newar[3][6], white) + self.assertEqual(newar[8][9], black) + self.assertEqual(newar[9][9], black) + # print("extract end") + + def test_2dslice_assignment(self): w = 2 * 5 * 8 h = 3 * 5 * 9 - sf = pygame.Surface ((w, h), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((w, h), 0, 32) + ar = pygame.PixelArray(sf) size = (w, h) strides = (1, w) offset = 0 - self._test_assignment (sf, ar, size, strides, offset) - xslice = slice (None, None, 2) - yslice = slice (None, None, 3) - ar, size, strides, offset = self._array_slice ( - ar, size, (xslice, yslice), strides, offset) - self._test_assignment (sf, ar, size, strides, offset) - xslice = slice (5, None, 5) - yslice = slice (5, None, 5) - ar, size, strides, offset = self._array_slice ( - ar, size, (xslice, yslice), strides, offset) - self._test_assignment (sf, ar, size, strides, offset) - - def _test_assignment (self, sf, ar, ar_size, ar_strides, ar_offset): - self.assertEqual (ar.shape, ar_size) + self._test_assignment(sf, ar, size, strides, offset) + xslice = slice(None, None, 2) + yslice = slice(None, None, 3) + ar, size, strides, offset = self._array_slice( + ar, size, (xslice, yslice), strides, offset + ) + self._test_assignment(sf, ar, size, strides, offset) + xslice = slice(5, None, 5) + yslice = slice(5, None, 5) + ar, size, strides, offset = self._array_slice( + ar, size, (xslice, yslice), strides, offset + ) + self._test_assignment(sf, ar, size, strides, offset) + + def _test_assignment(self, sf, ar, ar_size, ar_strides, ar_offset): + self.assertEqual(ar.shape, ar_size) ar_w, ar_h = ar_size ar_xstride, ar_ystride = ar_strides - sf_w, sf_h = sf.get_size () - black = pygame.Color ('black') - color = pygame.Color (0, 0, 12) - pxcolor = sf.map_rgb (color) - sf.fill (black) - for ar_x, ar_y in [(0, 0), - (0, ar_h - 4), - (ar_w - 3, 0), - (0, ar_h - 1), - (ar_w - 1, 0), - (ar_w - 1, ar_h - 1)]: + sf_w, sf_h = sf.get_size() + black = pygame.Color("black") + color = pygame.Color(0, 0, 12) + pxcolor = sf.map_rgb(color) + sf.fill(black) + for ar_x, ar_y in [ + (0, 0), + (0, ar_h - 4), + (ar_w - 3, 0), + (0, ar_h - 1), + (ar_w - 1, 0), + (ar_w - 1, ar_h - 1), + ]: sf_offset = ar_offset + ar_x * ar_xstride + ar_y * ar_ystride sf_y = sf_offset // sf_w sf_x = sf_offset - sf_y * sf_w sf_posn = (sf_x, sf_y) - sf_pix = sf.get_at (sf_posn) - self.assertEqual (sf_pix, black, - "at pixarr posn (%i, %i) (surf posn (%i, %i)): " - "%s != %s" % - (ar_x, ar_y, sf_x, sf_y, sf_pix, black)) + sf_pix = sf.get_at(sf_posn) + self.assertEqual( + sf_pix, + black, + "at pixarr posn (%i, %i) (surf posn (%i, %i)): " + "%s != %s" % (ar_x, ar_y, sf_x, sf_y, sf_pix, black), + ) ar[ar_x, ar_y] = pxcolor - sf_pix = sf.get_at (sf_posn) - self.assertEqual (sf_pix, color, - "at pixarr posn (%i, %i) (surf posn (%i, %i)): " - "%s != %s" % - (ar_x, ar_y, sf_x, sf_y, sf_pix, color)) - - def _array_slice (self, ar, size, slices, strides, offset): + sf_pix = sf.get_at(sf_posn) + self.assertEqual( + sf_pix, + color, + "at pixarr posn (%i, %i) (surf posn (%i, %i)): " + "%s != %s" % (ar_x, ar_y, sf_x, sf_y, sf_pix, color), + ) + + def _array_slice(self, ar, size, slices, strides, offset): ar = ar[slices] xslice, yslice = slices w, h = size @@ -880,37 +1148,37 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): def test_array_properties(self): # itemsize, ndim, shape, and strides. for bpp in [1, 2, 3, 4]: - sf = pygame.Surface ((2, 2), 0, bpp * 8) - ar = pygame.PixelArray (sf) - self.assertEqual (ar.itemsize, bpp) + sf = pygame.Surface((2, 2), 0, bpp * 8) + ar = pygame.PixelArray(sf) + self.assertEqual(ar.itemsize, bpp) for shape in [(4, 16), (5, 13)]: w, h = shape - sf = pygame.Surface (shape, 0, 32) - bpp = sf.get_bytesize () - pitch = sf.get_pitch () - ar = pygame.PixelArray (sf) - self.assertEqual (ar.ndim, 2) - self.assertEqual (ar.shape, shape) - self.assertEqual (ar.strides, (bpp, pitch)) - ar2 = ar[::2,:] + sf = pygame.Surface(shape, 0, 32) + bpp = sf.get_bytesize() + pitch = sf.get_pitch() + ar = pygame.PixelArray(sf) + self.assertEqual(ar.ndim, 2) + self.assertEqual(ar.shape, shape) + self.assertEqual(ar.strides, (bpp, pitch)) + ar2 = ar[::2, :] w2 = len(([0] * w)[::2]) - self.assertEqual (ar2.ndim, 2) - self.assertEqual (ar2.shape, (w2, h)) - self.assertEqual (ar2.strides, (2 * bpp, pitch)) - ar2 = ar[:,::2] + self.assertEqual(ar2.ndim, 2) + self.assertEqual(ar2.shape, (w2, h)) + self.assertEqual(ar2.strides, (2 * bpp, pitch)) + ar2 = ar[:, ::2] h2 = len(([0] * h)[::2]) - self.assertEqual (ar2.ndim, 2) - self.assertEqual (ar2.shape, (w, h2)) - self.assertEqual (ar2.strides, (bpp, 2 * pitch)) + self.assertEqual(ar2.ndim, 2) + self.assertEqual(ar2.shape, (w, h2)) + self.assertEqual(ar2.strides, (bpp, 2 * pitch)) ar2 = ar[1] - self.assertEqual (ar2.ndim, 1) - self.assertEqual (ar2.shape, (h,)) - self.assertEqual (ar2.strides, (pitch,)) - ar2 = ar[:,1] - self.assertEqual (ar2.ndim, 1) - self.assertEqual (ar2.shape, (w,)) - self.assertEqual (ar2.strides, (bpp,)) + self.assertEqual(ar2.ndim, 1) + self.assertEqual(ar2.shape, (h,)) + self.assertEqual(ar2.strides, (pitch,)) + ar2 = ar[:, 1] + self.assertEqual(ar2.ndim, 1) + self.assertEqual(ar2.shape, (w,)) + self.assertEqual(ar2.strides, (bpp,)) def test_self_assign(self): # This differs from NumPy arrays. @@ -919,201 +1187,196 @@ class PixelArrayTypeTest (unittest.TestCase, TestMixin): h = 20 max_y = h - 1 for bpp in [1, 2, 3, 4]: - sf = pygame.Surface ((w, h), 0, bpp * 8) - ar = pygame.PixelArray (sf) - for i in range (w * h): + sf = pygame.Surface((w, h), 0, bpp * 8) + ar = pygame.PixelArray(sf) + for i in range(w * h): ar[i % w, i // w] = i - ar[:,:] = ar[::-1,:] - for i in range (w * h): - self.assertEqual (ar[max_x - i % w, i // w], i) - ar = pygame.PixelArray (sf) - for i in range (w * h): + ar[:, :] = ar[::-1, :] + for i in range(w * h): + self.assertEqual(ar[max_x - i % w, i // w], i) + ar = pygame.PixelArray(sf) + for i in range(w * h): ar[i % w, i // w] = i - ar[:,:] = ar[:,::-1] - for i in range (w * h): - self.assertEqual (ar[i % w, max_y - i // w ], i) - ar = pygame.PixelArray (sf) + ar[:, :] = ar[:, ::-1] + for i in range(w * h): + self.assertEqual(ar[i % w, max_y - i // w], i) + ar = pygame.PixelArray(sf) for i in range(w * h): ar[i % w, i // w] = i - ar[:,:] = ar[::-1,::-1] - for i in range (w * h): - self.assertEqual (ar[max_x - i % w, max_y - i // w], i) + ar[:, :] = ar[::-1, ::-1] + for i in range(w * h): + self.assertEqual(ar[max_x - i % w, max_y - i // w], i) - def test_color_value (self): + def test_color_value(self): # Confirm that a PixelArray slice assignment distinguishes between # pygame.Color and tuple objects as single (r, g, b[, a]) colors # and other sequences as sequences of colors to be treated as # slices. - sf = pygame.Surface ((5, 5), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((5, 5), 0, 32) + ar = pygame.PixelArray(sf) index = slice(None, None, 1) - ar.__setitem__ (index, (1, 2, 3)) - self.assertEqual (ar[0, 0], sf.map_rgb ((1, 2, 3))) - ar.__setitem__ (index, pygame.Color (10, 11, 12)) - self.assertEqual (ar[0, 0], sf.map_rgb ((10, 11, 12))) - self.assertRaises (ValueError, ar.__setitem__, index, (1, 2, 3, 4, 5)) - self.assertRaises (ValueError, ar.__setitem__, (index, index), - (1, 2, 3, 4, 5)) - self.assertRaises (ValueError, ar.__setitem__, index, [1, 2, 3]) - self.assertRaises (ValueError, ar.__setitem__, (index, index), - [1, 2, 3]) - sf = pygame.Surface ((3, 3), 0, 32) - ar = pygame.PixelArray (sf) + ar.__setitem__(index, (1, 2, 3)) + self.assertEqual(ar[0, 0], sf.map_rgb((1, 2, 3))) + ar.__setitem__(index, pygame.Color(10, 11, 12)) + self.assertEqual(ar[0, 0], sf.map_rgb((10, 11, 12))) + self.assertRaises(ValueError, ar.__setitem__, index, (1, 2, 3, 4, 5)) + self.assertRaises(ValueError, ar.__setitem__, (index, index), (1, 2, 3, 4, 5)) + self.assertRaises(ValueError, ar.__setitem__, index, [1, 2, 3]) + self.assertRaises(ValueError, ar.__setitem__, (index, index), [1, 2, 3]) + sf = pygame.Surface((3, 3), 0, 32) + ar = pygame.PixelArray(sf) ar[:] = (20, 30, 40) - self.assertEqual (ar[0, 0], sf.map_rgb ((20, 30, 40))) + self.assertEqual(ar[0, 0], sf.map_rgb((20, 30, 40))) ar[:] = [20, 30, 40] - self.assertEqual (ar[0, 0], 20) - self.assertEqual (ar[1, 0], 30) - self.assertEqual (ar[2, 0], 40) + self.assertEqual(ar[0, 0], 20) + self.assertEqual(ar[1, 0], 30) + self.assertEqual(ar[2, 0], 40) - def test_transpose (self): + def test_transpose(self): # PixelArray.transpose(): swap axis on a 2D array, add a length # 1 x axis to a 1D array. - sf = pygame.Surface ((3, 7), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((3, 7), 0, 32) + ar = pygame.PixelArray(sf) w, h = ar.shape dx, dy = ar.strides - for i in range (w * h): + for i in range(w * h): x = i % w y = i // w ar[x, y] = i ar_t = ar.transpose() - self.assertEqual (ar_t.shape, (h, w)) - self.assertEqual (ar_t.strides, (dy, dx)) - for i in range (w * h): + self.assertEqual(ar_t.shape, (h, w)) + self.assertEqual(ar_t.strides, (dy, dx)) + for i in range(w * h): x = i % w y = i // w - self.assertEqual (ar_t[y, x], ar[x, y]) + self.assertEqual(ar_t[y, x], ar[x, y]) ar1D = ar[0] ar2D = ar1D.transpose() - self.assertEqual (ar2D.shape, (1, h)) - for y in range (h): - self.assertEqual (ar1D[y], ar2D[0, y]) - ar1D = ar[:,0] + self.assertEqual(ar2D.shape, (1, h)) + for y in range(h): + self.assertEqual(ar1D[y], ar2D[0, y]) + ar1D = ar[:, 0] ar2D = ar1D.transpose() - self.assertEqual (ar2D.shape, (1, w)) - for x in range (2): - self.assertEqual (ar1D[x], ar2D[0, x]) + self.assertEqual(ar2D.shape, (1, w)) + for x in range(2): + self.assertEqual(ar1D[x], ar2D[0, x]) - def test_length_1_dimension_broadcast (self): + def test_length_1_dimension_broadcast(self): w = 5 - sf = pygame.Surface ((w, w), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((w, w), 0, 32) + ar = pygame.PixelArray(sf) # y-axis broadcast. - sf_x = pygame.Surface ((w, 1), 0, 32) - ar_x = pygame.PixelArray (sf_x) - for i in range (w): + sf_x = pygame.Surface((w, 1), 0, 32) + ar_x = pygame.PixelArray(sf_x) + for i in range(w): ar_x[i, 0] = (w + 1) * 10 ar[...] = ar_x - for y in range (w): - for x in range (w): - self.assertEqual (ar[x, y], ar_x[x, 0]) + for y in range(w): + for x in range(w): + self.assertEqual(ar[x, y], ar_x[x, 0]) # x-axis broadcast. ar[...] = 0 - sf_y = pygame.Surface ((1, w), 0, 32) - ar_y = pygame.PixelArray (sf_y) - for i in range (w): + sf_y = pygame.Surface((1, w), 0, 32) + ar_y = pygame.PixelArray(sf_y) + for i in range(w): ar_y[0, i] = (w + 1) * 10 ar[...] = ar_y - for x in range (w): - for y in range (w): - self.assertEqual (ar[x, y], ar_y[0, y]) + for x in range(w): + for y in range(w): + self.assertEqual(ar[x, y], ar_y[0, y]) # (1, 1) array broadcast. ar[...] = 0 - sf_1px = pygame.Surface ((1, 1), 0, 32) - ar_1px = pygame.PixelArray (sf_1px) + sf_1px = pygame.Surface((1, 1), 0, 32) + ar_1px = pygame.PixelArray(sf_1px) ar_1px[0, 0] = 42 # Well it had to show up somewhere. ar[...] = ar_1px - for y in range (w): - for x in range (w): - self.assertEqual (ar[x, y], 42) + for y in range(w): + for x in range(w): + self.assertEqual(ar[x, y], 42) - def test_assign_size_mismatch (self): - sf = pygame.Surface ((7, 11), 0, 32) - ar = pygame.PixelArray (sf) - self.assertRaises (ValueError, ar.__setitem__, Ellipsis, ar[:, 0:2]) - self.assertRaises (ValueError, ar.__setitem__, Ellipsis, ar[0:2, :]) + def test_assign_size_mismatch(self): + sf = pygame.Surface((7, 11), 0, 32) + ar = pygame.PixelArray(sf) + self.assertRaises(ValueError, ar.__setitem__, Ellipsis, ar[:, 0:2]) + self.assertRaises(ValueError, ar.__setitem__, Ellipsis, ar[0:2, :]) - def test_repr (self): + def test_repr(self): # Python 3.x bug: the tp_repr slot function returned NULL instead # of a Unicode string, triggering an exception. - sf = pygame.Surface ((3, 1), pygame.SRCALPHA, 16) + sf = pygame.Surface((3, 1), pygame.SRCALPHA, 16) ar = pygame.PixelArray(sf) ar[...] = 42 - pixel = sf.get_at_mapped ((0, 0)) - self.assertEqual(repr (ar), - type (ar).__name__ + "([\n [42, 42, 42]]\n)") + pixel = sf.get_at_mapped((0, 0)) + self.assertEqual(repr(ar), type(ar).__name__ + "([\n [42, 42, 42]]\n)") +@unittest.skipIf(IS_PYPY, "pypy having issues") class PixelArrayArrayInterfaceTest(unittest.TestCase, TestMixin): - - @unittest.skipIf(IS_PYPY, 'skipping for PyPy (why?)') - def test_basic (self): + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_basic(self): # Check unchanging fields. - sf = pygame.Surface ((2, 2), 0, 32) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((2, 2), 0, 32) + ar = pygame.PixelArray(sf) - ai = arrinter.ArrayInterface (ar) - self.assertEqual (ai.two, 2) - self.assertEqual (ai.typekind, 'u') - self.assertEqual (ai.nd, 2) - self.assertEqual (ai.data, ar._pixels_address) + ai = arrinter.ArrayInterface(ar) + self.assertEqual(ai.two, 2) + self.assertEqual(ai.typekind, "u") + self.assertEqual(ai.nd, 2) + self.assertEqual(ai.data, ar._pixels_address) - @unittest.skipIf(IS_PYPY, 'skipping for PyPy (why?)') + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") def test_shape(self): - for shape in [[4, 16], [5, 13]]: w, h = shape - sf = pygame.Surface (shape, 0, 32) - ar = pygame.PixelArray (sf) - ai = arrinter.ArrayInterface (ar) + sf = pygame.Surface(shape, 0, 32) + ar = pygame.PixelArray(sf) + ai = arrinter.ArrayInterface(ar) ai_shape = [ai.shape[i] for i in range(ai.nd)] - self.assertEqual (ai_shape, shape) - ar2 = ar[::2,:] - ai2 = arrinter.ArrayInterface (ar2) + self.assertEqual(ai_shape, shape) + ar2 = ar[::2, :] + ai2 = arrinter.ArrayInterface(ar2) w2 = len(([0] * w)[::2]) ai_shape = [ai2.shape[i] for i in range(ai2.nd)] - self.assertEqual (ai_shape, [w2, h]) - ar2 = ar[:,::2] - ai2 = arrinter.ArrayInterface (ar2) + self.assertEqual(ai_shape, [w2, h]) + ar2 = ar[:, ::2] + ai2 = arrinter.ArrayInterface(ar2) h2 = len(([0] * h)[::2]) ai_shape = [ai2.shape[i] for i in range(ai2.nd)] - self.assertEqual (ai_shape, [w, h2]) + self.assertEqual(ai_shape, [w, h2]) - @unittest.skipIf(IS_PYPY, 'skipping for PyPy (why?)') - def test_itemsize (self): + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_itemsize(self): for bytes_per_pixel in range(1, 5): bits_per_pixel = 8 * bytes_per_pixel - sf = pygame.Surface ((2, 2), 0, bits_per_pixel) - ar = pygame.PixelArray (sf) - ai = arrinter.ArrayInterface (ar) - self.assertEqual (ai.itemsize, bytes_per_pixel) + sf = pygame.Surface((2, 2), 0, bits_per_pixel) + ar = pygame.PixelArray(sf) + ai = arrinter.ArrayInterface(ar) + self.assertEqual(ai.itemsize, bytes_per_pixel) - @unittest.skipIf(IS_PYPY, 'skipping for PyPy (why?)') - def test_flags (self): + @unittest.skipIf(IS_PYPY, "skipping for PyPy (why?)") + def test_flags(self): aim = arrinter - common_flags = (aim.PAI_NOTSWAPPED | aim.PAI_WRITEABLE | - aim.PAI_ALIGNED) - s = pygame.Surface ((10, 2), 0, 32) - ar = pygame.PixelArray (s) - ai = aim.ArrayInterface (ar) - self.assertEqual (ai.flags, common_flags | aim.PAI_FORTRAN) - - ar2 = ar[::2,:] - ai = aim.ArrayInterface (ar2) - self.assertEqual (ai.flags, common_flags) - - s = pygame.Surface ((8, 2), 0, 24) - ar = pygame.PixelArray (s) - ai = aim.ArrayInterface (ar) - self.assertEqual (ai.flags, common_flags | aim.PAI_FORTRAN) - - s = pygame.Surface ((7, 2), 0, 24) - ar = pygame.PixelArray (s) - ai = aim.ArrayInterface (ar) - self.assertEqual (ai.flags, common_flags) - - def test_slicing (self): + common_flags = aim.PAI_NOTSWAPPED | aim.PAI_WRITEABLE | aim.PAI_ALIGNED + s = pygame.Surface((10, 2), 0, 32) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags | aim.PAI_FORTRAN) + + ar2 = ar[::2, :] + ai = aim.ArrayInterface(ar2) + self.assertEqual(ai.flags, common_flags) + + s = pygame.Surface((8, 2), 0, 24) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags | aim.PAI_FORTRAN) + + s = pygame.Surface((7, 2), 0, 24) + ar = pygame.PixelArray(s) + ai = aim.ArrayInterface(ar) + self.assertEqual(ai.flags, common_flags) + + def test_slicing(self): # This will implicitly test data and strides fields. # # Need an 8 bit test surfaces because pixelcopy.make_surface @@ -1121,300 +1384,284 @@ class PixelArrayArrayInterfaceTest(unittest.TestCase, TestMixin): factors = [7, 3, 11] - w = reduce (operator.mul, factors, 1) + w = reduce(operator.mul, factors, 1) h = 13 - sf = pygame.Surface ((w, h), 0, 8) - color = sf.map_rgb ((1, 17, 128)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) for f in factors[:-1]: w = w // f - sf.fill ((0, 0, 0)) - ar = ar[f:f + w,:] + sf.fill((0, 0, 0)) + ar = ar[f : f + w, :] ar[0][0] = color ar[-1][-2] = color ar[0][-3] = color - sf2 = ar.make_surface () - sf3 = pygame.pixelcopy.make_surface (ar) - self.assert_surfaces_equal (sf3, sf2) + sf2 = ar.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar) + self.assert_surfaces_equal(sf3, sf2) - h = reduce (operator.mul, factors, 1) + h = reduce(operator.mul, factors, 1) w = 13 - sf = pygame.Surface ((w, h), 0, 8) - color = sf.map_rgb ((1, 17, 128)) - ar = pygame.PixelArray (sf) + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) for f in factors[:-1]: h = h // f - sf.fill ((0, 0, 0)) - ar = ar[:,f:f + h] + sf.fill((0, 0, 0)) + ar = ar[:, f : f + h] ar[0][0] = color ar[-1][-2] = color ar[0][-3] = color - sf2 = ar.make_surface () - sf3 = pygame.pixelcopy.make_surface (ar) - self.assert_surfaces_equal (sf3, sf2) + sf2 = ar.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar) + self.assert_surfaces_equal(sf3, sf2) w = 20 h = 10 - sf = pygame.Surface ((w, h), 0, 8) - color = sf.map_rgb ((1, 17, 128)) - ar = pygame.PixelArray (sf) - for slices in [(slice (w), slice (h)), - (slice (0, w, 2), slice (h)), - (slice (0, w, 3), slice (h)), - (slice (w), slice (0, h, 2)), - (slice (w), slice (0, h, 3)), - (slice (0, w, 2), slice (0, h, 2)), - (slice (0, w, 3), slice (0, h, 3)), - ]: - sf.fill ((0, 0, 0)) + sf = pygame.Surface((w, h), 0, 8) + color = sf.map_rgb((1, 17, 128)) + ar = pygame.PixelArray(sf) + for slices in [ + (slice(w), slice(h)), + (slice(0, w, 2), slice(h)), + (slice(0, w, 3), slice(h)), + (slice(w), slice(0, h, 2)), + (slice(w), slice(0, h, 3)), + (slice(0, w, 2), slice(0, h, 2)), + (slice(0, w, 3), slice(0, h, 3)), + ]: + sf.fill((0, 0, 0)) ar2 = ar[slices] ar2[0][0] = color ar2[-1][-2] = color ar2[0][-3] = color - sf2 = ar2.make_surface () - sf3 = pygame.pixelcopy.make_surface (ar2) - self.assert_surfaces_equal (sf3, sf2) + sf2 = ar2.make_surface() + sf3 = pygame.pixelcopy.make_surface(ar2) + self.assert_surfaces_equal(sf3, sf2) -@unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') +@unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") +@unittest.skipIf(IS_PYPY, "pypy having issues") class PixelArrayNewBufferTest(unittest.TestCase, TestMixin): - if pygame.HAVE_NEWBUF: from pygame.tests.test_utils import buftools - bitsize_to_format = {8: 'B', 16: '=H', 24: '3x', 32: '=I'} + bitsize_to_format = {8: "B", 16: "=H", 24: "3x", 32: "=I"} - def test_newbuf_2D (self): + def test_newbuf_2D(self): buftools = self.buftools Importer = buftools.Importer for bit_size in [8, 16, 24, 32]: - s = pygame.Surface ((10, 2), 0, bit_size) - ar = pygame.PixelArray (s) + s = pygame.Surface((10, 2), 0, bit_size) + ar = pygame.PixelArray(s) format = self.bitsize_to_format[bit_size] itemsize = ar.itemsize shape = ar.shape w, h = shape strides = ar.strides length = w * h * itemsize - imp = Importer (ar, buftools.PyBUF_FULL) - self.assertTrue (imp.obj, ar) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 2) - self.assertEqual (imp.itemsize, itemsize) - self.assertEqual (imp.format, format) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertTrue (imp.suboffsets is None) - self.assertEqual (imp.buf, s._pixels_address) - - s = pygame.Surface ((8, 16), 0, 32) - ar = pygame.PixelArray (s) - format = self.bitsize_to_format[s.get_bitsize ()] + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.format, format) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s._pixels_address) + + s = pygame.Surface((8, 16), 0, 32) + ar = pygame.PixelArray(s) + format = self.bitsize_to_format[s.get_bitsize()] itemsize = ar.itemsize shape = ar.shape w, h = shape strides = ar.strides length = w * h * itemsize - imp = Importer (ar, buftools.PyBUF_SIMPLE) - self.assertTrue (imp.obj, ar) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 0) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertTrue (imp.shape is None) - self.assertTrue (imp.strides is None) - self.assertTrue (imp.suboffsets is None) - self.assertEqual (imp.buf, s._pixels_address) - imp = Importer (ar, buftools.PyBUF_FORMAT) - self.assertEqual (imp.ndim, 0) - self.assertEqual (imp.format, format) - imp = Importer (ar, buftools.PyBUF_WRITABLE) - self.assertEqual (imp.ndim, 0) - self.assertTrue (imp.format is None) - imp = Importer (ar, buftools.PyBUF_F_CONTIGUOUS) - self.assertEqual (imp.ndim, 2) - self.assertTrue (imp.format is None) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - imp = Importer (ar, buftools.PyBUF_ANY_CONTIGUOUS) - self.assertEqual (imp.ndim, 2) - self.assertTrue (imp.format is None) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar, buftools.PyBUF_ND) - - ar_sliced = ar[:,::2] - format = self.bitsize_to_format[s.get_bitsize ()] + imp = Importer(ar, buftools.PyBUF_SIMPLE) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s._pixels_address) + imp = Importer(ar, buftools.PyBUF_FORMAT) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.format, format) + imp = Importer(ar, buftools.PyBUF_WRITABLE) + self.assertEqual(imp.ndim, 0) + self.assertTrue(imp.format is None) + imp = Importer(ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 2) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + imp = Importer(ar, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 2) + self.assertTrue(imp.format is None) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + + ar_sliced = ar[:, ::2] + format = self.bitsize_to_format[s.get_bitsize()] itemsize = ar_sliced.itemsize shape = ar_sliced.shape w, h = shape strides = ar_sliced.strides length = w * h * itemsize - imp = Importer (ar_sliced, buftools.PyBUF_STRIDED) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 2) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertEqual (imp.buf, s._pixels_address) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_SIMPLE) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_ND) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_ANY_CONTIGUOUS) - - ar_sliced = ar[::2,:] - format = self.bitsize_to_format[s.get_bitsize ()] + imp = Importer(ar_sliced, buftools.PyBUF_STRIDED) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, s._pixels_address) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises( + BufferError, Importer, ar_sliced, buftools.PyBUF_ANY_CONTIGUOUS + ) + + ar_sliced = ar[::2, :] + format = self.bitsize_to_format[s.get_bitsize()] itemsize = ar_sliced.itemsize shape = ar_sliced.shape w, h = shape strides = ar_sliced.strides length = w * h * itemsize - imp = Importer (ar_sliced, buftools.PyBUF_STRIDED) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 2) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertEqual (imp.buf, s._pixels_address) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_SIMPLE) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_ND) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar_sliced, - buftools.PyBUF_ANY_CONTIGUOUS) - - s2 = s.subsurface ((2, 3, 5, 7)) - ar = pygame.PixelArray (s2) - format = self.bitsize_to_format[s.get_bitsize ()] + imp = Importer(ar_sliced, buftools.PyBUF_STRIDED) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, s._pixels_address) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar_sliced, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises( + BufferError, Importer, ar_sliced, buftools.PyBUF_ANY_CONTIGUOUS + ) + + s2 = s.subsurface((2, 3, 5, 7)) + ar = pygame.PixelArray(s2) + format = self.bitsize_to_format[s.get_bitsize()] itemsize = ar.itemsize shape = ar.shape w, h = shape strides = ar.strides length = w * h * itemsize - imp = Importer (ar, buftools.PyBUF_STRIDES) - self.assertTrue (imp.obj, ar) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 2) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertTrue (imp.suboffsets is None) - self.assertEqual (imp.buf, s2._pixels_address) - self.assertRaises (BufferError, Importer, ar, buftools.PyBUF_SIMPLE) - self.assertRaises (BufferError, Importer, ar, buftools.PyBUF_FORMAT) - self.assertRaises (BufferError, Importer, ar, buftools.PyBUF_WRITABLE) - self.assertRaises (BufferError, Importer, ar, buftools.PyBUF_ND) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_ANY_CONTIGUOUS) + imp = Importer(ar, buftools.PyBUF_STRIDES) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 2) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, s2._pixels_address) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ANY_CONTIGUOUS) def test_newbuf_1D(self): buftools = self.buftools Importer = buftools.Importer - s = pygame.Surface ((2, 16), 0, 32) - ar_2D = pygame.PixelArray (s) + s = pygame.Surface((2, 16), 0, 32) + ar_2D = pygame.PixelArray(s) x = 0 ar = ar_2D[x] - format = self.bitsize_to_format[s.get_bitsize ()] + format = self.bitsize_to_format[s.get_bitsize()] itemsize = ar.itemsize shape = ar.shape h = shape[0] strides = ar.strides length = h * itemsize buf = s._pixels_address + x * itemsize - imp = Importer (ar, buftools.PyBUF_STRIDES) - self.assertTrue (imp.obj, ar) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 1) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertTrue (imp.suboffsets is None) - self.assertEqual (imp.buf, buf) - imp = Importer (ar, buftools.PyBUF_FULL) - self.assertEqual (imp.ndim, 1) - self.assertEqual (imp.format, format) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_SIMPLE) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_FORMAT) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_WRITABLE) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_ND) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises (BufferError, Importer, ar, - buftools.PyBUF_ANY_CONTIGUOUS) + imp = Importer(ar, buftools.PyBUF_STRIDES) + self.assertTrue(imp.obj, ar) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertTrue(imp.suboffsets is None) + self.assertEqual(imp.buf, buf) + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.format, format) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_SIMPLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_FORMAT) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ND) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, ar, buftools.PyBUF_ANY_CONTIGUOUS) y = 10 - ar = ar_2D[:,y] + ar = ar_2D[:, y] shape = ar.shape w = shape[0] strides = ar.strides length = w * itemsize buf = s._pixels_address + y * s.get_pitch() - imp = Importer (ar, buftools.PyBUF_FULL) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 1) - self.assertEqual (imp.itemsize, itemsize) - self.assertEqual (imp.format, format) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertEqual (imp.strides, strides) - self.assertEqual (imp.buf, buf) - self.assertTrue (imp.suboffsets is None) - imp = Importer (ar, buftools.PyBUF_SIMPLE) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 0) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertTrue (imp.shape is None) - self.assertTrue (imp.strides is None) - imp = Importer (ar, buftools.PyBUF_ND) - self.assertEqual (imp.len, length) - self.assertEqual (imp.ndim, 1) - self.assertEqual (imp.itemsize, itemsize) - self.assertTrue (imp.format is None) - self.assertFalse (imp.readonly) - self.assertEqual (imp.shape, shape) - self.assertTrue (imp.strides is None) - imp = Importer (ar, buftools.PyBUF_C_CONTIGUOUS) - self.assertEqual (imp.ndim, 1) - imp = Importer (ar, buftools.PyBUF_F_CONTIGUOUS) - self.assertEqual (imp.ndim, 1) - imp = Importer (ar, buftools.PyBUF_ANY_CONTIGUOUS) - self.assertEqual (imp.ndim, 1) - - -if __name__ == '__main__': + imp = Importer(ar, buftools.PyBUF_FULL) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertEqual(imp.format, format) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertEqual(imp.strides, strides) + self.assertEqual(imp.buf, buf) + self.assertTrue(imp.suboffsets is None) + imp = Importer(ar, buftools.PyBUF_SIMPLE) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 0) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertTrue(imp.shape is None) + self.assertTrue(imp.strides is None) + imp = Importer(ar, buftools.PyBUF_ND) + self.assertEqual(imp.len, length) + self.assertEqual(imp.ndim, 1) + self.assertEqual(imp.itemsize, itemsize) + self.assertTrue(imp.format is None) + self.assertFalse(imp.readonly) + self.assertEqual(imp.shape, shape) + self.assertTrue(imp.strides is None) + imp = Importer(ar, buftools.PyBUF_C_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = Importer(ar, buftools.PyBUF_F_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + imp = Importer(ar, buftools.PyBUF_ANY_CONTIGUOUS) + self.assertEqual(imp.ndim, 1) + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/pixelcopy_test.py b/venv/Lib/site-packages/pygame/tests/pixelcopy_test.py index 8cf89eb3997878b074035d89ed7da15f75679267..6510fd99e16895da4063a064520153869c3bf223 100644 --- a/venv/Lib/site-packages/pygame/tests/pixelcopy_test.py +++ b/venv/Lib/site-packages/pygame/tests/pixelcopy_test.py @@ -1,4 +1,3 @@ -import ctypes import platform import unittest @@ -8,43 +7,52 @@ except NameError: pass import pygame from pygame.locals import * -from pygame.pixelcopy import ( - surface_to_array, map_array, array_to_surface, - make_surface -) +from pygame.pixelcopy import surface_to_array, map_array, array_to_surface, make_surface -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() def unsigned32(i): """cast signed 32 bit integer to an unsigned integer""" return i & 0xFFFFFFFF -class PixelcopyModuleTest (unittest.TestCase): +@unittest.skipIf(IS_PYPY, "pypy having illegal instruction on mac") +class PixelcopyModuleTest(unittest.TestCase): bitsizes = [8, 16, 32] - test_palette = [(0, 0, 0, 255), - (10, 30, 60, 255), - (25, 75, 100, 255), - (100, 150, 200, 255), - (0, 100, 200, 255)] + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] surf_size = (10, 12) - test_points = [((0, 0), 1), ((4, 5), 1), ((9, 0), 2), - ((5, 5), 2), ((0, 11), 3), ((4, 6), 3), - ((9, 11), 4), ((5, 6), 4)] + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] def __init__(self, *args, **kwds): pygame.display.init() try: unittest.TestCase.__init__(self, *args, **kwds) - self.sources = [self._make_src_surface(8), - self._make_src_surface(16), - self._make_src_surface(16, srcalpha=True), - self._make_src_surface(24), - self._make_src_surface(32), - self._make_src_surface(32, srcalpha=True)] + self.sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] finally: pygame.display.quit() @@ -88,7 +96,7 @@ class PixelcopyModuleTest (unittest.TestCase): # of unsigned integers. The byte order is system dependent. dst = pygame.Surface(surf.get_size(), 0, dst_bitsize) dst.fill((0, 0, 0, 0)) - view = dst.get_view('2') + view = dst.get_view("2") self.assertFalse(surf.get_locked()) if dst_bitsize < src_bitsize: self.assertRaises(ValueError, surface_to_array, view, surf) @@ -99,17 +107,18 @@ class PixelcopyModuleTest (unittest.TestCase): for posn, i in self.test_points: sp = surf.get_at_mapped(posn) dp = dst.get_at_mapped(posn) - self.assertEqual(dp, sp, - "%s != %s: flags: %i" - ", bpp: %i, posn: %s" % - (dp, sp, - surf.get_flags(), surf.get_bitsize(), - posn)) + self.assertEqual( + dp, + sp, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dp, sp, surf.get_flags(), surf.get_bitsize(), posn), + ) del view if surf.get_masks()[3]: dst.fill((0, 0, 0, 0)) - view = dst.get_view('2') + view = dst.get_view("2") posn = (2, 1) surf.set_at(posn, alpha_color) self.assertFalse(surf.get_locked()) @@ -117,9 +126,9 @@ class PixelcopyModuleTest (unittest.TestCase): self.assertFalse(surf.get_locked()) sp = surf.get_at_mapped(posn) dp = dst.get_at_mapped(posn) - self.assertEqual(dp, sp, - "%s != %s: bpp: %i" % - (dp, sp, surf.get_bitsize())) + self.assertEqual( + dp, sp, "%s != %s: bpp: %i" % (dp, sp, surf.get_bitsize()) + ) if IS_PYPY: return @@ -129,22 +138,29 @@ class PixelcopyModuleTest (unittest.TestCase): for itemsize in [1, 2, 4, 8]: if itemsize < surf.get_bytesize(): continue - a = arrinter.Array(surf.get_size(), 'u', itemsize, - flags=pai_flags) + a = arrinter.Array(surf.get_size(), "u", itemsize, flags=pai_flags) surface_to_array(a, surf) for posn, i in self.test_points: sp = unsigned32(surf.get_at_mapped(posn)) dp = a[posn] - self.assertEqual(dp, sp, - "%s != %s: itemsize: %i, flags: %i" - ", bpp: %i, posn: %s" % - (dp, sp, itemsize, - surf.get_flags(), surf.get_bitsize(), - posn)) + self.assertEqual( + dp, + sp, + "%s != %s: itemsize: %i, flags: %i" + ", bpp: %i, posn: %s" + % ( + dp, + sp, + itemsize, + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) def test_surface_to_array_3d(self): - self.iter_surface_to_array_3d((0xff, 0xff00, 0xff0000, 0)) - self.iter_surface_to_array_3d((0xff0000, 0xff00, 0xff, 0)) + self.iter_surface_to_array_3d((0xFF, 0xFF00, 0xFF0000, 0)) + self.iter_surface_to_array_3d((0xFF0000, 0xFF00, 0xFF, 0)) def iter_surface_to_array_3d(self, rgba_masks): dst = pygame.Surface(self.surf_size, 0, 24, masks=rgba_masks) @@ -152,58 +168,63 @@ class PixelcopyModuleTest (unittest.TestCase): for surf in self.sources: dst.fill((0, 0, 0, 0)) src_bitsize = surf.get_bitsize() - view = dst.get_view('3') + view = dst.get_view("3") self.assertFalse(surf.get_locked()) surface_to_array(view, surf) self.assertFalse(surf.get_locked()) for posn, i in self.test_points: sc = surf.get_at(posn)[0:3] dc = dst.get_at(posn)[0:3] - self.assertEqual(dc, sc, - "%s != %s: flags: %i" - ", bpp: %i, posn: %s" % - (dc, sc, - surf.get_flags(), surf.get_bitsize(), - posn)) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, surf.get_flags(), surf.get_bitsize(), posn), + ) view = None def test_map_array(self): - targets = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True), - ] - source = pygame.Surface(self.surf_size, 0, 24, - masks=[0xff, 0xff00, 0xff0000, 0]) + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + source = pygame.Surface( + self.surf_size, 0, 24, masks=[0xFF, 0xFF00, 0xFF0000, 0] + ) self._fill_surface(source) - source_view = source.get_view('3') # (w, h, 3) + source_view = source.get_view("3") # (w, h, 3) for t in targets: - map_array(t.get_view('2'), source_view, t) + map_array(t.get_view("2"), source_view, t) for posn, i in self.test_points: sc = t.map_rgb(source.get_at(posn)) dc = t.get_at_mapped(posn) - self.assertEqual(dc, sc, - "%s != %s: flags: %i" - ", bpp: %i, posn: %s" % - (dc, sc, - t.get_flags(), t.get_bitsize(), - posn)) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, t.get_flags(), t.get_bitsize(), posn), + ) color = pygame.Color("salmon") color.set_length(3) for t in targets: - map_array(t.get_view('2'), color, t) + map_array(t.get_view("2"), color, t) sc = t.map_rgb(color) for posn, i in self.test_points: dc = t.get_at_mapped(posn) - self.assertEqual(dc, sc, - "%s != %s: flags: %i" - ", bpp: %i, posn: %s" % - (dc, sc, - t.get_flags(), t.get_bitsize(), - posn)) + self.assertEqual( + dc, + sc, + "%s != %s: flags: %i" + ", bpp: %i, posn: %s" + % (dc, sc, t.get_flags(), t.get_bitsize(), posn), + ) # mismatched shapes w, h = source.get_size() @@ -214,13 +235,14 @@ class PixelcopyModuleTest (unittest.TestCase): def test_array_to_surface_broadcasting(self): # target surfaces - targets = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True), - ] + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] w, h = self.surf_size @@ -229,67 +251,77 @@ class PixelcopyModuleTest (unittest.TestCase): for target in targets: source = pygame.Surface((1, h), 0, target) for y in range(h): - source.set_at((0, y), - pygame.Color(y + 1, y + h + 1, y + 2 * h + 1)) - pygame.pixelcopy.surface_to_array(column.get_view('2'), source) - pygame.pixelcopy.array_to_surface(target, column.get_view('2')) + source.set_at((0, y), pygame.Color(y + 1, y + h + 1, y + 2 * h + 1)) + pygame.pixelcopy.surface_to_array(column.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, column.get_view("2")) for x in range(w): for y in range(h): - self.assertEqual(target.get_at_mapped((x, y)), - column.get_at_mapped((0, y))) + self.assertEqual( + target.get_at_mapped((x, y)), column.get_at_mapped((0, y)) + ) # broadcast row row = pygame.Surface((w, 1), 0, 32) for target in targets: source = pygame.Surface((w, 1), 0, target) for x in range(w): - source.set_at((x, 0), - pygame.Color(x + 1, x + w + 1, x + 2 * w + 1)) - pygame.pixelcopy.surface_to_array(row.get_view('2'), source) - pygame.pixelcopy.array_to_surface(target, row.get_view('2')) + source.set_at((x, 0), pygame.Color(x + 1, x + w + 1, x + 2 * w + 1)) + pygame.pixelcopy.surface_to_array(row.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, row.get_view("2")) for x in range(w): for y in range(h): - self.assertEqual(target.get_at_mapped((x, y)), - row.get_at_mapped((x, 0))) + self.assertEqual( + target.get_at_mapped((x, y)), row.get_at_mapped((x, 0)) + ) # broadcast pixel pixel = pygame.Surface((1, 1), 0, 32) for target in targets: source = pygame.Surface((1, 1), 0, target) source.set_at((0, 0), pygame.Color(13, 47, 101)) - pygame.pixelcopy.surface_to_array(pixel.get_view('2'), source) - pygame.pixelcopy.array_to_surface(target, pixel.get_view('2')) + pygame.pixelcopy.surface_to_array(pixel.get_view("2"), source) + pygame.pixelcopy.array_to_surface(target, pixel.get_view("2")) p = pixel.get_at_mapped((0, 0)) for x in range(w): for y in range(h): self.assertEqual(target.get_at_mapped((x, y)), p) -class PixelCopyTestWithArray(unittest.TestCase): +@unittest.skipIf(IS_PYPY, "pypy having illegal instruction on mac") +class PixelCopyTestWithArrayNumpy(unittest.TestCase): try: import numpy except ImportError: - __tags__ = ['ignore', 'subprocess_ignore'] + __tags__ = ["ignore", "subprocess_ignore"] else: - pygame.surfarray.use_arraytype('numpy') + pygame.surfarray.use_arraytype("numpy") bitsizes = [8, 16, 32] - test_palette = [(0, 0, 0, 255), - (10, 30, 60, 255), - (25, 75, 100, 255), - (100, 150, 200, 255), - (0, 100, 200, 255)] + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] surf_size = (10, 12) - test_points = [((0, 0), 1), ((4, 5), 1), ((9, 0), 2), - ((5, 5), 2), ((0, 11), 3), ((4, 6), 3), - ((9, 11), 4), ((5, 6), 4)] - - pixels2d = set([8, 16, 32]) - pixels3d = set([24, 32]) - array2d = set([8, 16, 24, 32]) - array3d = set([24, 32]) + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] + + pixels2d = {8, 16, 32} + pixels3d = {24, 32} + array2d = {8, 16, 24, 32} + array3d = {24, 32} def __init__(self, *args, **kwds): import numpy @@ -302,12 +334,14 @@ class PixelCopyTestWithArray(unittest.TestCase): pygame.display.init() try: unittest.TestCase.__init__(self, *args, **kwds) - self.sources = [self._make_src_surface(8), - self._make_src_surface(16), - self._make_src_surface(16, srcalpha=True), - self._make_src_surface(24), - self._make_src_surface(32), - self._make_src_surface(32, srcalpha=True)] + self.sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] finally: pygame.display.quit() @@ -352,10 +386,10 @@ class PixelCopyTestWithArray(unittest.TestCase): dst_dims = self.surf_size destinations = [empty(dst_dims, t) for t in self.dst_types] - if (pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN): - swapped_dst = empty(dst_dims, dtype('>u4')) + if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: + swapped_dst = empty(dst_dims, dtype(">u4")) else: - swapped_dst = empty(dst_dims, dtype('u4')) + if pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN: + swapped_dst = empty(dst_dims, dtype(">u4")) else: - swapped_dst = empty(dst_dims, dtype('i', - '!i', '1i', '=1i', '@q', 'q', '4x', '8x']: + for format in [ + "=i", + "=I", + "=l", + "=L", + "=q", + "=Q", + "i", + "!i", + "1i", + "=1i", + "@q", + "q", + "4x", + "8x", + ]: surface.fill((255, 254, 253)) exp = Exporter(shape, format=format) exp._buf[:] = [42] * exp.buflen @@ -643,11 +701,10 @@ class PixelCopyTestWithArray(unittest.TestCase): for y in range(h): self.assertEqual(surface.get_at((x, y)), (42, 42, 42, 255)) # Some unsupported formats for array_to_surface and a 32 bit surface - for format in ['f', 'd', '?', 'x', - '1x', '2x', '3x', '5x', '6x', '7x', '9x']: + for format in ["f", "d", "?", "x", "1x", "2x", "3x", "5x", "6x", "7x", "9x"]: exp = Exporter(shape, format=format) self.assertRaises(ValueError, array_to_surface, surface, exp) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/rect_test.py b/venv/Lib/site-packages/pygame/tests/rect_test.py index d034667264e264ada5a2d5cc91e6f9491e333e57..a17afbdf96c7bcece0c5970b663dc74be20a1af1 100644 --- a/venv/Lib/site-packages/pygame/tests/rect_test.py +++ b/venv/Lib/site-packages/pygame/tests/rect_test.py @@ -1,7 +1,28 @@ +import math import unittest -from pygame import Rect +from collections.abc import Collection, Sequence +import platform +import random +import unittest + +from pygame import Rect, Vector2 +from pygame.tests import test_utils + +IS_PYPY = "PyPy" == platform.python_implementation() + +# todo can they be different on different platforms? +_int_min = -2147483647 - 1 # min value of int in C +_int_max = 2147483647 # max value of int in C + + +def _random_int(): + return random.randint(_int_min, _int_max) + class RectTypeTest(unittest.TestCase): + def _assertCountEqual(self, *args, **kwargs): + self.assertCountEqual(*args, **kwargs) + def testConstructionXYWidthHeight(self): r = Rect(1, 2, 3, 4) self.assertEqual(1, r.left) @@ -38,18 +59,154 @@ class RectTypeTest(unittest.TestCase): self.assertEqual((r.left, r.centery), r.midleft) self.assertEqual((r.right, r.centery), r.midright) + def test_rect_iter(self): + rect = Rect(50, 100, 150, 200) + + # call __iter__ explicitly to test that it is defined + rect_iterator = rect.__iter__() + for i, val in enumerate(rect_iterator): + self.assertEqual(rect[i], val) + def test_normalize(self): - r = Rect(1, 2, -3, -6) - r2 = Rect(r) - r2.normalize() - self.assertTrue(r2.width >= 0) - self.assertTrue(r2.height >= 0) - self.assertEqual((abs(r.width), abs(r.height)), r2.size) - self.assertEqual((-2, -4), r2.topleft) + """Ensures normalize works when width and height are both negative.""" + test_rect = Rect((1, 2), (-3, -6)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y + test_rect.h), + (-test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__positive_height(self): + """Ensures normalize works with a negative width and a positive height.""" + test_rect = Rect((1, 2), (-3, 6)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y), + (-test_rect.w, test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__positive_width(self): + """Ensures normalize works with a positive width and a negative height.""" + test_rect = Rect((1, 2), (3, -6)) + expected_normalized_rect = ( + (test_rect.x, test_rect.y + test_rect.h), + (test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__zero_height(self): + """Ensures normalize works with a negative width and a zero height.""" + test_rect = Rect((1, 2), (-3, 0)) + expected_normalized_rect = ( + (test_rect.x + test_rect.w, test_rect.y), + (-test_rect.w, test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__zero_width(self): + """Ensures normalize works with a zero width and a negative height.""" + test_rect = Rect((1, 2), (0, -6)) + expected_normalized_rect = ( + (test_rect.x, test_rect.y + test_rect.h), + (test_rect.w, -test_rect.h), + ) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_normalize__non_negative(self): + """Ensures normalize works when width and height are both non-negative. + + Tests combinations of positive and zero values for width and height. + The normalize method has no impact when both width and height are + non-negative. + """ + for size in ((3, 6), (3, 0), (0, 6), (0, 0)): + test_rect = Rect((1, 2), size) + expected_normalized_rect = Rect(test_rect) + + test_rect.normalize() + + self.assertEqual(test_rect, expected_normalized_rect) + + def test_x(self): + """Ensures changing the x attribute moves the rect and does not change + the rect's size. + """ + expected_x = 10 + expected_y = 2 + expected_size = (3, 4) + r = Rect((1, expected_y), expected_size) + + r.x = expected_x + + self.assertEqual(r.x, expected_x) + self.assertEqual(r.x, r.left) + self.assertEqual(r.y, expected_y) + self.assertEqual(r.size, expected_size) + + def test_x__invalid_value(self): + """Ensures the x attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.x = value + + def test_x__del(self): + """Ensures the x attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.x + + def test_y(self): + """Ensures changing the y attribute moves the rect and does not change + the rect's size. + """ + expected_x = 1 + expected_y = 20 + expected_size = (3, 4) + r = Rect((expected_x, 2), expected_size) + + r.y = expected_y + + self.assertEqual(r.y, expected_y) + self.assertEqual(r.y, r.top) + self.assertEqual(r.x, expected_x) + self.assertEqual(r.size, expected_size) + + def test_y__invalid_value(self): + """Ensures the y attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.y = value + + def test_y__del(self): + """Ensures the y attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.y def test_left(self): """Changing the left attribute moves the rect and does not change - the rect's width + the rect's width """ r = Rect(1, 2, 3, 4) new_left = 10 @@ -58,9 +215,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(new_left, r.left) self.assertEqual(Rect(new_left, 2, 3, 4), r) + def test_left__invalid_value(self): + """Ensures the left attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.left = value + + def test_left__del(self): + """Ensures the left attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.left + def test_right(self): """Changing the right attribute moves the rect and does not change - the rect's width + the rect's width """ r = Rect(1, 2, 3, 4) new_right = r.right + 20 @@ -72,9 +244,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_left, r.left) self.assertEqual(old_width, r.width) + def test_right__invalid_value(self): + """Ensures the right attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.right = value + + def test_right__del(self): + """Ensures the right attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.right + def test_top(self): """Changing the top attribute moves the rect and does not change - the rect's width + the rect's width """ r = Rect(1, 2, 3, 4) new_top = 10 @@ -83,9 +270,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(Rect(1, new_top, 3, 4), r) self.assertEqual(new_top, r.top) + def test_top__invalid_value(self): + """Ensures the top attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.top = value + + def test_top__del(self): + """Ensures the top attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.top + def test_bottom(self): """Changing the bottom attribute moves the rect and does not change - the rect's height + the rect's height """ r = Rect(1, 2, 3, 4) new_bottom = r.bottom + 20 @@ -97,9 +299,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_top, r.top) self.assertEqual(old_height, r.height) + def test_bottom__invalid_value(self): + """Ensures the bottom attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottom = value + + def test_bottom__del(self): + """Ensures the bottom attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottom + def test_centerx(self): """Changing the centerx attribute moves the rect and does not change - the rect's width + the rect's width """ r = Rect(1, 2, 3, 4) new_centerx = r.centerx + 20 @@ -111,9 +328,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_left, r.left) self.assertEqual(old_width, r.width) + def test_centerx__invalid_value(self): + """Ensures the centerx attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.centerx = value + + def test_centerx__del(self): + """Ensures the centerx attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.centerx + def test_centery(self): - """Changing the centerx attribute moves the rect and does not change - the rect's width + """Changing the centery attribute moves the rect and does not change + the rect's width """ r = Rect(1, 2, 3, 4) new_centery = r.centery + 20 @@ -125,9 +357,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_top, r.top) self.assertEqual(old_height, r.height) + def test_centery__invalid_value(self): + """Ensures the centery attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.centery = value + + def test_centery__del(self): + """Ensures the centery attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.centery + def test_topleft(self): """Changing the topleft attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) new_topleft = (r.left + 20, r.top + 30) @@ -137,9 +384,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(new_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_topleft__invalid_value(self): + """Ensures the topleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.topleft = value + + def test_topleft__del(self): + """Ensures the topleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.topleft + def test_bottomleft(self): """Changing the bottomleft attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) new_bottomleft = (r.left + 20, r.bottom + 30) @@ -151,9 +413,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_bottomleft__invalid_value(self): + """Ensures the bottomleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottomleft = value + + def test_bottomleft__del(self): + """Ensures the bottomleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottomleft + def test_topright(self): - """Changing the bottomleft attribute moves the rect and does not change - the rect's size + """Changing the topright attribute moves the rect and does not change + the rect's size """ r = Rect(1, 2, 3, 4) new_topright = (r.right + 20, r.top + 30) @@ -165,9 +442,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_topright__invalid_value(self): + """Ensures the topright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.topright = value + + def test_topright__del(self): + """Ensures the topright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.topright + def test_bottomright(self): """Changing the bottomright attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) new_bottomright = (r.right + 20, r.bottom + 30) @@ -179,9 +471,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_bottomright__invalid_value(self): + """Ensures the bottomright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.bottomright = value + + def test_bottomright__del(self): + """Ensures the bottomright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.bottomright + def test_center(self): """Changing the center attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) new_center = (r.centerx + 20, r.centery + 30) @@ -193,9 +500,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_center__invalid_value(self): + """Ensures the center attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.center = value + + def test_center__del(self): + """Ensures the center attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.center + def test_midleft(self): """Changing the midleft attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) new_midleft = (r.left + 20, r.centery + 30) @@ -207,12 +529,27 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_midleft__invalid_value(self): + """Ensures the midleft attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midleft = value + + def test_midleft__del(self): + """Ensures the midleft attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midleft + def test_midright(self): """Changing the midright attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) - new_midright= (r.right + 20, r.centery + 30) + new_midright = (r.right + 20, r.centery + 30) expected_topleft = (r.left + 20, r.top + 30) old_size = r.size @@ -221,12 +558,27 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_midright__invalid_value(self): + """Ensures the midright attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midright = value + + def test_midright__del(self): + """Ensures the midright attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midright + def test_midtop(self): """Changing the midtop attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) - new_midtop= (r.centerx + 20, r.top + 30) + new_midtop = (r.centerx + 20, r.top + 30) expected_topleft = (r.left + 20, r.top + 30) old_size = r.size @@ -235,9 +587,24 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_midtop__invalid_value(self): + """Ensures the midtop attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midtop = value + + def test_midtop__del(self): + """Ensures the midtop attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midtop + def test_midbottom(self): """Changing the midbottom attribute moves the rect and does not change - the rect's size + the rect's size """ r = Rect(1, 2, 3, 4) new_midbottom = (r.centerx + 20, r.bottom + 30) @@ -249,9 +616,23 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(expected_topleft, r.topleft) self.assertEqual(old_size, r.size) + def test_midbottom__invalid_value(self): + """Ensures the midbottom attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.midbottom = value + + def test_midbottom__del(self): + """Ensures the midbottom attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.midbottom + def test_width(self): - """Changing the width resizes the rect from the top-left corner - """ + """Changing the width resizes the rect from the top-left corner""" r = Rect(1, 2, 3, 4) new_width = 10 old_topleft = r.topleft @@ -262,9 +643,23 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(old_height, r.height) self.assertEqual(old_topleft, r.topleft) + def test_width__invalid_value(self): + """Ensures the width attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.width = value + + def test_width__del(self): + """Ensures the width attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.width + def test_height(self): - """Changing the height resizes the rect from the top-left corner - """ + """Changing the height resizes the rect from the top-left corner""" r = Rect(1, 2, 3, 4) new_height = 10 old_topleft = r.topleft @@ -275,9 +670,23 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(old_width, r.width) self.assertEqual(old_topleft, r.topleft) + def test_height__invalid_value(self): + """Ensures the height attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.height = value + + def test_height__del(self): + """Ensures the height attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.height + def test_size(self): - """Changing the size resizes the rect from the top-left corner - """ + """Changing the size resizes the rect from the top-left corner""" r = Rect(1, 2, 3, 4) new_size = (10, 20) old_topleft = r.topleft @@ -286,46 +695,87 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(new_size, r.size) self.assertEqual(old_topleft, r.topleft) + def test_size__invalid_value(self): + """Ensures the size attribute handles invalid values correctly.""" + r = Rect(0, 0, 1, 1) + + for value in (None, [], "1", 1, (1,), [1, 2, 3]): + with self.assertRaises(TypeError): + r.size = value + + def test_size__del(self): + """Ensures the size attribute can't be deleted.""" + r = Rect(0, 0, 1, 1) + + with self.assertRaises(AttributeError): + del r.size + def test_contains(self): r = Rect(1, 2, 3, 4) - self.assertTrue(r.contains(Rect(2, 3, 1, 1)), - "r does not contain Rect(2, 3, 1, 1)") - self.assertTrue(r.contains(Rect(r)), - "r does not contain the same rect as itself") - self.assertTrue(r.contains(Rect(2, 3, 0, 0)), - "r does not contain an empty rect within its bounds") - self.assertFalse(r.contains(Rect(0, 0, 1, 2)), - "r contains Rect(0, 0, 1, 2)") - self.assertFalse(r.contains(Rect(4, 6, 1, 1)), - "r contains Rect(4, 6, 1, 1)") - self.assertFalse(r.contains(Rect(4, 6, 0, 0)), - "r contains Rect(4, 6, 0, 0)") + self.assertTrue( + r.contains(Rect(2, 3, 1, 1)), "r does not contain Rect(2, 3, 1, 1)" + ) + self.assertTrue(Rect(2, 3, 1, 1) in r, "r does not contain Rect(2, 3, 1, 1) 2") + self.assertTrue( + r.contains(Rect(r)), "r does not contain the same rect as itself" + ) + self.assertTrue(r in Rect(r), "r does not contain the same rect as itself") + self.assertTrue( + r.contains(Rect(2, 3, 0, 0)), + "r does not contain an empty rect within its bounds", + ) + self.assertTrue( + Rect(2, 3, 0, 0) in r, + "r does not contain an empty rect within its bounds", + ) + self.assertFalse(r.contains(Rect(0, 0, 1, 2)), "r contains Rect(0, 0, 1, 2)") + self.assertFalse(r.contains(Rect(4, 6, 1, 1)), "r contains Rect(4, 6, 1, 1)") + self.assertFalse(r.contains(Rect(4, 6, 0, 0)), "r contains Rect(4, 6, 0, 0)") + self.assertFalse(Rect(0, 0, 1, 2) in r, "r contains Rect(0, 0, 1, 2)") + self.assertFalse(Rect(4, 6, 1, 1) in r, "r contains Rect(4, 6, 1, 1)") + self.assertFalse(Rect(4, 6, 0, 0) in r, "r contains Rect(4, 6, 0, 0)") + self.assertTrue(2 in Rect(0, 0, 1, 2), "r does not contain 2") + self.assertFalse(3 in Rect(0, 0, 1, 2), "r contains 3") + + self.assertRaises(TypeError, lambda: "string" in Rect(0, 0, 1, 2)) + self.assertRaises(TypeError, lambda: 4 + 3j in Rect(0, 0, 1, 2)) def test_collidepoint(self): r = Rect(1, 2, 3, 4) - self.assertTrue(r.collidepoint(r.left, r.top), - "r does not collide with point (left, top)") - self.assertFalse(r.collidepoint(r.left - 1, r.top), - "r collides with point (left - 1, top)") - self.assertFalse(r.collidepoint(r.left, r.top - 1), - "r collides with point (left, top - 1)") - self.assertFalse(r.collidepoint(r.left - 1, r.top - 1), - "r collides with point (left - 1, top - 1)") - - self.assertTrue(r.collidepoint(r.right - 1, r.bottom - 1), - "r does not collide with point (right - 1, bottom - 1)") - self.assertFalse(r.collidepoint(r.right, r.bottom), - "r collides with point (right, bottom)") - self.assertFalse(r.collidepoint(r.right - 1, r.bottom), - "r collides with point (right - 1, bottom)") - self.assertFalse(r.collidepoint(r.right, r.bottom - 1), - "r collides with point (right, bottom - 1)") + self.assertTrue( + r.collidepoint(r.left, r.top), "r does not collide with point (left, top)" + ) + self.assertFalse( + r.collidepoint(r.left - 1, r.top), "r collides with point (left - 1, top)" + ) + self.assertFalse( + r.collidepoint(r.left, r.top - 1), "r collides with point (left, top - 1)" + ) + self.assertFalse( + r.collidepoint(r.left - 1, r.top - 1), + "r collides with point (left - 1, top - 1)", + ) + + self.assertTrue( + r.collidepoint(r.right - 1, r.bottom - 1), + "r does not collide with point (right - 1, bottom - 1)", + ) + self.assertFalse( + r.collidepoint(r.right, r.bottom), "r collides with point (right, bottom)" + ) + self.assertFalse( + r.collidepoint(r.right - 1, r.bottom), + "r collides with point (right - 1, bottom)", + ) + self.assertFalse( + r.collidepoint(r.right, r.bottom - 1), + "r collides with point (right, bottom - 1)", + ) def test_inflate__larger(self): - """The inflate method inflates around the center of the rectangle - """ + """The inflate method inflates around the center of the rectangle""" r = Rect(2, 4, 6, 8) r2 = r.inflate(4, 6) @@ -338,8 +788,7 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(r.height + 6, r2.height) def test_inflate__smaller(self): - """The inflate method inflates around the center of the rectangle - """ + """The inflate method inflates around the center of the rectangle""" r = Rect(2, 4, 6, 8) r2 = r.inflate(-4, -6) @@ -352,8 +801,7 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(r.height - 6, r2.height) def test_inflate_ip__larger(self): - """The inflate_ip method inflates around the center of the rectangle - """ + """The inflate_ip method inflates around the center of the rectangle""" r = Rect(2, 4, 6, 8) r2 = Rect(r) r2.inflate_ip(-4, -6) @@ -367,8 +815,7 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(r.height - 6, r2.height) def test_inflate_ip__smaller(self): - """The inflate method inflates around the center of the rectangle - """ + """The inflate method inflates around the center of the rectangle""" r = Rect(2, 4, 6, 8) r2 = Rect(r) r2.inflate_ip(-4, -6) @@ -381,6 +828,258 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(r.width - 4, r2.width) self.assertEqual(r.height - 6, r2.height) + def test_scale_by__larger_single_argument(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = r.scale_by(2) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.top - 4, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.bottom + 4, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 2, r2.height) + + def test_scale_by__larger_single_argument_kwarg(self): + """The scale method scales around the center of the rectangle using + keyword arguments 'x' and 'y'""" + r = Rect(2, 4, 6, 8) + r2 = r.scale_by(x=2) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.top - 4, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.bottom + 4, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 2, r2.height) + + def test_scale_by__smaller_single_argument(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 8, 8) + r2 = r.scale_by(0.5) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 2, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 2, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height - 4, r2.height) + + def test_scale_by__larger(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + r2 = r.scale_by(2, 4) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by__larger_kwargs_scale_by(self): + """ + The scale method scales around the center of the rectangle + Uses 'scale_by' kwarg. + """ + # arrange + r = Rect(2, 4, 6, 8) + # act + r2 = r.scale_by(scale_by=(2, 4)) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by__larger_kwargs(self): + """ + The scale method scales around the center of the rectangle + Uses 'x' and 'y' kwargs. + """ + # arrange + r = Rect(2, 4, 6, 8) + # act + r2 = r.scale_by(x=2, y=4) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by__smaller(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 8, 8) + # act + r2 = r.scale_by(0.5, 0.25) + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.centery - r.h / 4 / 2, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.centery + r.h / 4 / 2, r2.bottom) + self.assertEqual(r.width - 4, r2.width) + self.assertEqual(r.height // 4, r2.height) + + def test_scale_by__subzero(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r.scale_by(0) + r.scale_by(-1) + r.scale_by(-0.000001) + r.scale_by(0.00001) + + rx1 = r.scale_by(10, 1) + self.assertEqual(r.centerx - r.w * 10 / 2, rx1.x) + self.assertEqual(r.y, rx1.y) + self.assertEqual(r.w * 10, rx1.w) + self.assertEqual(r.h, rx1.h) + rx2 = r.scale_by(-10, 1) + self.assertEqual(rx1.x, rx2.x) + self.assertEqual(rx1.y, rx2.y) + self.assertEqual(rx1.w, rx2.w) + self.assertEqual(rx1.h, rx2.h) + + ry1 = r.scale_by(1, 10) + self.assertEqual(r.x, ry1.x) + self.assertEqual(r.centery - r.h * 10 / 2, ry1.y) + self.assertEqual(r.w, ry1.w) + self.assertEqual(r.h * 10, ry1.h) + ry2 = r.scale_by(1, -10) + self.assertEqual(ry1.x, ry2.x) + self.assertEqual(ry1.y, ry2.y) + self.assertEqual(ry1.w, ry2.w) + self.assertEqual(ry1.h, ry2.h) + + r1 = r.scale_by(10) + self.assertEqual(r.centerx - r.w * 10 / 2, r1.x) + self.assertEqual(r.centery - r.h * 10 / 2, r1.y) + self.assertEqual(r.w * 10, r1.w) + self.assertEqual(r.h * 10, r1.h) + + def test_scale_by_identity(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(1, 1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_negative_identity(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(-1, -1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_identity_single_argument(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_negative_identity_single_argment(self): + """The scale method scales around the center of the rectangle""" + # arrange + r = Rect(2, 4, 6, 8) + # act + actual = r.scale_by(-1) + # assert + self.assertEqual(r.x, actual.x) + self.assertEqual(r.y, actual.y) + self.assertEqual(r.w, actual.w) + self.assertEqual(r.h, actual.h) + + def test_scale_by_ip__larger(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.scale_by_ip(2) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.top - 4, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.bottom + 4, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 2, r2.height) + + def test_scale_by_ip__smaller(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 8, 8) + r2 = Rect(r) + r2.scale_by_ip(0.5) + + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left + 2, r2.left) + self.assertEqual(r.top + 2, r2.top) + self.assertEqual(r.right - 2, r2.right) + self.assertEqual(r.bottom - 2, r2.bottom) + self.assertEqual(r.width / 2, r2.width) + self.assertEqual(r.height / 2, r2.height) + + def test_scale_by_ip__subzero(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r.scale_by_ip(0) + r.scale_by_ip(-1) + r.scale_by_ip(-0.000001) + r.scale_by_ip(0.00001) + + def test_scale_by_ip__kwargs(self): + """The scale method scales around the center of the rectangle""" + r = Rect(2, 4, 6, 8) + r2 = Rect(r) + r2.scale_by_ip(x=2, y=4) + + # assert + self.assertEqual(r.center, r2.center) + self.assertEqual(r.left - 3, r2.left) + self.assertEqual(r.centery - r.h * 4 / 2, r2.top) + self.assertEqual(r.right + 3, r2.right) + self.assertEqual(r.centery + r.h * 4 / 2, r2.bottom) + self.assertEqual(r.width * 2, r2.width) + self.assertEqual(r.height * 4, r2.height) + + def test_scale_by_ip__kwarg_exceptions(self): + """The scale method scales around the center of the rectangle using + keyword argument 'scale_by'. Tests for incorrect keyword args""" + r = Rect(2, 4, 6, 8) + + with self.assertRaises(TypeError): + r.scale_by_ip(scale_by=2) + + with self.assertRaises(TypeError): + r.scale_by_ip(scale_by=(1, 2), y=1) + def test_clamp(self): r = Rect(10, 10, 10, 10) c = Rect(19, 12, 5, 5).clamp(r) @@ -406,201 +1105,1532 @@ class RectTypeTest(unittest.TestCase): def test_clip(self): r1 = Rect(1, 2, 3, 4) - self.assertEqual(Rect(1, 2, 2, 2), r1.clip( Rect(0, 0, 3, 4))) - self.assertEqual(Rect(2, 2, 2, 4), r1.clip( Rect(2, 2, 10, 20))) + self.assertEqual(Rect(1, 2, 2, 2), r1.clip(Rect(0, 0, 3, 4))) + self.assertEqual(Rect(2, 2, 2, 4), r1.clip(Rect(2, 2, 10, 20))) self.assertEqual(Rect(2, 3, 1, 2), r1.clip(Rect(2, 3, 1, 2))) self.assertEqual((0, 0), r1.clip(20, 30, 5, 6).size) - self.assertEqual(r1, r1.clip(Rect(r1)), - "r1 does not clip an identical rect to itself") + self.assertEqual( + r1, r1.clip(Rect(r1)), "r1 does not clip an identical rect to itself" + ) - def test_move(self): - r = Rect(1, 2, 3, 4) - move_x = 10 - move_y = 20 - r2 = r.move(move_x, move_y) - expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) - self.assertEqual(expected_r2, r2) + def test_clipline(self): + """Ensures clipline handles four int parameters. - def test_move_ip(self): - r = Rect(1, 2, 3, 4) - r2 = Rect(r) - move_x = 10 - move_y = 20 - r2.move_ip(move_x, move_y) - expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) - self.assertEqual(expected_r2, r2) + Tests the clipline(x1, y1, x2, y2) format. + """ + rect = Rect((1, 2), (35, 40)) + x1 = 5 + y1 = 6 + x2 = 11 + y2 = 19 + expected_line = ((x1, y1), (x2, y2)) - def test_union(self): - r1 = Rect(1, 1, 1, 2) - r2 = Rect(-2, -2, 1, 2) - self.assertEqual(Rect(-2, -2, 4, 5), r1.union(r2)) + clipped_line = rect.clipline(x1, y1, x2, y2) - def test_union__with_identical_Rect(self): - r1 = Rect(1, 2, 3, 4) - self.assertEqual(r1, r1.union(Rect(r1))) + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - def test_union_ip(self): - r1 = Rect(1, 1, 1, 2) - r2 = Rect(-2, -2, 1, 2) - r1.union_ip(r2) - self.assertEqual(Rect(-2, -2, 4, 5), r1) + def test_clipline__two_sequences(self): + """Ensures clipline handles a sequence of two sequences. - def test_unionall(self): - r1 = Rect(0, 0, 1, 1) - r2 = Rect(-2, -2, 1, 1) - r3 = Rect(2, 2, 1, 1) + Tests the clipline((x1, y1), (x2, y2)) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) - r4 = r1.unionall([r2, r3]) - self.assertEqual(Rect(-2, -2, 5, 5), r4) + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) - def test_unionall_ip(self): - r1 = Rect(0, 0, 1, 1) - r2 = Rect(-2, -2, 1, 1) - r3 = Rect(2, 2, 1, 1) + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) - r1.unionall_ip([r2, r3]) - self.assertEqual(Rect(-2, -2, 5, 5), r1) + for inner_seq2 in INNER_SEQUENCES: + clipped_line = rect.clipline((endpt1, inner_seq2(pt2))) - # Bug for an empty list. Would return a Rect instead of None. - self.assertTrue(r1.unionall_ip([]) is None) + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - def test_colliderect(self): - r1 = Rect(1, 2, 3, 4) - self.assertTrue(r1.colliderect(Rect(0, 0, 2, 3)), - "r1 does not collide with Rect(0, 0, 2, 3)") - self.assertFalse(r1.colliderect(Rect(0, 0, 1, 2)), - "r1 collides with Rect(0, 0, 1, 2)") - self.assertFalse(r1.colliderect(Rect(r1.right, r1.bottom, 2, 2)), - "r1 collides with Rect(r1.right, r1.bottom, 2, 2)") - self.assertTrue(r1.colliderect(Rect(r1.left + 1, r1.top + 1, - r1.width - 2, r1.height - 2)), - "r1 does not collide with Rect(r1.left + 1, r1.top + 1, "+ - "r1.width - 2, r1.height - 2)") - self.assertTrue(r1.colliderect(Rect(r1.left - 1, r1.top - 1, - r1.width + 2, r1.height + 2)), - "r1 does not collide with Rect(r1.left - 1, r1.top - 1, "+ - "r1.width + 2, r1.height + 2)") - self.assertTrue(r1.colliderect(Rect(r1)), - "r1 does not collide with an identical rect") - self.assertFalse(r1.colliderect(Rect(r1.right, r1.bottom, 0, 0)), - "r1 collides with Rect(r1.right, r1.bottom, 0, 0)") - self.assertFalse(r1.colliderect(Rect(r1.right, r1.bottom, 1, 1)), - "r1 collides with Rect(r1.right, r1.bottom, 1, 1)") + def test_clipline__two_sequences_kwarg(self): + """Ensures clipline handles a sequence of two sequences using kwargs. - def testEquals(self): - """ check to see how the rect uses __eq__ + Tests the clipline((x1, y1), (x2, y2)) format. + Tests the sequences as different types. """ - r1 = Rect(1, 2, 3, 4) - r2 = Rect(10, 20, 30, 40) - r3 = (10, 20, 30, 40) - r4 = Rect(10, 20, 30, 40) + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) - class foo (Rect): - def __eq__(self, other): - return id(self) == id(other) - def __ne__(self, other): - return id(self) != id(other) + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) - class foo2 (Rect): - pass + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) - r5 = foo(10, 20, 30, 40) - r6 = foo2(10, 20, 30, 40) + for inner_seq2 in INNER_SEQUENCES: + clipped_line = rect.clipline( + first_coordinate=endpt1, second_coordinate=inner_seq2(pt2) + ) - self.assertNotEqual(r5, r2) + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - # because we define equality differently for this subclass. - self.assertEqual(r6, r2) + def test_clipline__sequence_of_four_ints(self): + """Ensures clipline handles a sequence of four ints. + Tests the clipline((x1, y1, x2, y2)) format. + Tests the sequence as different types. + """ + rect = Rect((1, 2), (35, 40)) + line = (5, 6, 11, 19) + expected_line = ((line[0], line[1]), (line[2], line[3])) - rect_list = [r1, r2, r3, r4, r6] + for outer_seq in (list, tuple): + clipped_line = rect.clipline(outer_seq(line)) - # see if we can remove 4 of these. - rect_list.remove(r2) - rect_list.remove(r2) - rect_list.remove(r2) - rect_list.remove(r2) - self.assertRaises(ValueError, rect_list.remove, r2) + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - def test_collidedict(self): + def test_clipline__sequence_of_four_ints_kwargs(self): + """Ensures clipline handles a sequence of four ints using kwargs. - # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidedict: + Tests the clipline((x1, y1, x2, y2)) format. + Tests the sequence as different types. + """ + rect = Rect((1, 2), (35, 40)) + line = (5, 6, 11, 19) + expected_line = ((line[0], line[1]), (line[2], line[3])) - # Rect.collidedict(dict): return (key, value) - # test if one rectangle in a dictionary intersects - # - # Returns the key and value of the first dictionary value that - # collides with the Rect. If no collisions are found, None is - # returned. - # - # Rect objects are not hashable and cannot be used as keys in a - # dictionary, only as values. + for outer_seq in (list, tuple): + clipped_line = rect.clipline(rect_arg=outer_seq(line)) - r = Rect(1, 1, 10, 10) - r1 = Rect(1, 1, 10, 10) - r2 = Rect(50, 50, 10, 10) - r3 = Rect(70, 70, 10, 10) - r4 = Rect(61, 61, 10, 10) + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - d = {1: r1, 2: r2, 3: r3} + def test_clipline__sequence_of_two_sequences(self): + """Ensures clipline handles a sequence of two sequences. - rects_values = 1 - val = r.collidedict(d, rects_values) - self.assertTrue(val) - self.assertEqual(len(val), 2) - self.assertEqual(val[0], 1) - self.assertEqual(val[1], r1) + Tests the clipline(((x1, y1), (x2, y2))) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) - none_d = {2: r2, 3: r3} - none_val = r.collidedict(none_d, rects_values) - self.assertFalse(none_val) + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) - barely_d = {1: r1, 2: r2, 3: r3} - k3, v3 = r4.collidedict(barely_d, rects_values) - self.assertEqual(k3, 3) - self.assertEqual(v3, r3) + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) + for inner_seq2 in INNER_SEQUENCES: + endpt2 = inner_seq2(pt2) - def test_collidedictall(self): + for outer_seq in (list, tuple): + clipped_line = rect.clipline(outer_seq((endpt1, endpt2))) - # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidedictall: + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - # Rect.collidedictall(dict): return [(key, value), ...] - # test if all rectangles in a dictionary intersect - # - # Returns a list of all the key and value pairs that intersect with - # the Rect. If no collisions are found an empty dictionary is - # returned. - # - # Rect objects are not hashable and cannot be used as keys in a - # dictionary, only as values. + def test_clipline__sequence_of_two_sequences_kwargs(self): + """Ensures clipline handles a sequence of two sequences using kwargs. - r = Rect(1, 1, 10, 10) + Tests the clipline(((x1, y1), (x2, y2))) format. + Tests the sequences as different types. + """ + rect = Rect((1, 2), (35, 40)) + pt1 = (5, 6) + pt2 = (11, 19) - r2 = Rect(1, 1, 10, 10) - r3 = Rect(5, 5, 10, 10) - r4 = Rect(10, 10, 10, 10) - r5 = Rect(50, 50, 10, 10) + INNER_SEQUENCES = (list, tuple, Vector2) + expected_line = (pt1, pt2) - rects_values = 1 - d = {2: r2} - l = r.collidedictall(d, rects_values) - self.assertEqual(l, [(2, r2)]) + for inner_seq1 in INNER_SEQUENCES: + endpt1 = inner_seq1(pt1) - d2 = {2: r2, 3: r3, 4: r4, 5: r5} - l2 = r.collidedictall(d2, rects_values) - self.assertEqual(l2, [(2, r2), (3, r3), (4, r4)]) + for inner_seq2 in INNER_SEQUENCES: + endpt2 = inner_seq2(pt2) - def test_collidelist(self): + for outer_seq in (list, tuple): + clipped_line = rect.clipline(x1=outer_seq((endpt1, endpt2))) - # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelist: + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) - # Rect.collidelist(list): return index - # test if one rectangle in a list intersects - # - # Test whether the rectangle collides with any in a sequence of - # rectangles. The index of the first collision found is returned. If - # no collisions are found an index of -1 is returned. + def test_clipline__floats(self): + """Ensures clipline handles float parameters.""" + rect = Rect((1, 2), (35, 40)) + x1 = 5.9 + y1 = 6.9 + x2 = 11.9 + y2 = 19.9 + + # Floats are truncated. + expected_line = ( + (math.floor(x1), math.floor(y1)), + (math.floor(x2), math.floor(y2)), + ) + + clipped_line = rect.clipline(x1, y1, x2, y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__floats_kwargs(self): + """Ensures clipline handles four float parameters. + + Tests the clipline(x1, y1, x2, y2) format. + """ + rect = Rect((1, 2), (35, 40)) + x1 = 5.9 + y1 = 6.9 + x2 = 11.9 + y2 = 19.9 + + # Floats are truncated. + expected_line = ( + (math.floor(x1), math.floor(y1)), + (math.floor(x2), math.floor(y2)), + ) + + clipped_line = rect.clipline(x1=x1, x2=y1, x3=x2, x4=y2) + + self.assertIsInstance(clipped_line, tuple) + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__kwarg_exceptions(self): + """Ensure clipline handles incorrect keyword arguments""" + r = Rect(2, 4, 6, 8) + + with self.assertRaises(TypeError): + r.clipline(x1=0) + + with self.assertRaises(TypeError): + r.clipline(first_coordinate=(1, 3, 5, 4), second_coordinate=(1, 2)) + + with self.assertRaises(TypeError): + r.clipline(first_coordinate=(1, 3), second_coordinate=(2, 2), x1=1) + + with self.assertRaises(TypeError): + r.clipline(rect_arg=(1, 3, 5)) + + with self.assertRaises(TypeError): + r.clipline(rect_arg=(1, 3, 5, 4), second_coordinate=(2, 2)) + + def test_clipline__no_overlap(self): + """Ensures lines that do not overlap the rect are not clipped.""" + rect = Rect((10, 25), (15, 20)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + lines = ( + (big_rect.bottomleft, big_rect.topleft), # Left edge. + (big_rect.topleft, big_rect.topright), # Top edge. + (big_rect.topright, big_rect.bottomright), # Right edge. + (big_rect.bottomright, big_rect.bottomleft), + ) # Bottom edge. + expected_line = () + + # Test lines outside rect. + for line in lines: + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__both_endpoints_outside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with both endpoints outside the rect. + """ + rect = Rect((0, 0), (20, 20)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + + # Create a dict of lines and expected results. + line_dict = { + (big_rect.midleft, big_rect.midright): ( + rect.midleft, + (rect.midright[0] - 1, rect.midright[1]), + ), + (big_rect.midtop, big_rect.midbottom): ( + rect.midtop, + (rect.midbottom[0], rect.midbottom[1] - 1), + ), + # Diagonals. + (big_rect.topleft, big_rect.bottomright): ( + rect.topleft, + (rect.bottomright[0] - 1, rect.bottomright[1] - 1), + ), + # This line needs a small adjustment to make sure it intersects + # the rect correctly. + ( + (big_rect.topright[0] - 1, big_rect.topright[1]), + (big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), + ): ( + (rect.topright[0] - 1, rect.topright[1]), + (rect.bottomleft[0], rect.bottomleft[1] - 1), + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__both_endpoints_inside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with both endpoints inside the rect. + """ + rect = Rect((-10, -5), (20, 20)) + # Use a smaller rect to help create test lines. + small_rect = rect.inflate(-2, -2) + + lines = ( + (small_rect.midleft, small_rect.midright), + (small_rect.midtop, small_rect.midbottom), + # Diagonals. + (small_rect.topleft, small_rect.bottomright), + (small_rect.topright, small_rect.bottomleft), + ) + + for line in lines: + expected_line = line + + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__endpoints_inside_and_outside(self): + """Ensures lines that overlap the rect are clipped. + + Testing lines with one endpoint outside the rect and the other is + inside the rect. + """ + rect = Rect((0, 0), (21, 21)) + # Use a bigger rect to help create test lines. + big_rect = rect.inflate(2, 2) + + # Create a dict of lines and expected results. + line_dict = { + (big_rect.midleft, rect.center): (rect.midleft, rect.center), + (big_rect.midtop, rect.center): (rect.midtop, rect.center), + (big_rect.midright, rect.center): ( + (rect.midright[0] - 1, rect.midright[1]), + rect.center, + ), + (big_rect.midbottom, rect.center): ( + (rect.midbottom[0], rect.midbottom[1] - 1), + rect.center, + ), + # Diagonals. + (big_rect.topleft, rect.center): (rect.topleft, rect.center), + (big_rect.topright, rect.center): ( + (rect.topright[0] - 1, rect.topright[1]), + rect.center, + ), + (big_rect.bottomright, rect.center): ( + (rect.bottomright[0] - 1, rect.bottomright[1] - 1), + rect.center, + ), + # This line needs a small adjustment to make sure it intersects + # the rect correctly. + ((big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), rect.center): ( + (rect.bottomleft[0], rect.bottomleft[1] - 1), + rect.center, + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__edges(self): + """Ensures clipline properly clips line that are along the rect edges.""" + rect = Rect((10, 25), (15, 20)) + + # Create a dict of edges and expected results. + edge_dict = { + # Left edge. + (rect.bottomleft, rect.topleft): ( + (rect.bottomleft[0], rect.bottomleft[1] - 1), + rect.topleft, + ), + # Top edge. + (rect.topleft, rect.topright): ( + rect.topleft, + (rect.topright[0] - 1, rect.topright[1]), + ), + # Right edge. + (rect.topright, rect.bottomright): (), + # Bottom edge. + (rect.bottomright, rect.bottomleft): (), + } + + for edge, expected_line in edge_dict.items(): + clipped_line = rect.clipline(edge) + + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + if expected_line: + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((edge[1], edge[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__equal_endpoints_with_overlap(self): + """Ensures clipline handles lines with both endpoints the same. + + Testing lines that overlap the rect. + """ + rect = Rect((10, 25), (15, 20)) + + # Test all the points in and on a rect. + pts = ( + (x, y) + for x in range(rect.left, rect.right) + for y in range(rect.top, rect.bottom) + ) + + for pt in pts: + expected_line = (pt, pt) + + clipped_line = rect.clipline((pt, pt)) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__equal_endpoints_no_overlap(self): + """Ensures clipline handles lines with both endpoints the same. + + Testing lines that do not overlap the rect. + """ + expected_line = () + rect = Rect((10, 25), (15, 20)) + + # Test points outside rect. + for pt in test_utils.rect_perimeter_pts(rect.inflate(2, 2)): + clipped_line = rect.clipline((pt, pt)) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__zero_size_rect(self): + """Ensures clipline handles zero sized rects correctly.""" + expected_line = () + + for size in ((0, 15), (15, 0), (0, 0)): + rect = Rect((10, 25), size) + + clipped_line = rect.clipline(rect.topleft, rect.topleft) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__negative_size_rect(self): + """Ensures clipline handles negative sized rects correctly.""" + expected_line = () + + for size in ((-15, 20), (15, -20), (-15, -20)): + rect = Rect((10, 25), size) + norm_rect = rect.copy() + norm_rect.normalize() + # Use a bigger rect to help create test lines. + big_rect = norm_rect.inflate(2, 2) + + # Create a dict of lines and expected results. Some line have both + # endpoints outside the rect and some have one inside and one + # outside. + line_dict = { + (big_rect.midleft, big_rect.midright): ( + norm_rect.midleft, + (norm_rect.midright[0] - 1, norm_rect.midright[1]), + ), + (big_rect.midtop, big_rect.midbottom): ( + norm_rect.midtop, + (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1), + ), + (big_rect.midleft, norm_rect.center): ( + norm_rect.midleft, + norm_rect.center, + ), + (big_rect.midtop, norm_rect.center): ( + norm_rect.midtop, + norm_rect.center, + ), + (big_rect.midright, norm_rect.center): ( + (norm_rect.midright[0] - 1, norm_rect.midright[1]), + norm_rect.center, + ), + (big_rect.midbottom, norm_rect.center): ( + (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1), + norm_rect.center, + ), + } + + for line, expected_line in line_dict.items(): + clipped_line = rect.clipline(line) + + # Make sure rect wasn't normalized. + self.assertNotEqual(rect, norm_rect) + self.assertTupleEqual(clipped_line, expected_line) + + # Swap endpoints to test for symmetry. + expected_line = (expected_line[1], expected_line[0]) + + clipped_line = rect.clipline((line[1], line[0])) + + self.assertTupleEqual(clipped_line, expected_line) + + def test_clipline__invalid_line(self): + """Ensures clipline handles invalid lines correctly.""" + rect = Rect((0, 0), (10, 20)) + invalid_lines = ( + (), + (1,), + (1, 2), + (1, 2, 3), + (1, 2, 3, 4, 5), + ((1, 2),), + ((1, 2), (3,)), + ((1, 2), 3), + ((1, 2, 5), (3, 4)), + ((1, 2), (3, 4, 5)), + ((1, 2), (3, 4), (5, 6)), + ) + + for line in invalid_lines: + with self.assertRaises(TypeError): + clipped_line = rect.clipline(line) + + with self.assertRaises(TypeError): + clipped_line = rect.clipline(*line) + + def test_move(self): + r = Rect(1, 2, 3, 4) + move_x = 10 + move_y = 20 + r2 = r.move(move_x, move_y) + expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) + self.assertEqual(expected_r2, r2) + + def test_move_ip(self): + r = Rect(1, 2, 3, 4) + r2 = Rect(r) + move_x = 10 + move_y = 20 + r2.move_ip(move_x, move_y) + expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height) + self.assertEqual(expected_r2, r2) + + @unittest.skipIf( + IS_PYPY, "fails on pypy (but only for: bottom, right, centerx, centery)" + ) + def test_set_float_values(self): + zero = 0 + pos = 124 + neg = -432 + # (initial, increment, expected, other) + data_rows = [ + (zero, 0.1, zero, _random_int()), + (zero, 0.4, zero, _random_int()), + (zero, 0.5, zero + 1, _random_int()), + (zero, 1.1, zero + 1, _random_int()), + (zero, 1.5, zero + 2, _random_int()), # >0f + (zero, -0.1, zero, _random_int()), + (zero, -0.4, zero, _random_int()), + (zero, -0.5, zero - 1, _random_int()), + (zero, -0.6, zero - 1, _random_int()), + (zero, -1.6, zero - 2, _random_int()), # <0f + (zero, 1, zero + 1, _random_int()), + (zero, 4, zero + 4, _random_int()), # >0i + (zero, -1, zero - 1, _random_int()), + (zero, -4, zero - 4, _random_int()), # <0i + (pos, 0.1, pos, _random_int()), + (pos, 0.4, pos, _random_int()), + (pos, 0.5, pos + 1, _random_int()), + (pos, 1.1, pos + 1, _random_int()), + (pos, 1.5, pos + 2, _random_int()), # >0f + (pos, -0.1, pos, _random_int()), + (pos, -0.4, pos, _random_int()), + (pos, -0.5, pos, _random_int()), + (pos, -0.6, pos - 1, _random_int()), + (pos, -1.6, pos - 2, _random_int()), # <0f + (pos, 1, pos + 1, _random_int()), + (pos, 4, pos + 4, _random_int()), # >0i + (pos, -1, pos - 1, _random_int()), + (pos, -4, pos - 4, _random_int()), # <0i + (neg, 0.1, neg, _random_int()), + (neg, 0.4, neg, _random_int()), + (neg, 0.5, neg, _random_int()), + (neg, 1.1, neg + 1, _random_int()), + (neg, 1.5, neg + 1, _random_int()), # >0f + (neg, -0.1, neg, _random_int()), + (neg, -0.4, neg, _random_int()), + (neg, -0.5, neg - 1, _random_int()), + (neg, -0.6, neg - 1, _random_int()), + (neg, -1.6, neg - 2, _random_int()), # <0f + (neg, 1, neg + 1, _random_int()), + (neg, 4, neg + 4, _random_int()), # >0i + (neg, -1, neg - 1, _random_int()), + (neg, -4, neg - 4, _random_int()), # <0i + ] + + single_value_attribute_names = [ + "x", + "y", + "w", + "h", + "width", + "height", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + ] + + tuple_value_attribute_names = [ + "topleft", + "topright", + "bottomleft", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "size", + "center", + ] + + for row in data_rows: + initial, inc, expected, other = row + new_value = initial + inc + for attribute_name in single_value_attribute_names: + with self.subTest(row=row, name=f"r.{attribute_name}"): + actual = Rect( + _random_int(), _random_int(), _random_int(), _random_int() + ) + # act + setattr(actual, attribute_name, new_value) + # assert + self.assertEqual(expected, getattr(actual, attribute_name)) + + for attribute_name in tuple_value_attribute_names: + with self.subTest(row=row, name=f"r.{attribute_name}[0]"): + actual = Rect( + _random_int(), _random_int(), _random_int(), _random_int() + ) + # act + setattr(actual, attribute_name, (new_value, other)) + # assert + self.assertEqual((expected, other), getattr(actual, attribute_name)) + + with self.subTest(row=row, name=f"r.{attribute_name}[1]"): + actual = Rect( + _random_int(), _random_int(), _random_int(), _random_int() + ) + # act + setattr(actual, attribute_name, (other, new_value)) + # assert + self.assertEqual((other, expected), getattr(actual, attribute_name)) + + def test_set_out_of_range_number_raises_exception(self): + i = 0 + # (initial, expected) + data_rows = [ + (_int_max + 1, TypeError), + (_int_max + 0.00001, TypeError), + (_int_max, None), + (_int_max - 1, None), + (_int_max - 2, None), + (_int_max - 10, None), + (_int_max - 63, None), + (_int_max - 64, None), + (_int_max - 65, None), + (_int_min - 1, TypeError), + (_int_min - 0.00001, TypeError), + (_int_min, None), + (_int_min + 1, None), + (_int_min + 2, None), + (_int_min + 10, None), + (_int_min + 62, None), + (_int_min + 63, None), + (_int_min + 64, None), + (0, None), + (100000, None), + (-100000, None), + ] + + single_attribute_names = [ + "x", + "y", + "w", + "h", + "width", + "height", + "top", + "left", + "bottom", + "right", + "centerx", + "centery", + ] + + tuple_value_attribute_names = [ + "topleft", + "topright", + "bottomleft", + "bottomright", + "midtop", + "midleft", + "midbottom", + "midright", + "size", + "center", + ] + + for row in data_rows: + for attribute_name in single_attribute_names: + value, expected = row + with self.subTest(row=row, name=f"r.{attribute_name}"): + actual = Rect(0, 0, 0, 0) + if expected: + # act/ assert + self.assertRaises( + TypeError, setattr, actual, attribute_name, value + ) + else: + # act + setattr(actual, attribute_name, value) + # assert + self.assertEqual(value, getattr(actual, attribute_name)) + other = _random_int() + + for attribute_name in tuple_value_attribute_names: + value, expected = row + with self.subTest(row=row, name=f"r.{attribute_name}[0]"): + actual = Rect(0, 0, 0, 0) + # act/ assert + if expected: + # act/ assert + self.assertRaises( + TypeError, setattr, actual, attribute_name, (value, other) + ) + else: + # act + setattr(actual, attribute_name, (value, other)) + # assert + self.assertEqual( + (value, other), getattr(actual, attribute_name) + ) + with self.subTest(row=row, name=f"r.{attribute_name}[1]"): + actual = Rect(0, 0, 0, 0) + # act/ assert + if expected: + # act/ assert + self.assertRaises( + TypeError, setattr, actual, attribute_name, (other, value) + ) + else: + # act + setattr(actual, attribute_name, (other, value)) + # assert + self.assertEqual( + (other, value), getattr(actual, attribute_name) + ) + + def test_update_XYWidthHeight(self): + """Test update with 4 int values(x, y, w, h)""" + rect = Rect(0, 0, 1, 1) + rect.update(1, 2, 3, 4) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_update__TopLeftSize(self): + """Test update with 2 tuples((x, y), (w, h))""" + rect = Rect(0, 0, 1, 1) + rect.update((1, 2), (3, 4)) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_update__List(self): + """Test update with list""" + rect = Rect(0, 0, 1, 1) + rect2 = [1, 2, 3, 4] + rect.update(rect2) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_update__RectObject(self): + """Test update with other rect object""" + rect = Rect(0, 0, 1, 1) + rect2 = Rect(1, 2, 3, 4) + rect.update(rect2) + + self.assertEqual(1, rect.left) + self.assertEqual(2, rect.top) + self.assertEqual(3, rect.width) + self.assertEqual(4, rect.height) + + def test_union(self): + r1 = Rect(1, 1, 1, 2) + r2 = Rect(-2, -2, 1, 2) + self.assertEqual(Rect(-2, -2, 4, 5), r1.union(r2)) + + def test_union__with_identical_Rect(self): + r1 = Rect(1, 2, 3, 4) + self.assertEqual(r1, r1.union(Rect(r1))) + + def test_union_ip(self): + r1 = Rect(1, 1, 1, 2) + r2 = Rect(-2, -2, 1, 2) + r1.union_ip(r2) + self.assertEqual(Rect(-2, -2, 4, 5), r1) + + def test_unionall(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r4 = r1.unionall([r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r4) + + def test_unionall__invalid_rect_format(self): + """Ensures unionall correctly handles invalid rect parameters.""" + numbers = [0, 1.2, 2, 3.3] + strs = ["a", "b", "c"] + nones = [None, None] + + for invalid_rects in (numbers, strs, nones): + with self.assertRaises(TypeError): + Rect(0, 0, 1, 1).unionall(invalid_rects) + + def test_unionall__kwargs(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r4 = r1.unionall(rect=[r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r4) + + def test_unionall_ip(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r1.unionall_ip([r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r1) + + # Bug for an empty list. Would return a Rect instead of None. + self.assertTrue(r1.unionall_ip([]) is None) + + def test_unionall_ip__kwargs(self): + r1 = Rect(0, 0, 1, 1) + r2 = Rect(-2, -2, 1, 1) + r3 = Rect(2, 2, 1, 1) + + r1.unionall_ip(rects=[r2, r3]) + self.assertEqual(Rect(-2, -2, 5, 5), r1) + + # Bug for an empty list. Would return a Rect instead of None. + self.assertTrue(r1.unionall_ip([]) is None) + + def test_colliderect(self): + r1 = Rect(1, 2, 3, 4) + self.assertTrue( + r1.colliderect(Rect(0, 0, 2, 3)), + "r1 does not collide with Rect(0, 0, 2, 3)", + ) + self.assertFalse( + r1.colliderect(Rect(0, 0, 1, 2)), "r1 collides with Rect(0, 0, 1, 2)" + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 2, 2)), + "r1 collides with Rect(r1.right, r1.bottom, 2, 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1.left + 1, r1.top + 1, r1.width - 2, r1.height - 2)), + "r1 does not collide with Rect(r1.left + 1, r1.top + 1, " + + "r1.width - 2, r1.height - 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1.left - 1, r1.top - 1, r1.width + 2, r1.height + 2)), + "r1 does not collide with Rect(r1.left - 1, r1.top - 1, " + + "r1.width + 2, r1.height + 2)", + ) + self.assertTrue( + r1.colliderect(Rect(r1)), "r1 does not collide with an identical rect" + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 0, 0)), + "r1 collides with Rect(r1.right, r1.bottom, 0, 0)", + ) + self.assertFalse( + r1.colliderect(Rect(r1.right, r1.bottom, 1, 1)), + "r1 collides with Rect(r1.right, r1.bottom, 1, 1)", + ) + + def testEquals(self): + """check to see how the rect uses __eq__""" + r1 = Rect(1, 2, 3, 4) + r2 = Rect(10, 20, 30, 40) + r3 = (10, 20, 30, 40) + r4 = Rect(10, 20, 30, 40) + + class foo(Rect): + def __eq__(self, other): + return id(self) == id(other) + + def __ne__(self, other): + return id(self) != id(other) + + class foo2(Rect): + pass + + r5 = foo(10, 20, 30, 40) + r6 = foo2(10, 20, 30, 40) + + self.assertNotEqual(r5, r2) + + # because we define equality differently for this subclass. + self.assertEqual(r6, r2) + + rect_list = [r1, r2, r3, r4, r6] + + # see if we can remove 4 of these. + rect_list.remove(r2) + rect_list.remove(r2) + rect_list.remove(r2) + rect_list.remove(r2) + self.assertRaises(ValueError, rect_list.remove, r2) + + def test_collidedict(self): + """Ensures collidedict detects collisions.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_item = rect.collidedict(d, use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, expected_items) + + def test_collidedict__no_collision(self): + """Ensures collidedict returns None when no collisions.""" + rect = Rect(1, 1, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__barely_touching(self): + """Ensures collidedict works correctly for rects that barely touch.""" + rect = Rect(1, 1, 10, 10) + # Small rect to test barely touching collisions. + collide_rect = Rect(0, 0, 1, 1) + + collide_item1 = ("collide 1", collide_rect) + no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + no_collide_rect_values = dict( + (no_collide_item1, no_collide_item2, no_collide_item3) + ) + + # Dict to check collisions with keys. + no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()} + + # Tests the collide_rect on each of the rect's corners. + for attr in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(collide_rect, attr, getattr(rect, attr)) + + for use_values in (True, False): + if use_values: + expected_item = collide_item1 + d = dict(no_collide_rect_values) + else: + expected_item = (tuple(collide_item1[1]), collide_item1[0]) + d = dict(no_collide_rect_keys) + + d.update((expected_item,)) # Add in the expected item. + + collide_item = rect.collidedict(d, use_values) + + self.assertTupleEqual(collide_item, expected_item) + + def test_collidedict__zero_sized_rects(self): + """Ensures collidedict works correctly with zero sized rects. + + There should be no collisions with zero sized rects. + """ + zero_rect1 = Rect(1, 1, 0, 0) + zero_rect2 = Rect(1, 1, 1, 0) + zero_rect3 = Rect(1, 1, 0, 1) + zero_rect4 = Rect(1, 1, -1, 0) + zero_rect5 = Rect(1, 1, 0, -1) + + no_collide_item1 = ("no collide 1", zero_rect1.copy()) + no_collide_item2 = ("no collide 2", zero_rect2.copy()) + no_collide_item3 = ("no collide 3", zero_rect3.copy()) + no_collide_item4 = ("no collide 4", zero_rect4.copy()) + no_collide_item5 = ("no collide 5", zero_rect5.copy()) + no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10)) + no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2)) + + # Dict to check collisions with values. + rect_values = dict( + ( + no_collide_item1, + no_collide_item2, + no_collide_item3, + no_collide_item4, + no_collide_item5, + no_collide_item6, + no_collide_item7, + ) + ) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + for zero_rect in ( + zero_rect1, + zero_rect2, + zero_rect3, + zero_rect4, + zero_rect5, + ): + collide_item = zero_rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__zero_sized_rects_as_args(self): + """Ensures collidedict works correctly with zero sized rects as args. + + There should be no collisions with zero sized rects. + """ + rect = Rect(0, 0, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(1, 1, 0, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 1, 0)) + no_collide_item3 = ("no collide 3", Rect(1, 1, 0, 1)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertIsNone(collide_item) + + def test_collidedict__negative_sized_rects(self): + """Ensures collidedict works correctly with negative sized rects.""" + neg_rect = Rect(1, 1, -1, -1) + + collide_item1 = ("collide 1", neg_rect.copy()) + collide_item2 = ("collide 2", Rect(0, 0, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(1, 1, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, collide_item2, no_collide_item1)) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + collide_items = value_collide_items + d = rect_values + else: + collide_items = key_collide_items + d = rect_keys + + collide_item = neg_rect.collidedict(d, use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, collide_items) + + def test_collidedict__negative_sized_rects_as_args(self): + """Ensures collidedict works correctly with negative sized rect args.""" + rect = Rect(0, 0, 10, 10) + + collide_item1 = ("collide 1", Rect(1, 1, -1, -1)) + no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, no_collide_item1, no_collide_item2)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + for use_values in (True, False): + if use_values: + expected_item = collide_item1 + d = rect_values + else: + expected_item = (tuple(collide_item1[1]), collide_item1[0]) + d = rect_keys + + collide_item = rect.collidedict(d, use_values) + + self.assertTupleEqual(collide_item, expected_item) + + def test_collidedict__invalid_dict_format(self): + """Ensures collidedict correctly handles invalid dict parameters.""" + rect = Rect(0, 0, 10, 10) + + invalid_value_dict = ("collide", rect.copy()) + invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0] + + for use_values in (True, False): + d = invalid_value_dict if use_values else invalid_key_dict + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(d, use_values) + + def test_collidedict__invalid_dict_value_format(self): + """Ensures collidedict correctly handles dicts with invalid values.""" + rect = Rect(0, 0, 10, 10) + rect_keys = {tuple(rect): "collide"} + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(rect_keys, 1) + + def test_collidedict__invalid_dict_key_format(self): + """Ensures collidedict correctly handles dicts with invalid keys.""" + rect = Rect(0, 0, 10, 10) + rect_values = {"collide": rect.copy()} + + with self.assertRaises(TypeError): + collide_item = rect.collidedict(rect_values) + + def test_collidedict__invalid_use_values_format(self): + """Ensures collidedict correctly handles invalid use_values parameters.""" + rect = Rect(0, 0, 1, 1) + d = {} + + for invalid_param in (None, d, 1.1): + with self.assertRaises(TypeError): + collide_item = rect.collidedict(d, invalid_param) + + def test_collidedict__kwargs(self): + """Ensures collidedict detects collisions via keyword arguments.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 10, 10)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 10, 10)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = (collide_item1, collide_item2) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items) + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_item = rect.collidedict(rect_dict=d, values=use_values) + + # The detected collision could be any of the possible items. + self.assertIn(collide_item, expected_items) + + def test_collidedictall(self): + """Ensures collidedictall detects collisions.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__no_collision(self): + """Ensures collidedictall returns an empty list when no collisions.""" + rect = Rect(1, 1, 10, 10) + + no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__barely_touching(self): + """Ensures collidedictall works correctly for rects that barely touch.""" + rect = Rect(1, 1, 10, 10) + # Small rect to test barely touching collisions. + collide_rect = Rect(0, 0, 1, 1) + + collide_item1 = ("collide 1", collide_rect) + no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20)) + no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + no_collide_rect_values = dict( + (no_collide_item1, no_collide_item2, no_collide_item3) + ) + + # Dict to check collisions with keys. + no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()} + + # Tests the collide_rect on each of the rect's corners. + for attr in ("topleft", "topright", "bottomright", "bottomleft"): + setattr(collide_rect, attr, getattr(rect, attr)) + + for use_values in (True, False): + if use_values: + expected_items = [collide_item1] + d = dict(no_collide_rect_values) + else: + expected_items = [(tuple(collide_item1[1]), collide_item1[0])] + d = dict(no_collide_rect_keys) + + d.update(expected_items) # Add in the expected items. + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__zero_sized_rects(self): + """Ensures collidedictall works correctly with zero sized rects. + + There should be no collisions with zero sized rects. + """ + zero_rect1 = Rect(2, 2, 0, 0) + zero_rect2 = Rect(2, 2, 2, 0) + zero_rect3 = Rect(2, 2, 0, 2) + zero_rect4 = Rect(2, 2, -2, 0) + zero_rect5 = Rect(2, 2, 0, -2) + + no_collide_item1 = ("no collide 1", zero_rect1.copy()) + no_collide_item2 = ("no collide 2", zero_rect2.copy()) + no_collide_item3 = ("no collide 3", zero_rect3.copy()) + no_collide_item4 = ("no collide 4", zero_rect4.copy()) + no_collide_item5 = ("no collide 5", zero_rect5.copy()) + no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10)) + no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2)) + + # Dict to check collisions with values. + rect_values = dict( + ( + no_collide_item1, + no_collide_item2, + no_collide_item3, + no_collide_item4, + no_collide_item5, + no_collide_item6, + no_collide_item7, + ) + ) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + for zero_rect in ( + zero_rect1, + zero_rect2, + zero_rect3, + zero_rect4, + zero_rect5, + ): + collide_items = zero_rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__zero_sized_rects_as_args(self): + """Ensures collidedictall works correctly with zero sized rects + as args. + + There should be no collisions with zero sized rects. + """ + rect = Rect(0, 0, 20, 20) + + no_collide_item1 = ("no collide 1", Rect(2, 2, 0, 0)) + no_collide_item2 = ("no collide 2", Rect(2, 2, 2, 0)) + no_collide_item3 = ("no collide 3", Rect(2, 2, 0, 2)) + + # Dict to check collisions with values. + rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3)) + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + + expected_items = [] + + for use_values in (True, False): + d = rect_values if use_values else rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__negative_sized_rects(self): + """Ensures collidedictall works correctly with negative sized rects.""" + neg_rect = Rect(2, 2, -2, -2) + + collide_item1 = ("collide 1", neg_rect.copy()) + collide_item2 = ("collide 2", Rect(0, 0, 20, 20)) + no_collide_item1 = ("no collide 1", Rect(2, 2, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, collide_item2, no_collide_item1)) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = neg_rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__negative_sized_rects_as_args(self): + """Ensures collidedictall works correctly with negative sized rect + args. + """ + rect = Rect(0, 0, 10, 10) + + collide_item1 = ("collide 1", Rect(1, 1, -1, -1)) + no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0)) + no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1)) + + # Dict to check collisions with values. + rect_values = dict((collide_item1, no_collide_item1, no_collide_item2)) + value_collide_items = [collide_item1] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(d, use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidedictall__invalid_dict_format(self): + """Ensures collidedictall correctly handles invalid dict parameters.""" + rect = Rect(0, 0, 10, 10) + + invalid_value_dict = ("collide", rect.copy()) + invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0] + + for use_values in (True, False): + d = invalid_value_dict if use_values else invalid_key_dict + + with self.assertRaises(TypeError): + collide_item = rect.collidedictall(d, use_values) + + def test_collidedictall__invalid_dict_value_format(self): + """Ensures collidedictall correctly handles dicts with invalid values.""" + rect = Rect(0, 0, 10, 10) + rect_keys = {tuple(rect): "collide"} + + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(rect_keys, 1) + + def test_collidedictall__invalid_dict_key_format(self): + """Ensures collidedictall correctly handles dicts with invalid keys.""" + rect = Rect(0, 0, 10, 10) + rect_values = {"collide": rect.copy()} + + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(rect_values) + + def test_collidedictall__invalid_use_values_format(self): + """Ensures collidedictall correctly handles invalid use_values + parameters. + """ + rect = Rect(0, 0, 1, 1) + d = {} + + for invalid_param in (None, d, 1.1): + with self.assertRaises(TypeError): + collide_items = rect.collidedictall(d, invalid_param) + + def test_collidedictall__kwargs(self): + """Ensures collidedictall detects collisions via keyword arguments.""" + rect = Rect(1, 1, 10, 10) + + collide_item1 = ("collide 1", rect.copy()) + collide_item2 = ("collide 2", Rect(5, 5, 10, 10)) + no_collide_item1 = ("no collide 1", Rect(60, 60, 20, 20)) + no_collide_item2 = ("no collide 2", Rect(70, 70, 20, 20)) + + # Dict to check collisions with values. + rect_values = dict( + (collide_item1, collide_item2, no_collide_item1, no_collide_item2) + ) + value_collide_items = [collide_item1, collide_item2] + + # Dict to check collisions with keys. + rect_keys = {tuple(v): k for k, v in rect_values.items()} + key_collide_items = [(tuple(v), k) for k, v in value_collide_items] + + for use_values in (True, False): + if use_values: + expected_items = value_collide_items + d = rect_values + else: + expected_items = key_collide_items + d = rect_keys + + collide_items = rect.collidedictall(rect_dict=d, values=use_values) + + self._assertCountEqual(collide_items, expected_items) + + def test_collidelist(self): + # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelist: + + # Rect.collidelist(list): return index + # test if one rectangle in a list intersects + # + # Test whether the rectangle collides with any in a sequence of + # rectangles. The index of the first collision found is returned. If + # no collisions are found an index of -1 is returned. r = Rect(1, 1, 10, 10) l = [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(15, 15, 1, 1)] @@ -610,17 +2640,32 @@ class RectTypeTest(unittest.TestCase): f = [Rect(50, 50, 1, 1), (100, 100, 4, 4)] self.assertEqual(r.collidelist(f), -1) + def test_collidelist__kwargs(self): + # Rect.collidelist(list): return index + # test if one rectangle in a list intersects + # + # Test whether the rectangle collides with any in a sequence of + # rectangles using keyword arguments. The index of the first collision + # found is returned. If no collisions are found an index + # of -1 is returned. - def test_collidelistall(self): + r = Rect(1, 1, 10, 10) + l = [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(15, 15, 1, 1)] + self.assertEqual(r.collidelist(l), 1) + + f = [Rect(50, 50, 1, 1), (100, 100, 4, 4)] + self.assertEqual(r.collidelist(rects=f), -1) + + def test_collidelistall(self): # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelistall: - # Rect.collidelistall(list): return indices - # test if all rectangles in a list intersect - # - # Returns a list of all the indices that contain rectangles that - # collide with the Rect. If no intersecting rectangles are found, an - # empty list is returned. + # Rect.collidelistall(list): return indices + # test if all rectangles in a list intersect + # + # Returns a list of all the indices that contain rectangles that + # collide with the Rect. If no intersecting rectangles are found, an + # empty list is returned. r = Rect(1, 1, 10, 10) @@ -635,17 +2680,482 @@ class RectTypeTest(unittest.TestCase): f = [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)] self.assertFalse(r.collidelistall(f)) + def test_collidelistall_returns_empty_list(self): + r = Rect(1, 1, 10, 10) - def test_fit(self): + l = [ + Rect(112, 1, 10, 10), + Rect(50, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(-20, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), []) + + def test_collidelistall_list_of_tuples(self): + r = Rect(1, 1, 10, 10) + + l = [ + (1, 1, 10, 10), + (5, 5, 10, 10), + (15, 15, 1, 1), + (2, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [(50, 50, 1, 1), (20, 20, 5, 5)] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_two_tuples(self): + r = Rect(1, 1, 10, 10) + + l = [ + ((1, 1), (10, 10)), + ((5, 5), (10, 10)), + ((15, 15), (1, 1)), + ((2, 2), (1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [((50, 50), (1, 1)), ((20, 20), (5, 5))] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_lists(self): + r = Rect(1, 1, 10, 10) + + l = [ + [1, 1, 10, 10], + [5, 5, 10, 10], + [15, 15, 1, 1], + [2, 2, 1, 1], + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [[50, 50, 1, 1], [20, 20, 5, 5]] + self.assertFalse(r.collidelistall(f)) + + class _ObjectWithRectAttribute: + def __init__(self, r): + self.rect = r + + class _ObjectWithCallableRectAttribute: + def __init__(self, r): + self._rect = r + + def rect(self): + return self._rect + + class _ObjectWithRectProperty: + def __init__(self, r): + self._rect = r + + @property + def rect(self): + return self._rect + + class _ObjectWithMultipleRectAttribute: + def __init__(self, r1, r2, r3): + self.rect1 = r1 + self.rect2 = r2 + self.rect3 = r3 + + def test_collidelistall_list_of_object_with_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_object_with_callable_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithCallableRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(2, 2, 1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_object_with_callable_rect_returning_object_with_rect_attribute( + self, + ): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)) + ), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall_list_of_object_with_rect_property(self): + r = Rect(1, 1, 10, 10) + + l = [ + self._ObjectWithRectProperty(Rect(1, 1, 10, 10)), + self._ObjectWithRectProperty(Rect(5, 5, 10, 10)), + self._ObjectWithRectProperty(Rect(15, 15, 1, 1)), + self._ObjectWithRectProperty(Rect(2, 2, 1, 1)), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(20, 20, 5, 5)), + ] + self.assertFalse(r.collidelistall(f)) + + def test_collidelistall__kwargs(self): + # Rect.collidelistall(list): return indices + # test if all rectangles in a list intersect using keyword arguments. + # + # Returns a list of all the indices that contain rectangles that + # collide with the Rect. If no intersecting rectangles are found, an + # empty list is returned. + + r = Rect(1, 1, 10, 10) + + l = [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ] + self.assertEqual(r.collidelistall(l), [0, 1, 3]) + + f = [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)] + self.assertFalse(r.collidelistall(rects=f)) + def test_collideobjects_call_variants(self): + # arrange + r = Rect(1, 1, 10, 10) + rects = [Rect(1, 2, 3, 4), Rect(10, 20, 30, 40)] + objects = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + ] + + # act / verify + r.collideobjects(rects) + r.collideobjects(rects, key=None) + r.collideobjects(objects, key=lambda o: o.rect1) + self.assertRaises(TypeError, r.collideobjects, objects) + + def test_collideobjects_without_key(self): + r = Rect(1, 1, 10, 10) + types_to_test = [ + [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(4, 4, 1, 1)], + [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithRectAttribute(Rect(4, 4, 1, 1)), + ], + [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(5, 5, 10, 10)), + self._ObjectWithRectProperty(Rect(4, 4, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(4, 4, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(4, 4, 1, 1)) + ), + ], + [(50, 50, 1, 1), (5, 5, 10, 10), (4, 4, 1, 1)], + [((50, 50), (1, 1)), ((5, 5), (10, 10)), ((4, 4), (1, 1))], + [[50, 50, 1, 1], [5, 5, 10, 10], [4, 4, 1, 1]], + [ + Rect(50, 50, 1, 1), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + (4, 4, 1, 1), + ], # mix + ] + + for l in types_to_test: + with self.subTest(type=l[0].__class__.__name__): + # act + actual = r.collideobjects(l) + # assert + self.assertEqual(actual, l[1]) + + types_to_test = [ + [Rect(50, 50, 1, 1), Rect(100, 100, 4, 4)], + [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(100, 100, 4, 4)), + ], + [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(100, 100, 4, 4)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(100, 100, 4, 4)), + ], + [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(100, 100, 4, 4)) + ), + ], + [(50, 50, 1, 1), (100, 100, 4, 4)], + [((50, 50), (1, 1)), ((100, 100), (4, 4))], + [[50, 50, 1, 1], [100, 100, 4, 4]], + [Rect(50, 50, 1, 1), [100, 100, 4, 4]], # mix + ] + + for f in types_to_test: + with self.subTest(type=f[0].__class__.__name__, expected=None): + # act + actual = r.collideobjects(f) + # assert + self.assertEqual(actual, None) + + def test_collideobjects_list_of_object_with_multiple_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + things = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 1, 10, 10), Rect(5, 5, 1, 1), Rect(-73, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(5, 5, 10, 10), Rect(-5, -5, 10, 10), Rect(3, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(15, 15, 1, 1), Rect(100, 1, 1, 1), Rect(3, 83, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(2, 2, 1, 1), Rect(1, -81, 10, 10), Rect(3, 8, 3, 3) + ), + ] + self.assertEqual(r.collideobjects(things, key=lambda o: o.rect1), things[0]) + self.assertEqual(r.collideobjects(things, key=lambda o: o.rect2), things[0]) + self.assertEqual(r.collideobjects(things, key=lambda o: o.rect3), things[1]) + + f = [ + self._ObjectWithMultipleRectAttribute( + Rect(50, 50, 1, 1), Rect(11, 1, 1, 1), Rect(2, -32, 2, 2) + ), + self._ObjectWithMultipleRectAttribute( + Rect(20, 20, 5, 5), Rect(1, 11, 1, 1), Rect(-20, 2, 2, 2) + ), + ] + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect1)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect2)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect3)) + + def test_collideobjectsall_call_variants(self): + # arrange + r = Rect(1, 1, 10, 10) + rects = [Rect(1, 2, 3, 4), Rect(10, 20, 30, 40)] + objects = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + self._ObjectWithMultipleRectAttribute( + Rect(1, 2, 3, 4), Rect(10, 20, 30, 40), Rect(100, 200, 300, 400) + ), + ] + + # act / verify + r.collideobjectsall(rects) + r.collideobjectsall(rects, key=None) + r.collideobjectsall(objects, key=lambda o: o.rect1) + self.assertRaises(TypeError, r.collideobjectsall, objects) + + def test_collideobjectsall(self): + r = Rect(1, 1, 10, 10) + + types_to_test = [ + [ + Rect(1, 1, 10, 10), + Rect(5, 5, 10, 10), + Rect(15, 15, 1, 1), + Rect(2, 2, 1, 1), + ], + [ + (1, 1, 10, 10), + (5, 5, 10, 10), + (15, 15, 1, 1), + (2, 2, 1, 1), + ], + [ + ((1, 1), (10, 10)), + ((5, 5), (10, 10)), + ((15, 15), (1, 1)), + ((2, 2), (1, 1)), + ], + [ + [1, 1, 10, 10], + [5, 5, 10, 10], + [15, 15, 1, 1], + [2, 2, 1, 1], + ], + [ + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(1, 1, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(5, 5, 10, 10)), + self._ObjectWithCallableRectAttribute(Rect(15, 15, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(2, 2, 1, 1)), + ], + [ + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(1, 1, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(5, 5, 10, 10)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(15, 15, 1, 1)) + ), + self._ObjectWithCallableRectAttribute( + self._ObjectWithRectAttribute(Rect(2, 2, 1, 1)) + ), + ], + [ + self._ObjectWithRectProperty(Rect(1, 1, 10, 10)), + self._ObjectWithRectProperty(Rect(5, 5, 10, 10)), + self._ObjectWithRectProperty(Rect(15, 15, 1, 1)), + self._ObjectWithRectProperty(Rect(2, 2, 1, 1)), + ], + ] + for things in types_to_test: + with self.subTest(type=things[0].__class__.__name__): + # act + actual = r.collideobjectsall(things, key=None) + # assert + self.assertEqual(actual, [things[0], things[1], things[3]]) + + types_to_test = [ + [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)], + [(50, 50, 1, 1), (20, 20, 5, 5)], + [((50, 50), (1, 1)), ((20, 20), (5, 5))], + [[50, 50, 1, 1], [20, 20, 5, 5]], + [ + self._ObjectWithRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithRectAttribute(Rect(20, 20, 5, 5)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ], + [ + self._ObjectWithCallableRectAttribute(Rect(50, 50, 1, 1)), + self._ObjectWithCallableRectAttribute(Rect(20, 20, 5, 5)), + ], + [ + self._ObjectWithRectProperty(Rect(50, 50, 1, 1)), + self._ObjectWithRectProperty(Rect(20, 20, 5, 5)), + ], + ] + for f in types_to_test: + with self.subTest(type=f[0].__class__.__name__, expected=None): + # act + actual = r.collideobjectsall(f) + # assert + self.assertFalse(actual) + + def test_collideobjectsall_list_of_object_with_multiple_rect_attribute(self): + r = Rect(1, 1, 10, 10) + + things = [ + self._ObjectWithMultipleRectAttribute( + Rect(1, 1, 10, 10), Rect(5, 5, 1, 1), Rect(-73, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(5, 5, 10, 10), Rect(-5, -5, 10, 10), Rect(3, 3, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(15, 15, 1, 1), Rect(100, 1, 1, 1), Rect(3, 83, 3, 3) + ), + self._ObjectWithMultipleRectAttribute( + Rect(2, 2, 1, 1), Rect(1, -81, 10, 10), Rect(3, 8, 3, 3) + ), + ] + self.assertEqual( + r.collideobjectsall(things, key=lambda o: o.rect1), + [things[0], things[1], things[3]], + ) + self.assertEqual( + r.collideobjectsall(things, key=lambda o: o.rect2), [things[0], things[1]] + ) + self.assertEqual( + r.collideobjectsall(things, key=lambda o: o.rect3), [things[1], things[3]] + ) + + f = [ + self._ObjectWithMultipleRectAttribute( + Rect(50, 50, 1, 1), Rect(11, 1, 1, 1), Rect(2, -32, 2, 2) + ), + self._ObjectWithMultipleRectAttribute( + Rect(20, 20, 5, 5), Rect(1, 11, 1, 1), Rect(-20, 2, 2, 2) + ), + ] + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect1)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect2)) + self.assertFalse(r.collideobjectsall(f, key=lambda o: o.rect3)) + + def test_fit(self): # __doc__ (as of 2008-08-02) for pygame.rect.Rect.fit: - # Rect.fit(Rect): return Rect - # resize and move a rectangle with aspect ratio - # - # Returns a new rectangle that is moved and resized to fit another. - # The aspect ratio of the original Rect is preserved, so the new - # rectangle may be smaller than the target in either width or height. + # Rect.fit(Rect): return Rect + # resize and move a rectangle with aspect ratio + # + # Returns a new rectangle that is moved and resized to fit another. + # The aspect ratio of the original Rect is preserved, so the new + # rectangle may be smaller than the target in either width or height. r = Rect(10, 10, 30, 30) @@ -657,14 +3167,11 @@ class RectTypeTest(unittest.TestCase): f2 = r2.fit(r) self.assertTrue(r.contains(f2)) - - def test_copy(self): r = Rect(1, 2, 10, 20) c = r.copy() self.assertEqual(c, r) - def test_subscript(self): r = Rect(1, 2, 3, 4) self.assertEqual(r[0], 1) @@ -692,7 +3199,7 @@ class RectTypeTest(unittest.TestCase): self.assertEqual(r, [1, 2, 3, 4]) self.assertRaises(TypeError, r.__setitem__, None, 0) self.assertEqual(r, [1, 2, 3, 4]) - self.assertRaises(TypeError, r.__setitem__, 0, '') + self.assertRaises(TypeError, r.__setitem__, 0, "") self.assertEqual(r, [1, 2, 3, 4]) self.assertRaises(IndexError, r.__setitem__, 4, 0) self.assertEqual(r, [1, 2, 3, 4]) @@ -717,6 +3224,22 @@ class RectTypeTest(unittest.TestCase): r[::-1] = r self.assertEqual(r, [14, 13, 12, 11]) + def test_ass_subscript_deletion(self): + r = Rect(0, 0, 0, 0) + with self.assertRaises(TypeError): + del r[0] + + with self.assertRaises(TypeError): + del r[0:2] + + with self.assertRaises(TypeError): + del r[...] + + def test_collection_abc(self): + r = Rect(64, 70, 75, 30) + self.assertTrue(isinstance(r, Collection)) + self.assertFalse(isinstance(r, Sequence)) + class SubclassTest(unittest.TestCase): class MyRect(Rect): @@ -780,5 +3303,11 @@ class SubclassTest(unittest.TestCase): self.assertTrue(isinstance(mr2, self.MyRect)) self.assertRaises(AttributeError, getattr, mr2, "an_attribute") -if __name__ == '__main__': + def test_collection_abc(self): + mr1 = self.MyRect(64, 70, 75, 30) + self.assertTrue(isinstance(mr1, Collection)) + self.assertFalse(isinstance(mr1, Sequence)) + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_3_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_4_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_5_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/fake_6_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py index d3545e3e7084e6e617562d0659c77fb52ecaf28e..0ba0e94d132e20e051e60be86a6b3dd91adebbd4 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): pass @@ -34,5 +34,6 @@ class KeyModuleTest(unittest.TestCase): def test_set_repeat(self): pass -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py index c4ded952de3d0bcda8e78c38a383b0a4f1cc78fd..649055a612b9fdacf2f5cb9f5251aef9017d60fd 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py @@ -1,22 +1,23 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): pass -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py index 1b76918ad54fe4d6e0c9cdd7bc7dee4388b95c32..bdd8a3b7497b28ea6cd47b669380168134abccea 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py index 34d32c67c0d906bfaa40ad9ad50bffe0b4289c7a..126bc2b8e0529a33f11de2e2ed79dece15b4f7b7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/magic_tag_test.py @@ -1,22 +1,22 @@ -__tags__ = ['magic'] +__tags__ = ["magic"] -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -34,5 +34,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py index cc830bbdcc8440891e81189548e6b422c8e502c9..468c75fb781ecc437ac5d7ec914940cf27f66e69 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/everything/sleep_test.py @@ -1,22 +1,22 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest import time + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): stop_time = time.time() + 10.0 @@ -25,5 +25,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py index 063cbc44cbce8f7618869cb8d9a4b17439fe0bc2..3ef959a0d002a569363354133b9d26fb2784fe84 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py @@ -1,22 +1,22 @@ -__tags__ = ['invisible'] +__tags__ = ["invisible"] -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -37,5 +37,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py index 34d32c67c0d906bfaa40ad9ad50bffe0b4289c7a..126bc2b8e0529a33f11de2e2ed79dece15b4f7b7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/exclude/magic_tag_test.py @@ -1,22 +1,22 @@ -__tags__ = ['magic'] +__tags__ = ["magic"] -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -34,5 +34,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_3_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py index b540a3ce57a1f6321b3cd59d549ba57d3c18c964..1e75feaf49b2c04ee2fde3272883030b6c99bf5a 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/failures1/fake_4_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -37,5 +37,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py index 1c68eaa08c76acbe19c7faf398c4ed34370a0d7a..b88f1aebe34fd11f11b16a6ee17961037e2a17dc 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete/fake_3_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py index 1b76918ad54fe4d6e0c9cdd7bc7dee4388b95c32..bdd8a3b7497b28ea6cd47b669380168134abccea 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py index a4262fa0a06a1719967b10105aa060edd4e3015b..3e9e93673d0ffbb95e88541c329ba340034ddc92 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -36,5 +36,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py index 7362aa4e53fc264b3e90897a6eb43e00d21307f2..f59ad40ab99779a5ecd2f46c7178cb7805986793 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py @@ -1,20 +1,21 @@ import sys -if __name__ == '__main__': + +if __name__ == "__main__": import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -36,5 +37,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py index b540a3ce57a1f6321b3cd59d549ba57d3c18c964..1e75feaf49b2c04ee2fde3272883030b6c99bf5a 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -37,5 +37,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py index 6b4af95d377618af379f0942fae2a8458f7a71a4..467c72545584cd172d8f396fd212372f995db259 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py @@ -1,20 +1,21 @@ import sys -if __name__ == '__main__': + +if __name__ == "__main__": import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -37,5 +38,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py index b540a3ce57a1f6321b3cd59d549ba57d3c18c964..1e75feaf49b2c04ee2fde3272883030b6c99bf5a 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -37,5 +37,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py index db6736a9affae792974c233383882302d7d5aef0..a1eadd104798caf2901a4d9e85b23e12e5809533 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/run_tests__test.py @@ -4,92 +4,102 @@ import subprocess, os, sys, re, difflib ################################################################################ -IGNORE = ( - '.svn', - 'infinite_loop', -) +IGNORE = (".svn", "infinite_loop") NORMALIZERS = ( - (r"Ran (\d+) tests in (\d+\.\d+)s", "Ran \\1 tests in X.XXXs" ), - (r'File ".*?([^/\\.]+\.py)"', 'File "\\1"'), + (r"Ran (\d+) tests in (\d+\.\d+)s", "Ran \\1 tests in X.XXXs"), + (r'File ".*?([^/\\.]+\.py)"', 'File "\\1"'), ) ################################################################################ + def norm_result(result): "normalize differences, such as timing between output" for normalizer, replacement in NORMALIZERS: - if hasattr(normalizer, '__call__'): + if hasattr(normalizer, "__call__"): result = normalizer(result) else: result = re.sub(normalizer, replacement, result) - + return result + def call_proc(cmd, cd=None): - proc = subprocess.Popen ( - cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd = cd, - universal_newlines = True, + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=cd, + universal_newlines=True, ) if proc.wait(): - print ("%s %s" % (cmd, proc.wait())) + print(f"{cmd} {proc.wait()}") raise Exception(proc.stdout.read()) return proc.stdout.read() + ################################################################################ -unnormed_diff = '-u' in sys.argv -verbose = '-v' in sys.argv or unnormed_diff -if '-h' in sys.argv or '--help' in sys.argv: sys.exit ( - "\nCOMPARES OUTPUT OF SINGLE VS SUBPROCESS MODE OF RUN_TESTS.PY\n\n" - '-v, to output diffs even on success\n' - '-u, to output diffs of unnormalized tests\n\n' - "Each line of a Differ delta begins with a two-letter code:\n\n" - " '- ' line unique to sequence 1\n" - " '+ ' line unique to sequence 2\n" - " ' ' line common to both sequences\n" - " '? ' line not present in either input sequence\n" -) +unnormed_diff = "-u" in sys.argv +verbose = "-v" in sys.argv or unnormed_diff +if "-h" in sys.argv or "--help" in sys.argv: + sys.exit( + "\nCOMPARES OUTPUT OF SINGLE VS SUBPROCESS MODE OF RUN_TESTS.PY\n\n" + "-v, to output diffs even on success\n" + "-u, to output diffs of unnormalized tests\n\n" + "Each line of a Differ delta begins with a two-letter code:\n\n" + " '- ' line unique to sequence 1\n" + " '+ ' line unique to sequence 2\n" + " ' ' line common to both sequences\n" + " '? ' line not present in either input sequence\n" + ) -main_dir = os.path.split(os.path.abspath(sys.argv[0]))[0] -trunk_dir = os.path.normpath(os.path.join(main_dir, '../../')) +main_dir = os.path.split(os.path.abspath(sys.argv[0]))[0] +trunk_dir = os.path.normpath(os.path.join(main_dir, "../../")) -test_suite_dirs = [x for x in os.listdir(main_dir) - if os.path.isdir(os.path.join(main_dir, x)) - and x not in IGNORE ] +test_suite_dirs = [ + x + for x in os.listdir(main_dir) + if os.path.isdir(os.path.join(main_dir, x)) and x not in IGNORE +] ################################################################################ + def assert_on_results(suite, single, sub): - test = globals().get('%s_test' % suite) - if hasattr(test, '__call_'): + test = globals().get(f"{suite}_test") + if hasattr(test, "__call_"): test(suite, single, sub) - print ("assertions on %s OK" % (suite,)) + print(f"assertions on {suite} OK") # Don't modify tests in suites below. These assertions are in place to make sure # that tests are actually being ran -def all_ok_test(uite, *args): + +def all_ok_test(suite, *args): for results in args: - assert "Ran 36 tests" in results # some tests are runing - assert "OK" in results # OK + assert "Ran 36 tests" in results # some tests are running + assert "OK" in results # OK + def failures1_test(suite, *args): - for results in args: + for results in args: assert "FAILED (failures=2)" in results assert "Ran 18 tests" in results + ################################################################################ -# Test that output is the same in single process and subprocess modes +# Test that output is the same in single process and subprocess modes # -base_cmd = [sys.executable, 'run_tests.py', '-i'] +base_cmd = [sys.executable, "run_tests.py", "-i"] -cmd = base_cmd + ['-n', '-f'] -sub_cmd = base_cmd + ['-f'] -time_out_cmd = base_cmd + ['-t', '4', '-f', 'infinite_loop' ] +cmd = base_cmd + ["-n", "-f"] +sub_cmd = base_cmd + ["-f"] +time_out_cmd = base_cmd + ["-t", "4", "-f", "infinite_loop"] passes = 0 failed = False @@ -98,34 +108,38 @@ for suite in test_suite_dirs: single = call_proc(cmd + [suite], trunk_dir) subs = call_proc(sub_cmd + [suite], trunk_dir) - normed_single, normed_subs = map(norm_result,(single, subs)) + normed_single, normed_subs = map(norm_result, (single, subs)) failed = normed_single != normed_subs if failed: - print ('%s suite comparison FAILED\n' % (suite,)) + print(f"{suite} suite comparison FAILED\n") else: passes += 1 - print ('%s suite comparison OK' % (suite,)) - + print(f"{suite} suite comparison OK") + assert_on_results(suite, single, subs) if verbose or failed: - print ("difflib.Differ().compare(single, suprocessed):\n") - print (''.join ( list( - difflib.Differ().compare ( - (unnormed_diff and single or normed_single).splitlines(1), - (unnormed_diff and subs or normed_subs).splitlines(1) - )) - )) + print("difflib.Differ().compare(single, suprocessed):\n") + print( + "".join( + list( + difflib.Differ().compare( + (unnormed_diff and single or normed_single).splitlines(1), + (unnormed_diff and subs or normed_subs).splitlines(1), + ) + ) + ) + ) sys.stdout.write("infinite_loop suite (subprocess mode timeout) ") loop_test = call_proc(time_out_cmd, trunk_dir) assert "successfully terminated" in loop_test passes += 1 -print ("OK") +print("OK") -print ("\n%s/%s suites pass" % (passes, len(test_suite_dirs) + 1)) +print(f"\n{passes}/{len(test_suite_dirs) + 1} suites pass") -print ("\n-h for help") +print("\n-h for help") ################################################################################ diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py index 398aef58acc106851feae392d014173f11a7e03b..3be92e1ad8280ddf5b0e8db29a5fc4df13b59dc7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/fake_2_test.py @@ -1,20 +1,20 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): self.assertTrue(True) @@ -35,5 +35,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py b/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py index e1c9857c491181a6d6357bf88aed35373caee5ac..bab528a9e2c555f4b3631b8a394ce5920fb9cae7 100644 --- a/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py +++ b/venv/Lib/site-packages/pygame/tests/run_tests__tests/timeout/sleep_test.py @@ -1,22 +1,22 @@ -if __name__ == '__main__': +if __name__ == "__main__": import sys import os - pkg_dir = (os.path.split( - os.path.split( - os.path.split( - os.path.abspath(__file__))[0])[0])[0]) + + pkg_dir = os.path.split( + os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] + )[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') + is_pygame_pkg = __name__.startswith("pygame.tests.") import unittest import time + class KeyModuleTest(unittest.TestCase): def test_get_focused(self): stop_time = time.time() + 10.0 @@ -26,5 +26,5 @@ class KeyModuleTest(unittest.TestCase): self.assertTrue(True) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/rwobject_test.py b/venv/Lib/site-packages/pygame/tests/rwobject_test.py index ad5ad331d2ffb4b497c8f312bb5bbd9396888793..31723aee9874c3d2a009b70833f5aa9d5424ad35 100644 --- a/venv/Lib/site-packages/pygame/tests/rwobject_test.py +++ b/venv/Lib/site-packages/pygame/tests/rwobject_test.py @@ -1,8 +1,7 @@ -import sys +import pathlib import unittest from pygame import encode_string, encode_file_path -from pygame.compat import bytes_, as_bytes, as_unicode class RWopsEncodeStringTest(unittest.TestCase): @@ -14,66 +13,72 @@ class RWopsEncodeStringTest(unittest.TestCase): self.assertIsNone(encoded_string) def test_returns_bytes(self): - u = as_unicode(r"Hello") + u = "Hello" encoded_string = encode_string(u) - self.assertIsInstance(encoded_string, bytes_) + self.assertIsInstance(encoded_string, bytes) def test_obj_bytes(self): - b = as_bytes("encyclop\xE6dia") - encoded_string = encode_string(b, 'ascii', 'strict') + b = b"encyclop\xE6dia" + encoded_string = encode_string(b, "ascii", "strict") self.assertIs(encoded_string, b) def test_encode_unicode(self): - u = as_unicode(r"\u00DEe Olde Komp\u00FCter Shoppe") - b = u.encode('utf-8') - self.assertEqual(encode_string(u, 'utf-8'), b) + u = "\u00DEe Olde Komp\u00FCter Shoppe" + b = u.encode("utf-8") + self.assertEqual(encode_string(u, "utf-8"), b) def test_error_fowarding(self): self.assertRaises(SyntaxError, encode_string) def test_errors(self): - s = r"abc\u0109defg\u011Dh\u0125ij\u0135klmnoprs\u015Dtu\u016Dvz" - u = as_unicode(s) - b = u.encode('ascii', 'ignore') - self.assertEqual(encode_string(u, 'ascii', 'ignore'), b) + u = "abc\u0109defg\u011Dh\u0125ij\u0135klmnoprs\u015Dtu\u016Dvz" + b = u.encode("ascii", "ignore") + self.assertEqual(encode_string(u, "ascii", "ignore"), b) def test_encoding_error(self): - u = as_unicode(r"a\x80b") - encoded_string = encode_string(u, 'ascii', 'strict') + u = "a\x80b" + encoded_string = encode_string(u, "ascii", "strict") self.assertIsNone(encoded_string) def test_check_defaults(self): - u = as_unicode(r"a\u01F7b") + u = "a\u01F7b" b = u.encode("unicode_escape", "backslashreplace") encoded_string = encode_string(u) self.assertEqual(encoded_string, b) def test_etype(self): - u = as_unicode(r"a\x80b") - self.assertRaises(SyntaxError, encode_string, - u, 'ascii', 'strict', SyntaxError) + u = "a\x80b" + self.assertRaises(SyntaxError, encode_string, u, "ascii", "strict", SyntaxError) + + def test_etype__invalid(self): + """Ensures invalid etypes are properly handled.""" + + for etype in ("SyntaxError", self): + self.assertRaises(TypeError, encode_string, "test", etype=etype) def test_string_with_null_bytes(self): - b = as_bytes("a\x00b\x00c") + b = b"a\x00b\x00c" encoded_string = encode_string(b, etype=SyntaxError) - encoded_decode_string = encode_string(b.decode(), 'ascii', 'strict') + encoded_decode_string = encode_string(b.decode(), "ascii", "strict") self.assertIs(encoded_string, b) self.assertEqual(encoded_decode_string, b) try: from sys import getrefcount as _g - getrefcount = _g # This nonsense is for Python 3.x + + getrefcount = _g # This nonsense is for Python 3.x except ImportError: pass else: + def test_refcount(self): - bpath = as_bytes(" This is a string that is not cached.")[1:] - upath = bpath.decode('ascii') + bpath = b" This is a string that is not cached."[1:] + upath = bpath.decode("ascii") before = getrefcount(bpath) bpath = encode_string(bpath) self.assertEqual(getrefcount(bpath), before) @@ -81,37 +86,54 @@ class RWopsEncodeStringTest(unittest.TestCase): self.assertEqual(getrefcount(bpath), before) def test_smp(self): - utf_8 = as_bytes("a\xF0\x93\x82\xA7b") - u = as_unicode(r"a\U000130A7b") - b = encode_string(u, 'utf-8', 'strict', AssertionError) + utf_8 = b"a\xF0\x93\x82\xA7b" + u = "a\U000130A7b" + b = encode_string(u, "utf-8", "strict", AssertionError) self.assertEqual(b, utf_8) - # For Python 3.1, surrogate pair handling depends on whether the - # interpreter was built with UCS-2 or USC-4 unicode strings. - ##u = as_unicode(r"a\uD80C\uDCA7b") - ##b = encode_string(u, 'utf-8', 'strict', AssertionError) - ##self.assertEqual(b, utf_8) + + def test_pathlib_obj(self): + """Test loading string representation of pathlib object""" + """ + We do this because pygame functions internally use pg_EncodeString + to decode the filenames passed to them. So if we test that here, we + can safely assume that all those functions do not have any issues + with pathlib objects + """ + encoded = encode_string(pathlib.PurePath("foo"), "utf-8") + self.assertEqual(encoded, b"foo") + + encoded = encode_string(pathlib.Path("baz")) + self.assertEqual(encoded, b"baz") + class RWopsEncodeFilePathTest(unittest.TestCase): # Most tests can be skipped since RWopsEncodeFilePath wraps # RWopsEncodeString def test_encoding(self): - u = as_unicode(r"Hello") + u = "Hello" encoded_file_path = encode_file_path(u) - self.assertIsInstance(encoded_file_path, bytes_) + self.assertIsInstance(encoded_file_path, bytes) def test_error_fowarding(self): self.assertRaises(SyntaxError, encode_file_path) def test_path_with_null_bytes(self): - b = as_bytes("a\x00b\x00c") + b = b"a\x00b\x00c" encoded_file_path = encode_file_path(b) self.assertIsNone(encoded_file_path) def test_etype(self): - b = as_bytes("a\x00b\x00c") + b = b"a\x00b\x00c" self.assertRaises(TypeError, encode_file_path, b, TypeError) -if __name__ == '__main__': + def test_etype__invalid(self): + """Ensures invalid etypes are properly handled.""" + + for etype in ("SyntaxError", self): + self.assertRaises(TypeError, encode_file_path, "test", etype) + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/scrap_tags.py b/venv/Lib/site-packages/pygame/tests/scrap_tags.py index 760152160355a56c4e99aa995a2d5227e292f6be..17a82ffdb43dd2dbcf8f2e68f7d044ba17321c95 100644 --- a/venv/Lib/site-packages/pygame/tests/scrap_tags.py +++ b/venv/Lib/site-packages/pygame/tests/scrap_tags.py @@ -1,24 +1,26 @@ -# For now the scrap module has not been updated for SDL 2 -__tags__ = ['SDL2_ignore'] - -import sys - -exclude = False - -if sys.platform == 'win32' or sys.platform.startswith('linux'): - try: - import pygame - pygame.scrap._NOT_IMPLEMENTED_ - except AttributeError: - pass - else: - exclude = True -else: - exclude = True - -if exclude: - __tags__.extend(['ignore', 'subprocess_ignore']) - - - - +__tags__ = ["ignore", "subprocess_ignore"] + +# TODO: make scrap_test.py work +# This test used to work only on linux and windows. +# Currently it only work in windows, and in linux it throws: +# `pygame.error: content could not be placed in clipboard.` +# The old test and tags kept here for reference when fixing. + +# import sys +# +# exclude = False +# +# if sys.platform == "win32" or sys.platform.startswith("linux"): +# try: +# import pygame +# +# pygame.scrap._NOT_IMPLEMENTED_ +# except AttributeError: +# pass +# else: +# exclude = True +# else: +# exclude = True +# +# if exclude: +# __tags__.extend(["ignore", "subprocess_ignore"]) diff --git a/venv/Lib/site-packages/pygame/tests/scrap_test.py b/venv/Lib/site-packages/pygame/tests/scrap_test.py index 60ff2482723d93696cb645dea0c676ced30de0eb..6b7f6faa98ca6e1bac8cd8252c2556590e223fc8 100644 --- a/venv/Lib/site-packages/pygame/tests/scrap_test.py +++ b/venv/Lib/site-packages/pygame/tests/scrap_test.py @@ -1,16 +1,16 @@ import os import sys -if os.environ.get('SDL_VIDEODRIVER') == 'dummy': - __tags__ = ('ignore', 'subprocess_ignore') + +if os.environ.get("SDL_VIDEODRIVER") == "dummy": + __tags__ = ("ignore", "subprocess_ignore") import unittest from pygame.tests.test_utils import trunk_relative_path import pygame from pygame import scrap -from pygame.compat import as_bytes -class ScrapModuleTest(unittest.TestCase): +class ScrapModuleTest(unittest.TestCase): @classmethod def setUpClass(cls): pygame.display.init() @@ -29,6 +29,16 @@ class ScrapModuleTest(unittest.TestCase): self.assertTrue(scrap.get_init()) + def test_init__reinit(self): + """Ensures reinitializing the scrap module doesn't clear its data.""" + data_type = pygame.SCRAP_TEXT + expected_data = b"test_init__reinit" + scrap.put(data_type, expected_data) + + scrap.init() + + self.assertEqual(scrap.get(data_type), expected_data) + def test_get_init(self): """Ensures get_init gets the init state.""" self.assertTrue(scrap.get_init()) @@ -47,15 +57,14 @@ class ScrapModuleTest(unittest.TestCase): """ # Use a unique data type identifier to ensure there is no preexisting # data. - DATA_TYPE = 'test_get__owned_empty_type' + DATA_TYPE = "test_get__owned_empty_type" if scrap.lost(): # Try to acquire the clipboard. - scrap.put(pygame.SCRAP_TEXT, b'text to clipboard') + scrap.put(pygame.SCRAP_TEXT, b"text to clipboard") if scrap.lost(): - self.skipTest('requires the pygame application to own the ' - 'clipboard') + self.skipTest("requires the pygame application to own the clipboard") data = scrap.get(DATA_TYPE) @@ -78,21 +87,18 @@ class ScrapModuleTest(unittest.TestCase): def test_put__text(self): """Ensures put can place text into the clipboard.""" - scrap.put(pygame.SCRAP_TEXT, as_bytes("Hello world")) + scrap.put(pygame.SCRAP_TEXT, b"Hello world") - self.assertEqual(scrap.get(pygame.SCRAP_TEXT), as_bytes("Hello world")) + self.assertEqual(scrap.get(pygame.SCRAP_TEXT), b"Hello world") - scrap.put(pygame.SCRAP_TEXT, as_bytes("Another String")) + scrap.put(pygame.SCRAP_TEXT, b"Another String") - self.assertEqual(scrap.get(pygame.SCRAP_TEXT), - as_bytes("Another String")) + self.assertEqual(scrap.get(pygame.SCRAP_TEXT), b"Another String") - @unittest.skipIf('pygame.image' not in sys.modules, - 'requires pygame.image module') + @unittest.skipIf("pygame.image" not in sys.modules, "requires pygame.image module") def test_put__bmp_image(self): """Ensures put can place a BMP image into the clipboard.""" - sf = pygame.image.load(trunk_relative_path( - "examples/data/asprite.bmp")) + sf = pygame.image.load(trunk_relative_path("examples/data/asprite.bmp")) expected_string = pygame.image.tostring(sf, "RGBA") scrap.put(pygame.SCRAP_BMP, expected_string) @@ -102,12 +108,12 @@ class ScrapModuleTest(unittest.TestCase): """Ensures put can place data into the clipboard when using a user defined type identifier. """ - DATA_TYPE = 'arbitrary buffer' + DATA_TYPE = "arbitrary buffer" - scrap.put(DATA_TYPE, as_bytes('buf')) + scrap.put(DATA_TYPE, b"buf") r = scrap.get(DATA_TYPE) - self.assertEqual(r, as_bytes('buf')) + self.assertEqual(r, b"buf") class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): @@ -117,6 +123,7 @@ class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): A separate class is used to prevent tests that acquire the clipboard from interfering with these tests. """ + @classmethod def setUpClass(cls): pygame.display.init() @@ -133,8 +140,7 @@ class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): # Skip test if the pygame application owns the clipboard. Currently, # there is no way to give up ownership. if not scrap.lost(): - self.skipTest('requires the pygame application to not own the ' - 'clipboard') + self.skipTest("requires the pygame application to not own the clipboard") def test_get__not_owned(self): """Ensures get works when there is no data of the requested type @@ -145,7 +151,7 @@ class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): # Use a unique data type identifier to ensure there is no preexisting # data. - DATA_TYPE = 'test_get__not_owned' + DATA_TYPE = "test_get__not_owned" data = scrap.get(DATA_TYPE) @@ -169,7 +175,7 @@ class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): # Use a unique data type identifier to ensure there is no preexisting # data. - DATA_TYPE = 'test_contains__not_owned' + DATA_TYPE = "test_contains__not_owned" contains = scrap.contains(DATA_TYPE) @@ -187,21 +193,21 @@ class ScrapModuleClipboardNotOwnedTest(unittest.TestCase): class X11InteractiveTest(unittest.TestCase): - __tags__ = ['ignore', 'subprocess_ignore'] + __tags__ = ["ignore", "subprocess_ignore"] try: pygame.display.init() except Exception: pass else: - if pygame.display.get_driver() == 'x11': - __tags__ = ['interactive'] + if pygame.display.get_driver() == "x11": + __tags__ = ["interactive"] pygame.display.quit() def test_issue_208(self): """PATCH: pygame.scrap on X11, fix copying into PRIMARY selection - Copying into theX11 PRIMARY selection (mouse copy/paste) would not - work due to a confusion between content type and clipboard type. + Copying into theX11 PRIMARY selection (mouse copy/paste) would not + work due to a confusion between content type and clipboard type. """ @@ -215,28 +221,31 @@ class X11InteractiveTest(unittest.TestCase): display.init() display.set_caption("Interactive X11 Paste Test") screen = display.set_mode((600, 200)) - screen.fill(pygame.Color('white')) + screen.fill(pygame.Color("white")) text = "Scrap put() succeeded." - msg = ('Some text has been placed into the X11 clipboard.' - ' Please click the center mouse button in an open' - ' text window to retrieve it.' - '\n\nDid you get "{}"? (y/n)').format(text) + msg = ( + "Some text has been placed into the X11 clipboard." + " Please click the center mouse button in an open" + " text window to retrieve it." + '\n\nDid you get "{}"? (y/n)' + ).format(text) word_wrap(screen, msg, font, 6) display.flip() event.pump() scrap.init() scrap.set_mode(SCRAP_SELECTION) - scrap.put(SCRAP_TEXT, text.encode('UTF-8')) + scrap.put(SCRAP_TEXT, text.encode("UTF-8")) while True: e = event.wait() if e.type == QUIT: break if e.type == KEYDOWN: - success = (e.key == K_y) + success = e.key == K_y break pygame.display.quit() self.assertTrue(success) + def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)): font.origin = True surf_width, surf_height = surf.get_size() @@ -244,9 +253,9 @@ def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)): height = surf_height - 2 * margin line_spacing = int(1.25 * font.get_sized_height()) x, y = margin, margin + line_spacing - space = font.get_rect(' ') + space = font.get_rect(" ") for word in iwords(text): - if word == '\n': + if word == "\n": x, y = margin, y + line_spacing else: bounds = font.get_rect(word) @@ -260,6 +269,7 @@ def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)): x += bounds.width + space.width return x, y + def iwords(text): # r"\n|[^ ]+" # @@ -267,24 +277,25 @@ def iwords(text): tail = head end = len(text) while head < end: - if text[head] == ' ': + if text[head] == " ": head += 1 tail = head + 1 - elif text[head] == '\n': + elif text[head] == "\n": head += 1 - yield '\n' + yield "\n" tail = head + 1 elif tail == end: yield text[head:] head = end - elif text[tail] == '\n': + elif text[tail] == "\n": yield text[head:tail] head = tail - elif text[tail] == ' ': + elif text[tail] == " ": yield text[head:tail] head = tail else: tail += 1 -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/sndarray_tags.py b/venv/Lib/site-packages/pygame/tests/sndarray_tags.py index 6493eb2850b7409f6f4997adf6cae4b8735837c4..68fa7a5bdd3a231a8259cec31d8fbcb183f16eb4 100644 --- a/venv/Lib/site-packages/pygame/tests/sndarray_tags.py +++ b/venv/Lib/site-packages/pygame/tests/sndarray_tags.py @@ -1,4 +1,4 @@ -__tags__ = ['array'] +__tags__ = ["array"] exclude = False @@ -9,4 +9,4 @@ except ImportError: exclude = True if exclude: - __tags__.extend(('ignore', 'subprocess_ignore')) + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/venv/Lib/site-packages/pygame/tests/sndarray_test.py b/venv/Lib/site-packages/pygame/tests/sndarray_test.py index 67b110b896a3300bef9da398a4b54280844ec8f5..5b624caf417d19056f0ed6951cad2a2f9e879c1a 100644 --- a/venv/Lib/site-packages/pygame/tests/sndarray_test.py +++ b/venv/Lib/site-packages/pygame/tests/sndarray_test.py @@ -3,14 +3,10 @@ import unittest from numpy import int8, int16, uint8, uint16, float32, array, alltrue import pygame -from pygame.compat import as_bytes import pygame.sndarray -SDL2 = pygame.get_sdl_version()[0] >= 2 - - -class SndarrayTest (unittest.TestCase): +class SndarrayTest(unittest.TestCase): array_dtypes = {8: uint8, -8: int8, 16: uint16, -16: int16, 32: float32} def _assert_compatible(self, arr, size): @@ -18,7 +14,6 @@ class SndarrayTest (unittest.TestCase): self.assertEqual(arr.dtype, dtype) def test_array(self): - def check_array(size, channels, test_data): try: pygame.mixer.init(22050, size, channels, allowedchanges=0) @@ -32,40 +27,37 @@ class SndarrayTest (unittest.TestCase): snd = pygame.sndarray.make_sound(srcarr) arr = pygame.sndarray.array(snd) self._assert_compatible(arr, size) - self.assertTrue(alltrue(arr == srcarr), - "size: %i\n%s\n%s" % ( - size, arr, test_data)) + self.assertTrue( + alltrue(arr == srcarr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) finally: pygame.mixer.quit() - check_array(8, 1, [0, 0x0f, 0xf0, 0xff]) - check_array(8, 2, - [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xff, 0x40]]) - check_array(16, 1, [0, 0x00ff, 0xff00, 0xffff]) - check_array(16, 2, [[0, 0xffff], [0xffff, 0], - [0x00ff, 0xff00], [0x0f0f, 0xf0f0]]) - check_array(-8, 1, [0, -0x80, 0x7f, 0x64]) - check_array(-8, 2, - [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xff, 0]]) - check_array(-16, 1, [0, 0x7fff, -0x7fff, -1]) - check_array(-16, 2, [[0, -0x7fff], [-0x7fff, 0], - [0x7fff, 0], [0, 0x7fff]]) + check_array(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_array(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_array(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_array( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_array(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_array(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_array(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_array(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) def test_get_arraytype(self): array_type = pygame.sndarray.get_arraytype() - self.assertEqual(array_type, 'numpy', - "unknown array type %s" % array_type) + self.assertEqual(array_type, "numpy", f"unknown array type {array_type}") def test_get_arraytypes(self): arraytypes = pygame.sndarray.get_arraytypes() - self.assertIn('numpy', arraytypes) + self.assertIn("numpy", arraytypes) for atype in arraytypes: - self.assertEqual(atype, 'numpy', "unknown array type %s" % atype) + self.assertEqual(atype, "numpy", f"unknown array type {atype}") def test_make_sound(self): - def check_sound(size, channels, test_data): try: pygame.mixer.init(22050, size, channels, allowedchanges=0) @@ -78,31 +70,28 @@ class SndarrayTest (unittest.TestCase): srcarr = array(test_data, self.array_dtypes[size]) snd = pygame.sndarray.make_sound(srcarr) arr = pygame.sndarray.samples(snd) - self.assertTrue(alltrue(arr == srcarr), - "size: %i\n%s\n%s" % ( - size, arr, test_data)) + self.assertTrue( + alltrue(arr == srcarr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) finally: pygame.mixer.quit() - check_sound(8, 1, [0, 0x0f, 0xf0, 0xff]) - check_sound(8, 2, - [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xff, 0x40]]) - check_sound(16, 1, [0, 0x00ff, 0xff00, 0xffff]) - check_sound(16, 2, [[0, 0xffff], [0xffff, 0], - [0x00ff, 0xff00], [0x0f0f, 0xf0f0]]) - check_sound(-8, 1, [0, -0x80, 0x7f, 0x64]) - check_sound(-8, 2, - [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xff, 0]]) - check_sound(-16, 1, [0, 0x7fff, -0x7fff, -1]) - check_sound(-16, 2, [[0, -0x7fff], [-0x7fff, 0], - [0x7fff, 0], [0, 0x7fff]]) - - if SDL2: - check_sound(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) + check_sound(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_sound(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_sound(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_sound( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_sound(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_sound(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_sound(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_sound(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + check_sound(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) def test_samples(self): + null_byte = b"\x00" - null_byte = as_bytes('\x00') def check_sample(size, channels, test_data): try: pygame.mixer.init(22050, size, channels, allowedchanges=0) @@ -112,53 +101,44 @@ class SndarrayTest (unittest.TestCase): try: __, sz, __ = pygame.mixer.get_init() if sz == size: - zeroed = null_byte * ((abs(size) // 8) * - len(test_data) * - channels) + zeroed = null_byte * ((abs(size) // 8) * len(test_data) * channels) snd = pygame.mixer.Sound(buffer=zeroed) samples = pygame.sndarray.samples(snd) self._assert_compatible(samples, size) - ##print ('X %s' % (samples.shape,)) - ##print ('Y %s' % (test_data,)) + ##print('X %s' % (samples.shape,)) + ##print('Y %s' % (test_data,)) samples[...] = test_data arr = pygame.sndarray.array(snd) - self.assertTrue(alltrue(samples == arr), - "size: %i\n%s\n%s" % ( - size, arr, test_data)) + self.assertTrue( + alltrue(samples == arr), + "size: %i\n%s\n%s" % (size, arr, test_data), + ) finally: pygame.mixer.quit() - check_sample(8, 1, [0, 0x0f, 0xf0, 0xff]) - check_sample(8, 2, - [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xff, 0x40]]) - check_sample(16, 1, [0, 0x00ff, 0xff00, 0xffff]) - check_sample(16, 2, [[0, 0xffff], [0xffff, 0], - [0x00ff, 0xff00], [0x0f0f, 0xf0f0]]) - check_sample(-8, 1, [0, -0x80, 0x7f, 0x64]) - check_sample(-8, 2, - [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xff, 0]]) - check_sample(-16, 1, [0, 0x7fff, -0x7fff, -1]) - check_sample(-16, 2, [[0, -0x7fff], [-0x7fff, 0], - [0x7fff, 0], [0, 0x7fff]]) - - if SDL2: - check_sample(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) + check_sample(8, 1, [0, 0x0F, 0xF0, 0xFF]) + check_sample(8, 2, [[0, 0x80], [0x2D, 0x41], [0x64, 0xA1], [0xFF, 0x40]]) + check_sample(16, 1, [0, 0x00FF, 0xFF00, 0xFFFF]) + check_sample( + 16, 2, [[0, 0xFFFF], [0xFFFF, 0], [0x00FF, 0xFF00], [0x0F0F, 0xF0F0]] + ) + check_sample(-8, 1, [0, -0x80, 0x7F, 0x64]) + check_sample(-8, 2, [[0, -0x80], [-0x64, 0x64], [0x25, -0x50], [0xFF, 0]]) + check_sample(-16, 1, [0, 0x7FFF, -0x7FFF, -1]) + check_sample(-16, 2, [[0, -0x7FFF], [-0x7FFF, 0], [0x7FFF, 0], [0, 0x7FFF]]) + check_sample(32, 2, [[0.0, -1.0], [-1.0, 0], [1.0, 0], [0, 1.0]]) def test_use_arraytype(self): - def do_use_arraytype(atype): pygame.sndarray.use_arraytype(atype) - pygame.sndarray.use_arraytype('numpy') - self.assertEqual(pygame.sndarray.get_arraytype(), 'numpy') - - self.assertRaises(ValueError, do_use_arraytype, 'not an option') + pygame.sndarray.use_arraytype("numpy") + self.assertEqual(pygame.sndarray.get_arraytype(), "numpy") + self.assertRaises(ValueError, do_use_arraytype, "not an option") - @unittest.skipIf(not SDL2, 'requires SDL2') def test_float32(self): - """ sized arrays work with Sounds and 32bit float arrays. - """ + """sized arrays work with Sounds and 32bit float arrays.""" try: pygame.mixer.init(22050, 32, 2, allowedchanges=0) except pygame.error: @@ -170,5 +150,5 @@ class SndarrayTest (unittest.TestCase): pygame.mixer.quit() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/sprite_test.py b/venv/Lib/site-packages/pygame/tests/sprite_test.py index 86a7a1db53e0ca4bc6d65bd6b5321e69ab6a8f5c..531263c0cf944853f12b0229eb7586733e3ccdd9 100644 --- a/venv/Lib/site-packages/pygame/tests/sprite_test.py +++ b/venv/Lib/site-packages/pygame/tests/sprite_test.py @@ -1,5 +1,4 @@ #################################### IMPORTS ################################### -# -*- encoding: utf-8 -*- import unittest @@ -10,13 +9,15 @@ from pygame import sprite ################################# MODULE LEVEL ################################# -class SpriteModuleTest( unittest.TestCase ): + +class SpriteModuleTest(unittest.TestCase): pass + ######################### SPRITECOLLIDE FUNCTIONS TEST ######################### -class SpriteCollideTest( unittest.TestCase ): +class SpriteCollideTest(unittest.TestCase): def setUp(self): self.ag = sprite.AbstractGroup() self.ag2 = sprite.AbstractGroup() @@ -24,9 +25,9 @@ class SpriteCollideTest( unittest.TestCase ): self.s2 = sprite.Sprite(self.ag2) self.s3 = sprite.Sprite(self.ag2) - self.s1.image = pygame.Surface((50,10), pygame.SRCALPHA, 32) - self.s2.image = pygame.Surface((10,10), pygame.SRCALPHA, 32) - self.s3.image = pygame.Surface((10,10), pygame.SRCALPHA, 32) + self.s1.image = pygame.Surface((50, 10), pygame.SRCALPHA, 32) + self.s2.image = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + self.s3.image = pygame.Surface((10, 10), pygame.SRCALPHA, 32) self.s1.rect = self.s1.image.get_rect() self.s2.rect = self.s2.image.get_rect() @@ -36,44 +37,39 @@ class SpriteCollideTest( unittest.TestCase ): def test_spritecollide__works_if_collided_cb_is_None(self): # Test that sprites collide without collided function. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, collided = None - ), - [self.s2] + self.assertEqual( + sprite.spritecollide(self.s1, self.ag2, dokill=False, collided=None), + [self.s2], ) def test_spritecollide__works_if_collided_cb_not_passed(self): # Should also work when collided function isn't passed at all. - self.assertEqual(sprite.spritecollide ( - self.s1, self.ag2, dokill = False), - [self.s2] + self.assertEqual( + sprite.spritecollide(self.s1, self.ag2, dokill=False), [self.s2] ) def test_spritecollide__collided_must_be_a_callable(self): # Need to pass a callable. - self.assertRaises ( - TypeError, - sprite.spritecollide, self.s1, self.ag2, dokill = False, collided = 1 + self.assertRaises( + TypeError, sprite.spritecollide, self.s1, self.ag2, dokill=False, collided=1 ) def test_spritecollide__collided_defaults_to_collide_rect(self): # collide_rect should behave the same as default. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, collided = sprite.collide_rect + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_rect ), - [self.s2] + [self.s2], ) def test_collide_rect_ratio__ratio_of_one_like_default(self): # collide_rect_ratio should behave the same as default at a 1.0 ratio. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, - collided = sprite.collide_rect_ratio(1.0) + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_rect_ratio(1.0) ), - [self.s2] + [self.s2], ) def test_collide_rect_ratio__collides_all_at_ratio_of_twenty(self): @@ -81,28 +77,34 @@ class SpriteCollideTest( unittest.TestCase ): collided_func = sprite.collide_rect_ratio(20.0) expected_sprites = sorted(self.ag2.sprites(), key=id) - collided_sprites = sorted(sprite.spritecollide( - self.s1, self.ag2, dokill=False, collided=collided_func), key=id) + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) self.assertListEqual(collided_sprites, expected_sprites) def test_collide_circle__no_radius_set(self): # collide_circle with no radius set. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, collided = sprite.collide_circle + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_circle ), - [self.s2] + [self.s2], ) def test_collide_circle_ratio__no_radius_and_ratio_of_one(self): # collide_circle_ratio with no radius set, at a 1.0 ratio. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, - collided = sprite.collide_circle_ratio(1.0) + self.assertEqual( + sprite.spritecollide( + self.s1, + self.ag2, + dokill=False, + collided=sprite.collide_circle_ratio(1.0), ), - [self.s2] + [self.s2], ) def test_collide_circle_ratio__no_radius_and_ratio_of_twenty(self): @@ -110,8 +112,52 @@ class SpriteCollideTest( unittest.TestCase ): collided_func = sprite.collide_circle_ratio(20.0) expected_sprites = sorted(self.ag2.sprites(), key=id) - collided_sprites = sorted(sprite.spritecollide( - self.s1, self.ag2, dokill=False, collided=collided_func), key=id) + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + + self.assertListEqual(expected_sprites, collided_sprites) + + def test_collide_circle__radius_set_by_collide_circle_ratio(self): + # Call collide_circle_ratio with no radius set, at a 20.0 ratio. + # That should return group ag2 AND set the radius attribute of the + # sprites in such a way that collide_circle would give same result as + # if it had been called without the radius being set. + collided_func = sprite.collide_circle_ratio(20.0) + + sprite.spritecollide(self.s1, self.ag2, dokill=False, collided=collided_func) + + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_circle + ), + [self.s2], + ) + + def test_collide_circle_ratio__no_radius_and_ratio_of_two_twice(self): + # collide_circle_ratio with no radius set, at a 2.0 ratio, + # called twice to check if the bug where the calculated radius + # is not stored correctly in the radius attribute of each sprite. + collided_func = sprite.collide_circle_ratio(2.0) + + # Calling collide_circle_ratio will set the radius attribute of the + # sprites. If an incorrect value is stored then we will not get the + # same result next time it is called: + expected_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) self.assertListEqual(expected_sprites, collided_sprites) @@ -124,8 +170,11 @@ class SpriteCollideTest( unittest.TestCase ): expected_sprites = sorted(self.ag2.sprites(), key=id) collided_sprites = sorted( - sprite.spritecollide(self.s1, self.ag2, dokill=False, - collided=collided_func), key=id) + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) self.assertListEqual(expected_sprites, collided_sprites) @@ -137,24 +186,27 @@ class SpriteCollideTest( unittest.TestCase ): collided_func = sprite.collide_circle_ratio(0.5) expected_sprites = sorted(self.ag2.sprites(), key=id) - collided_sprites = sorted(sprite.spritecollide( - self.s1, self.ag2, dokill=False, collided=collided_func), key=id) + collided_sprites = sorted( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=collided_func + ), + key=id, + ) self.assertListEqual(expected_sprites, collided_sprites) def test_collide_mask__opaque(self): # make some fully opaque sprites that will collide with masks. - self.s1.image.fill((255,255,255,255)) - self.s2.image.fill((255,255,255,255)) - self.s3.image.fill((255,255,255,255)) + self.s1.image.fill((255, 255, 255, 255)) + self.s2.image.fill((255, 255, 255, 255)) + self.s3.image.fill((255, 255, 255, 255)) # masks should be autogenerated from image if they don't exist. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, - collided = sprite.collide_mask + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask ), - [self.s2] + [self.s2], ) self.s1.mask = pygame.mask.from_surface(self.s1.image) @@ -162,32 +214,30 @@ class SpriteCollideTest( unittest.TestCase ): self.s3.mask = pygame.mask.from_surface(self.s3.image) # with set masks. - self.assertEqual ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, - collided = sprite.collide_mask + self.assertEqual( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask ), - [self.s2] + [self.s2], ) def test_collide_mask__transparent(self): # make some sprites that are fully transparent, so they won't collide. - self.s1.image.fill((255,255,255,0)) - self.s2.image.fill((255,255,255,0)) - self.s3.image.fill((255,255,255,0)) + self.s1.image.fill((255, 255, 255, 0)) + self.s2.image.fill((255, 255, 255, 0)) + self.s3.image.fill((255, 255, 255, 0)) self.s1.mask = pygame.mask.from_surface(self.s1.image, 255) self.s2.mask = pygame.mask.from_surface(self.s2.image, 255) self.s3.mask = pygame.mask.from_surface(self.s3.image, 255) - self.assertFalse ( - sprite.spritecollide ( - self.s1, self.ag2, dokill = False, collided = sprite.collide_mask + self.assertFalse( + sprite.spritecollide( + self.s1, self.ag2, dokill=False, collided=sprite.collide_mask ) ) def test_spritecollideany__without_collided_callback(self): - # pygame.sprite.spritecollideany(sprite, group) -> sprite # finds any sprites that collide @@ -223,7 +273,6 @@ class SpriteCollideTest( unittest.TestCase ): self.assertIn(collided_sprite, expected_sprite_choices) def test_spritecollideany__with_collided_callback(self): - # pygame.sprite.spritecollideany(sprite, group) -> sprite # finds any sprites that collide @@ -240,10 +289,13 @@ class SpriteCollideTest( unittest.TestCase ): return_container = [True] # This function is configurable using the mutable default arguments! - def collided_callback(spr_a, spr_b, - arg_dict_a=arg_dict_a, arg_dict_b=arg_dict_b, - return_container=return_container): - + def collided_callback( + spr_a, + spr_b, + arg_dict_a=arg_dict_a, + arg_dict_b=arg_dict_b, + return_container=return_container, + ): count = arg_dict_a.get(spr_a, 0) arg_dict_a[spr_a] = 1 + count @@ -255,8 +307,7 @@ class SpriteCollideTest( unittest.TestCase ): # This should return a sprite from self.ag2 because the callback # function (collided_callback()) currently returns True. expected_sprite_choices = self.ag2.sprites() - collided_sprite = sprite.spritecollideany(self.s1, self.ag2, - collided_callback) + collided_sprite = sprite.spritecollideany(self.s1, self.ag2, collided_callback) self.assertIn(collided_sprite, expected_sprite_choices) @@ -278,8 +329,7 @@ class SpriteCollideTest( unittest.TestCase ): # This should return None because the callback function # (collided_callback()) currently returns False. - collided_sprite = sprite.spritecollideany(self.s1, self.ag2, - collided_callback) + collided_sprite = sprite.spritecollideany(self.s1, self.ag2, collided_callback) self.assertIsNone(collided_sprite) @@ -294,7 +344,6 @@ class SpriteCollideTest( unittest.TestCase ): self.assertEqual(arg_dict_b[s], 1) def test_groupcollide__without_collided_callback(self): - # pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb) -> dict # collision detection between group and group @@ -331,28 +380,30 @@ class SpriteCollideTest( unittest.TestCase ): self.assertDictEqual(expected_dict, crashed) def test_groupcollide__with_collided_callback(self): - collided_callback_true = lambda spr_a, spr_b: True collided_callback_false = lambda spr_a, spr_b: False # test no kill expected_dict = {} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False, - collided_callback_false) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_false + ) self.assertDictEqual(expected_dict, crashed) expected_dict = {self.s1: sorted(self.ag2.sprites(), key=id)} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False, - collided_callback_true) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_true + ) for value in crashed.values(): value.sort(key=id) self.assertDictEqual(expected_dict, crashed) # expected_dict is the same again for this collide - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, False, - collided_callback_true) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, False, collided_callback_true + ) for value in crashed.values(): value.sort(key=id) @@ -360,22 +411,25 @@ class SpriteCollideTest( unittest.TestCase ): # Test dokill2=True (kill colliding sprites in second group). expected_dict = {} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, True, - collided_callback_false) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_false + ) self.assertDictEqual(expected_dict, crashed) expected_dict = {self.s1: sorted(self.ag2.sprites(), key=id)} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, True, - collided_callback_true) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_true + ) for value in crashed.values(): value.sort(key=id) self.assertDictEqual(expected_dict, crashed) expected_dict = {} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, False, True, - collided_callback_true) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, False, True, collided_callback_true + ) self.assertDictEqual(expected_dict, crashed) @@ -383,20 +437,23 @@ class SpriteCollideTest( unittest.TestCase ): self.ag.add(self.s2) self.ag2.add(self.s3) expected_dict = {} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, True, False, - collided_callback_false) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_false + ) self.assertDictEqual(expected_dict, crashed) expected_dict = {self.s1: [self.s3], self.s2: [self.s3]} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, True, False, - collided_callback_true) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_true + ) self.assertDictEqual(expected_dict, crashed) expected_dict = {} - crashed = pygame.sprite.groupcollide(self.ag, self.ag2, True, False, - collided_callback_true) + crashed = pygame.sprite.groupcollide( + self.ag, self.ag2, True, False, collided_callback_true + ) self.assertDictEqual(expected_dict, crashed) @@ -430,7 +487,8 @@ class SpriteCollideTest( unittest.TestCase ): ################################################################################ -class AbstractGroupTypeTest( unittest.TestCase ): + +class AbstractGroupTypeTest(unittest.TestCase): def setUp(self): self.ag = sprite.AbstractGroup() self.ag2 = sprite.AbstractGroup() @@ -440,31 +498,31 @@ class AbstractGroupTypeTest( unittest.TestCase ): self.s4 = sprite.Sprite(self.ag2) self.s1.image = pygame.Surface((10, 10)) - self.s1.image.fill(pygame.Color('red')) + self.s1.image.fill(pygame.Color("red")) self.s1.rect = self.s1.image.get_rect() self.s2.image = pygame.Surface((10, 10)) - self.s2.image.fill(pygame.Color('green')) + self.s2.image.fill(pygame.Color("green")) self.s2.rect = self.s2.image.get_rect() self.s2.rect.left = 10 self.s3.image = pygame.Surface((10, 10)) - self.s3.image.fill(pygame.Color('blue')) + self.s3.image.fill(pygame.Color("blue")) self.s3.rect = self.s3.image.get_rect() self.s3.rect.top = 10 self.s4.image = pygame.Surface((10, 10)) - self.s4.image.fill(pygame.Color('white')) + self.s4.image.fill(pygame.Color("white")) self.s4.rect = self.s4.image.get_rect() self.s4.rect.left = 10 self.s4.rect.top = 10 self.bg = pygame.Surface((20, 20)) self.scr = pygame.Surface((20, 20)) - self.scr.fill(pygame.Color('grey')) + self.scr.fill(pygame.Color("grey")) - def test_has( self ): - " See if AbstractGroup.has() works as expected. " + def test_has(self): + "See if AbstractGroup.has() works as expected." self.assertEqual(True, self.s1 in self.ag) @@ -475,8 +533,7 @@ class AbstractGroupTypeTest( unittest.TestCase ): # see if one of them not being in there. self.assertNotEqual(True, self.ag.has([self.s1, self.s2, self.s3])) self.assertNotEqual(True, self.ag.has(self.s1, self.s2, self.s3)) - self.assertNotEqual(True, self.ag.has(self.s1, - sprite.Group(self.s2, self.s3))) + self.assertNotEqual(True, self.ag.has(self.s1, sprite.Group(self.s2, self.s3))) self.assertNotEqual(True, self.ag.has(self.s1, [self.s2, self.s3])) # test empty list processing @@ -507,24 +564,20 @@ class AbstractGroupTypeTest( unittest.TestCase ): self.assertIn(self.s1, self.ag2) def test_clear(self): - self.ag.draw(self.scr) self.ag.clear(self.scr, self.bg) - self.assertEqual((0, 0, 0, 255), - self.scr.get_at((5, 5))) - self.assertEqual((0, 0, 0, 255), - self.scr.get_at((15, 5))) + self.assertEqual((0, 0, 0, 255), self.scr.get_at((5, 5))) + self.assertEqual((0, 0, 0, 255), self.scr.get_at((15, 5))) def test_draw(self): - self.ag.draw(self.scr) - self.assertEqual((255, 0, 0, 255), - self.scr.get_at((5, 5))) - self.assertEqual((0, 255, 0, 255), - self.scr.get_at((15, 5))) + self.assertEqual((255, 0, 0, 255), self.scr.get_at((5, 5))) + self.assertEqual((0, 255, 0, 255), self.scr.get_at((15, 5))) - def test_empty(self): + self.assertEqual(self.ag.spritedict[self.s1], pygame.Rect(0, 0, 10, 10)) + self.assertEqual(self.ag.spritedict[self.s2], pygame.Rect(10, 0, 10, 10)) + def test_empty(self): self.ag.empty() self.assertFalse(self.s1 in self.ag) self.assertFalse(self.s2 in self.ag) @@ -534,7 +587,6 @@ class AbstractGroupTypeTest( unittest.TestCase ): self.assertFalse(self.ag.has_internal(self.s3)) def test_remove(self): - # Test removal of 1 sprite self.ag.remove(self.s1) self.assertFalse(self.ag in self.s1.groups()) @@ -559,7 +611,6 @@ class AbstractGroupTypeTest( unittest.TestCase ): self.assertFalse(self.ag.has(self.s1, self.s2, self.s3, self.s4)) def test_remove_internal(self): - self.ag.remove_internal(self.s1) self.assertFalse(self.ag.has_internal(self.s1)) @@ -570,11 +621,12 @@ class AbstractGroupTypeTest( unittest.TestCase ): self.assertListEqual(sprite_list, expected_sprites) def test_update(self): - class test_sprite(pygame.sprite.Sprite): sink = [] + def __init__(self, *groups): pygame.sprite.Sprite.__init__(self, *groups) + def update(self, *args): self.sink += args @@ -583,11 +635,30 @@ class AbstractGroupTypeTest( unittest.TestCase ): self.assertEqual(test_sprite.sink, [1, 2, 3]) + def test_update_with_kwargs(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + sink_kwargs = {} + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args, **kwargs): + self.sink += args + self.sink_kwargs.update(kwargs) + + s = test_sprite(self.ag) + self.ag.update(1, 2, 3, foo=4, bar=5) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + self.assertEqual(test_sprite.sink_kwargs, {"foo": 4, "bar": 5}) + ################################################################################ # A base class to share tests between similar classes + class LayeredGroupBase: def test_get_layer_of_sprite(self): expected_layer = 666 @@ -756,7 +827,7 @@ class LayeredGroupBase: sprite_count = 10 for i in range(sprite_count): sprites.append(self.sprite()) - sprites[-1].rect = 0 + sprites[-1].rect = pygame.Rect((0, 0, 0, 0)) self.LG.add(sprites) @@ -801,8 +872,7 @@ class LayeredGroupBase: self.assertListEqual(layers, expected_layers) def test_add__layers_are_correct(self): - layers = [1, 4, 6, 8, 3, 6, 2, 6, 4, 5, 6, 1, 0, 9, 7, 6, 54, 8, 2, - 43, 6, 1] + layers = [1, 4, 6, 8, 3, 6, 2, 6, 4, 5, 6, 1, 0, 9, 7, 6, 54, 8, 2, 43, 6, 1] for lay in layers: self.LG.add(self.sprite(), layer=lay) layers.sort() @@ -832,6 +902,19 @@ class LayeredGroupBase: self.assertEqual(spr2.layer, expected_layer) + def test_get_sprites_at(self): + sprites = [] + expected_sprites = [] + for i in range(3): + spr = self.sprite() + spr.rect = pygame.Rect(i * 50, i * 50, 100, 100) + sprites.append(spr) + if i < 2: + expected_sprites.append(spr) + self.LG.add(sprites) + result = self.LG.get_sprites_at((50, 50)) + self.assertEqual(result, expected_sprites) + def test_get_top_layer(self): layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] for i in layers: @@ -841,8 +924,7 @@ class LayeredGroupBase: self.assertEqual(top_layer, self.LG.get_top_layer()) self.assertEqual(top_layer, max(layers)) self.assertEqual(top_layer, max(self.LG._spritelayers.values())) - self.assertEqual(top_layer, - self.LG._spritelayers[self.LG._spritelist[-1]]) + self.assertEqual(top_layer, self.LG._spritelayers[self.LG._spritelist[-1]]) def test_get_bottom_layer(self): layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] @@ -853,8 +935,7 @@ class LayeredGroupBase: self.assertEqual(bottom_layer, self.LG.get_bottom_layer()) self.assertEqual(bottom_layer, min(layers)) self.assertEqual(bottom_layer, min(self.LG._spritelayers.values())) - self.assertEqual(bottom_layer, - self.LG._spritelayers[self.LG._spritelist[0]]) + self.assertEqual(bottom_layer, self.LG._spritelayers[self.LG._spritelist[0]]) def test_move_to_front(self): layers = [1, 5, 2, 8, 4, 5, 3, 88, 23, 0] @@ -893,8 +974,40 @@ class LayeredGroupBase: def test_get_sprites_from_layer(self): sprites = {} - layers = [1, 4, 5, 6, 3, 7, 8, 2, 1, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 0, 1, 6, 5, 4, 3, 2] + layers = [ + 1, + 4, + 5, + 6, + 3, + 7, + 8, + 2, + 1, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + 1, + 6, + 5, + 4, + 3, + 2, + ] for lay in layers: spr = self.sprite() spr._layer = lay @@ -916,13 +1029,12 @@ class LayeredGroupBase: def test_switch_layer(self): sprites1 = [] sprites2 = [] - layers = [3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 2, - 3, 2, 3] + layers = [3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3] for lay in layers: spr = self.sprite() spr._layer = lay self.LG.add(spr) - if lay==2: + if lay == 2: sprites1.append(spr) else: sprites2.append(spr) @@ -956,18 +1068,21 @@ class LayeredGroupBase: ########################## LAYERED RENDER GROUP TESTS ########################## + class LayeredUpdatesTypeTest__SpriteTest(LayeredGroupBase, unittest.TestCase): sprite = sprite.Sprite def setUp(self): self.LG = sprite.LayeredUpdates() + class LayeredUpdatesTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): sprite = sprite.DirtySprite def setUp(self): self.LG = sprite.LayeredUpdates() + class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): sprite = sprite.DirtySprite @@ -1009,10 +1124,10 @@ class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): # 3. Moves the red_sprite and calls LayeredDirty.draw(surface) a few # times. # 4. Checks to make sure the sprites were redrawn correctly. - RED = pygame.Color('red') - BLUE = pygame.Color('blue') - WHITE = pygame.Color('white') - YELLOW = pygame.Color('yellow') + RED = pygame.Color("red") + BLUE = pygame.Color("blue") + WHITE = pygame.Color("white") + YELLOW = pygame.Color("yellow") surface = pygame.Surface((60, 80)) surface.fill(WHITE) @@ -1037,8 +1152,9 @@ class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): blue_sprite.image = image_source # The rect is a bit smaller than the source_rect to make sure # LayeredDirty.draw() is using the correct dimensions. - blue_sprite.rect = pygame.Rect(start_pos, - (blue_sprite_source.w - 7, blue_sprite_source.h - 7)) + blue_sprite.rect = pygame.Rect( + start_pos, (blue_sprite_source.w - 7, blue_sprite_source.h - 7) + ) blue_sprite.source_rect = blue_sprite_source start_x, start_y = blue_sprite.rect.topleft end_x = start_x + blue_sprite.source_rect.w @@ -1069,7 +1185,7 @@ class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): surface.lock() # Lock surface for possible speed up. try: for y in range(start_y, end_y): - for x in range(start_x, end_x): + for x in range(start_x, end_x): if red_sprite.rect.collidepoint(x, y): expected_color = RED else: @@ -1077,8 +1193,7 @@ class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): color = surface.get_at((x, y)) - self.assertEqual(color, expected_color, - 'pos=({}, {})'.format(x, y)) + self.assertEqual(color, expected_color, f"pos=({x}, {y})") finally: surface.unlock() @@ -1101,6 +1216,7 @@ class LayeredDirtyTypeTest__DirtySprite(LayeredGroupBase, unittest.TestCase): # # tests common between sprite classes + class SpriteBase: def setUp(self): self.groups = [] @@ -1110,7 +1226,6 @@ class SpriteBase: self.sprite = self.Sprite() def test_add_internal(self): - for g in self.groups: self.sprite.add_internal(g) @@ -1118,7 +1233,6 @@ class SpriteBase: self.assertIn(g, self.sprite.groups()) def test_remove_internal(self): - for g in self.groups: self.sprite.add_internal(g) @@ -1129,11 +1243,13 @@ class SpriteBase: self.assertFalse(g in self.sprite.groups()) def test_update(self): - + # What does this and the next test actually test? class test_sprite(pygame.sprite.Sprite): sink = [] + def __init__(self, *groups): pygame.sprite.Sprite.__init__(self, *groups) + def update(self, *args): self.sink += args @@ -1142,6 +1258,24 @@ class SpriteBase: self.assertEqual(test_sprite.sink, [1, 2, 3]) + def test_update_with_kwargs(self): + class test_sprite(pygame.sprite.Sprite): + sink = [] + sink_dict = {} + + def __init__(self, *groups): + pygame.sprite.Sprite.__init__(self, *groups) + + def update(self, *args, **kwargs): + self.sink += args + self.sink_dict.update(kwargs) + + s = test_sprite() + s.update(1, 2, 3, foo=4, bar=5) + + self.assertEqual(test_sprite.sink, [1, 2, 3]) + self.assertEqual(test_sprite.sink_dict, {"foo": 4, "bar": 5}) + def test___init____added_to_groups_passed(self): expected_groups = sorted(self.groups, key=id) sprite = self.Sprite(self.groups) @@ -1157,8 +1291,9 @@ class SpriteBase: self.assertListEqual(groups, expected_groups) def test_alive(self): - self.assertFalse(self.sprite.alive(), - "Sprite should not be alive if in no groups") + self.assertFalse( + self.sprite.alive(), "Sprite should not be alive if in no groups" + ) self.sprite.add(self.groups) @@ -1166,7 +1301,7 @@ class SpriteBase: def test_groups(self): for i, g in enumerate(self.groups): - expected_groups = sorted(self.groups[:i+1], key=id) + expected_groups = sorted(self.groups[: i + 1], key=id) self.sprite.add(g) groups = sorted(self.sprite.groups(), key=id) @@ -1191,25 +1326,54 @@ class SpriteBase: ############################## SPRITE CLASS TESTS ############################## + class SpriteTypeTest(SpriteBase, unittest.TestCase): Sprite = sprite.Sprite - Groups = [ sprite.Group, - sprite.LayeredUpdates, - sprite.RenderUpdates, - sprite.OrderedUpdates, ] + Groups = [ + sprite.Group, + sprite.LayeredUpdates, + sprite.RenderUpdates, + sprite.OrderedUpdates, + ] + class DirtySpriteTypeTest(SpriteBase, unittest.TestCase): Sprite = sprite.DirtySprite - Groups = [ sprite.Group, - sprite.LayeredUpdates, - sprite.RenderUpdates, - sprite.OrderedUpdates, - sprite.LayeredDirty, ] + Groups = [ + sprite.Group, + sprite.LayeredUpdates, + sprite.RenderUpdates, + sprite.OrderedUpdates, + sprite.LayeredDirty, + ] + + +class WeakSpriteTypeTest(SpriteTypeTest): + Sprite = sprite.WeakSprite + + def test_weak_group_ref(self): + """ + We create a list of groups, add them to the sprite. + When we then delete the groups, the sprite should be "dead" + """ + import gc + + groups = [Group() for Group in self.Groups] + self.sprite.add(groups) + del groups + gc.collect() + self.assertFalse(self.sprite.alive()) + + +class DirtyWeakSpriteTypeTest(DirtySpriteTypeTest, WeakSpriteTypeTest): + Sprite = sprite.WeakDirtySprite + ############################## BUG TESTS ####################################### + class SingleGroupBugsTest(unittest.TestCase): def test_memoryleak_bug(self): # For memoryleak bug posted to mailing list by Tobias Steinrücken on 16/11/10. @@ -1221,7 +1385,7 @@ class SingleGroupBugsTest(unittest.TestCase): class MySprite(sprite.Sprite): def __init__(self, *args, **kwargs): sprite.Sprite.__init__(self, *args, **kwargs) - self.image = pygame.Surface( (2, 4), 0, 24 ) + self.image = pygame.Surface((2, 4), 0, 24) self.rect = self.image.get_rect() g = sprite.GroupSingle() @@ -1244,5 +1408,5 @@ class SingleGroupBugsTest(unittest.TestCase): ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/surface_test.py b/venv/Lib/site-packages/pygame/tests/surface_test.py index 6d5a917291b23bfbf57536562a0152fe0e98f0c8..5ce78b6e8075874bf50f04d1785fa254a18479d0 100644 --- a/venv/Lib/site-packages/pygame/tests/surface_test.py +++ b/venv/Lib/site-packages/pygame/tests/surface_test.py @@ -1,17 +1,17 @@ import os - import unittest from pygame.tests import test_utils from pygame.tests.test_utils import ( - example_path, AssertRaisesRegexMixin, SurfaceSubclass) + example_path, + SurfaceSubclass, +) + try: from pygame.tests.test_utils.arrinter import * except (ImportError, NameError): pass - import pygame from pygame.locals import * -from pygame.compat import xrange_, as_bytes, as_unicode from pygame.bufferproxy import BufferProxy import platform @@ -19,32 +19,19 @@ import gc import weakref import ctypes -IS_PYPY = 'PyPy' == platform.python_implementation() - -def intify(i): - """If i is a long, cast to an int while preserving the bits""" - if 0x80000000 & i: - return int((0xFFFFFFFF & i)) - return i - -def longify(i): - """If i is an int, cast to a long while preserving the bits""" - if i < 0: - return 0xFFFFFFFF & i - return long(i) +IS_PYPY = "PyPy" == platform.python_implementation() -class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): +class SurfaceTypeTest(unittest.TestCase): def test_surface__pixel_format_as_surface_subclass(self): """Ensure a subclassed surface can be used for pixel format when creating a new surface.""" expected_depth = 16 expected_flags = SRCALPHA expected_size = (13, 37) - depth_surface = SurfaceSubclass((11, 21), expected_flags, - expected_depth) + depth_surface = SurfaceSubclass((11, 21), expected_flags, expected_depth) - surface = pygame.Surface(expected_size, 0, depth_surface) + surface = pygame.Surface(expected_size, expected_flags, depth_surface) self.assertIsNot(surface, depth_surface) self.assertIsInstance(surface, pygame.Surface) @@ -53,9 +40,17 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(surface.get_flags(), expected_flags) self.assertEqual(surface.get_bitsize(), expected_depth) - def test_set_clip( self ): - """ see if surface.set_clip(None) works correctly. - """ + def test_surface_created_opaque_black(self): + surf = pygame.Surface((20, 20)) + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 255)) + + # See https://github.com/pygame/pygame/issues/1395 + pygame.display.set_mode((500, 500)) + surf = pygame.Surface((20, 20)) + self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 255)) + + def test_set_clip(self): + """see if surface.set_clip(None) works correctly.""" s = pygame.Surface((800, 600)) r = pygame.Rect(10, 10, 10, 10) s.set_clip(r) @@ -67,57 +62,94 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(res[2], 800) def test_print(self): - surf = pygame.Surface((70,70), 0, 32) - self.assertEqual(repr(surf), '') + surf = pygame.Surface((70, 70), 0, 32) + self.assertEqual(repr(surf), "") def test_keyword_arguments(self): - surf = pygame.Surface((70,70), flags=SRCALPHA, depth=32) + surf = pygame.Surface((70, 70), flags=SRCALPHA, depth=32) self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA) self.assertEqual(surf.get_bitsize(), 32) # sanity check to make sure the check below is valid - surf_16 = pygame.Surface((70,70), 0, 16) + surf_16 = pygame.Surface((70, 70), 0, 16) self.assertEqual(surf_16.get_bytesize(), 2) # try again with an argument list - surf_16 = pygame.Surface((70,70), depth=16) + surf_16 = pygame.Surface((70, 70), depth=16) self.assertEqual(surf_16.get_bytesize(), 2) def test_set_at(self): - - #24bit surfaces - s = pygame.Surface( (100, 100), 0, 24) - s.fill((0,0,0)) + # 24bit surfaces + s = pygame.Surface((100, 100), 0, 24) + s.fill((0, 0, 0)) # set it with a tuple. - s.set_at((0,0), (10,10,10, 255)) - r = s.get_at((0,0)) + s.set_at((0, 0), (10, 10, 10, 255)) + r = s.get_at((0, 0)) self.assertIsInstance(r, pygame.Color) - self.assertEqual(r, (10,10,10, 255)) + self.assertEqual(r, (10, 10, 10, 255)) # try setting a color with a single integer. - s.fill((0,0,0,255)) - s.set_at ((10, 1), 0x0000FF) - r = s.get_at((10,1)) - self.assertEqual(r, (0,0,255, 255)) + s.fill((0, 0, 0, 255)) + s.set_at((10, 1), 0x0000FF) + r = s.get_at((10, 1)) + self.assertEqual(r, (0, 0, 255, 255)) + def test_set_at__big_endian(self): + """png files are loaded in big endian format (BGR rather than RGB)""" + pygame.display.init() + try: + image = pygame.image.load(example_path(os.path.join("data", "BGR.png"))) + # Check they start red, green and blue + self.assertEqual(image.get_at((10, 10)), pygame.Color(255, 0, 0)) + self.assertEqual(image.get_at((10, 20)), pygame.Color(0, 255, 0)) + self.assertEqual(image.get_at((10, 40)), pygame.Color(0, 0, 255)) + # Set three pixels that are already red, green, blue + # to red, green and, blue with set_at: + image.set_at((10, 10), pygame.Color(255, 0, 0)) + image.set_at((10, 20), pygame.Color(0, 255, 0)) + image.set_at((10, 40), pygame.Color(0, 0, 255)) + + # Check they still are + self.assertEqual(image.get_at((10, 10)), pygame.Color(255, 0, 0)) + self.assertEqual(image.get_at((10, 20)), pygame.Color(0, 255, 0)) + self.assertEqual(image.get_at((10, 40)), pygame.Color(0, 0, 255)) + + finally: + pygame.display.quit() def test_SRCALPHA(self): # has the flag been passed in ok? - surf = pygame.Surface((70,70), SRCALPHA, 32) + surf = pygame.Surface((70, 70), SRCALPHA, 32) self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA) - #24bit surfaces can not have SRCALPHA. + # 24bit surfaces can not have SRCALPHA. self.assertRaises(ValueError, pygame.Surface, (100, 100), pygame.SRCALPHA, 24) # if we have a 32 bit surface, the SRCALPHA should have worked too. - surf2 = pygame.Surface((70,70), SRCALPHA) + surf2 = pygame.Surface((70, 70), SRCALPHA) if surf2.get_bitsize() == 32: self.assertEqual(surf2.get_flags() & SRCALPHA, SRCALPHA) + def test_flags_default0_nodisplay(self): + """is set to zero, and SRCALPHA is not set by default with no display initialized.""" + pygame.display.quit() + surf = pygame.Surface((70, 70)) + self.assertEqual(surf.get_flags() & SRCALPHA, 0) + + def test_flags_default0_display(self): + """is set to zero, and SRCALPH is not set by default even when the display is initialized.""" + pygame.display.set_mode((320, 200)) + try: + surf = pygame.Surface((70, 70)) + self.assertEqual(surf.get_flags() & SRCALPHA, 0) + finally: + pygame.display.quit() + def test_masks(self): def make_surf(bpp, flags, masks): pygame.Surface((10, 10), flags, bpp, masks) + # With some masks SDL_CreateRGBSurface does not work properly. masks = (0xFF000000, 0xFF0000, 0xFF00, 0) self.assertEqual(make_surf(32, 0, masks), None) @@ -129,42 +161,42 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): masks = (0x6F0000, 0xFF00, 0xFF, 0) self.assertRaises(ValueError, make_surf, 32, 0, masks) - def test_get_bounding_rect (self): - surf = pygame.Surface ((70, 70), SRCALPHA, 32) - surf.fill((0,0,0,0)) + def test_get_bounding_rect(self): + surf = pygame.Surface((70, 70), SRCALPHA, 32) + surf.fill((0, 0, 0, 0)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.width, 0) self.assertEqual(bound_rect.height, 0) - surf.set_at((30,30),(255,255,255,1)) + surf.set_at((30, 30), (255, 255, 255, 1)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.left, 30) self.assertEqual(bound_rect.top, 30) self.assertEqual(bound_rect.width, 1) self.assertEqual(bound_rect.height, 1) - surf.set_at((29,29),(255,255,255,1)) + surf.set_at((29, 29), (255, 255, 255, 1)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.left, 29) self.assertEqual(bound_rect.top, 29) self.assertEqual(bound_rect.width, 2) self.assertEqual(bound_rect.height, 2) - surf = pygame.Surface ((70, 70), 0, 24) - surf.fill((0,0,0)) + surf = pygame.Surface((70, 70), 0, 24) + surf.fill((0, 0, 0)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.width, surf.get_width()) self.assertEqual(bound_rect.height, surf.get_height()) - surf.set_colorkey((0,0,0)) + surf.set_colorkey((0, 0, 0)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.width, 0) self.assertEqual(bound_rect.height, 0) - surf.set_at((30,30),(255,255,255)) + surf.set_at((30, 30), (255, 255, 255)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.left, 30) self.assertEqual(bound_rect.top, 30) self.assertEqual(bound_rect.width, 1) self.assertEqual(bound_rect.height, 1) - surf.set_at((60,60),(255,255,255)) + surf.set_at((60, 60), (255, 255, 255)) bound_rect = surf.get_bounding_rect() self.assertEqual(bound_rect.left, 30) self.assertEqual(bound_rect.top, 30) @@ -183,7 +215,7 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_copy(self): """Ensure a surface can be copied.""" color = (25, 25, 25, 25) - s1 = pygame.Surface((32,32), pygame.SRCALPHA, 32) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) s1.fill(color) s2 = s1.copy() @@ -192,13 +224,13 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): s2rect = s2.get_rect() self.assertEqual(s1rect.size, s2rect.size) - self.assertEqual(s2.get_at((10,10)), color) + self.assertEqual(s2.get_at((10, 10)), color) def test_fill(self): """Ensure a surface can be filled.""" color = (25, 25, 25, 25) fill_rect = pygame.Rect(0, 0, 16, 16) - s1 = pygame.Surface((32,32), pygame.SRCALPHA, 32) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) s1.fill(color, fill_rect) for pt in test_utils.rect_area_pts(fill_rect): @@ -207,22 +239,189 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): for pt in test_utils.rect_outer_bounds(fill_rect): self.assertNotEqual(s1.get_at(pt), color) - def test_fill_negative_coordinates(self): + def test_fill_rle(self): + """Test RLEACCEL flag with fill()""" + color = (250, 25, 25, 255) + + surf = pygame.Surface((32, 32)) + blit_surf = pygame.Surface((32, 32)) + + blit_surf.set_colorkey((255, 0, 255), pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK) + surf.blit(blit_surf, (0, 0)) + blit_surf.fill(color) + self.assertEqual( + blit_surf.mustlock(), (blit_surf.get_flags() & pygame.RLEACCEL) != 0 + ) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL) + + def test_mustlock_rle(self): + """Test RLEACCEL flag with mustlock()""" + surf = pygame.Surface((100, 100)) + blit_surf = pygame.Surface((100, 100)) + blit_surf.set_colorkey((0, 0, 255), pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK) + surf.blit(blit_surf, (0, 0)) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL) + self.assertTrue(blit_surf.mustlock()) + + def test_mustlock_surf_alpha_rle(self): + """Test RLEACCEL flag with mustlock() on a surface + with per pixel alpha - new feature in SDL2""" + surf = pygame.Surface((100, 100)) + blit_surf = pygame.Surface((100, 100), depth=32, flags=pygame.SRCALPHA) + blit_surf.set_colorkey((192, 191, 192, 255), pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK) + surf.blit(blit_surf, (0, 0)) + self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL) + self.assertTrue(blit_surf.get_flags() & pygame.SRCALPHA) + self.assertTrue(blit_surf.mustlock()) + + def test_copy_rle(self): + """Test copying a surface set to use run length encoding""" + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255, 0, 255), pygame.RLEACCEL) + self.assertTrue(s1.get_flags() & pygame.RLEACCELOK) + + newsurf = s1.copy() + self.assertTrue(s1.get_flags() & pygame.RLEACCELOK) + self.assertTrue(newsurf.get_flags() & pygame.RLEACCELOK) + + def test_subsurface_rle(self): + """Ensure an RLE sub-surface works independently of its parent.""" + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255, 0, 255), pygame.RLEACCEL) + s1.fill(color) + s2 = s1.subsurface(sub_rect) + s2.fill(color2) + s0.blit(s1, (0, 0)) + self.assertTrue(s1.get_flags() & pygame.RLEACCEL) + self.assertTrue(not s2.get_flags() & pygame.RLEACCEL) + + def test_subsurface_rle2(self): + """Ensure an RLE sub-surface works independently of its parent.""" + color = (250, 25, 25, 255) + color2 = (200, 200, 250, 255) + sub_rect = pygame.Rect(16, 16, 16, 16) + + s0 = pygame.Surface((32, 32), 24) + s1 = pygame.Surface((32, 32), 24) + s1.set_colorkey((255, 0, 255), pygame.RLEACCEL) + s1.fill(color) + s2 = s1.subsurface(sub_rect) + s2.fill(color2) + s0.blit(s2, (0, 0)) + self.assertTrue(s1.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not s2.get_flags() & pygame.RLEACCELOK) + + def test_solarwolf_rle_usage(self): + """Test for error/crash when calling set_colorkey() followed + by convert twice in succession. Code originally taken + from solarwolf.""" + + def optimize(img): + clear = img.get_colorkey() + img.set_colorkey(clear, RLEACCEL) + self.assertEqual(img.get_colorkey(), clear) + return img.convert() + + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + + image = pygame.image.load(example_path(os.path.join("data", "alien1.png"))) + image = image.convert() + orig_colorkey = image.get_colorkey() + + image = optimize(image) + image = optimize(image) + self.assertTrue(image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not image.get_flags() & pygame.RLEACCEL) + self.assertEqual(image.get_colorkey(), orig_colorkey) + self.assertTrue(isinstance(image, pygame.Surface)) + finally: + pygame.display.quit() + + def test_solarwolf_rle_usage_2(self): + """Test for RLE status after setting alpha""" + + pygame.display.init() + try: + pygame.display.set_mode((640, 480), depth=32) + blit_to_surf = pygame.Surface((100, 100)) + + image = pygame.image.load(example_path(os.path.join("data", "alien1.png"))) + image = image.convert() + orig_colorkey = image.get_colorkey() + # set the colorkey with RLEACCEL, should add the RLEACCELOK flag + image.set_colorkey(orig_colorkey, RLEACCEL) + self.assertTrue(image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not image.get_flags() & pygame.RLEACCEL) + + # now blit the surface - should add the RLEACCEL flag + blit_to_surf.blit(image, (0, 0)) + self.assertTrue(image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(image.get_flags() & pygame.RLEACCEL) + + # Now set the alpha, without RLE acceleration - should strip all + # RLE flags + image.set_alpha(90) + self.assertTrue(not image.get_flags() & pygame.RLEACCELOK) + self.assertTrue(not image.get_flags() & pygame.RLEACCEL) + + finally: + pygame.display.quit() + + def test_set_alpha__set_colorkey_rle(self): + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) + blit_to_surf = pygame.Surface((80, 71)) + blit_to_surf.fill((255, 255, 255)) + + image = pygame.image.load(example_path(os.path.join("data", "alien1.png"))) + image = image.convert() + orig_colorkey = image.get_colorkey() + + # Add the RLE flag while setting alpha for the whole surface + image.set_alpha(90, RLEACCEL) + blit_to_surf.blit(image, (0, 0)) + sample_pixel_rle = blit_to_surf.get_at((50, 50)) + + # Now reset the colorkey to the original value with RLE + self.assertEqual(image.get_colorkey(), orig_colorkey) + image.set_colorkey(orig_colorkey, RLEACCEL) + blit_to_surf.fill((255, 255, 255)) + blit_to_surf.blit(image, (0, 0)) + sample_pixel_no_rle = blit_to_surf.get_at((50, 50)) + + self.assertAlmostEqual(sample_pixel_rle.r, sample_pixel_no_rle.r, delta=2) + self.assertAlmostEqual(sample_pixel_rle.g, sample_pixel_no_rle.g, delta=2) + self.assertAlmostEqual(sample_pixel_rle.b, sample_pixel_no_rle.b, delta=2) + + finally: + pygame.display.quit() + + def test_fill_negative_coordinates(self): # negative coordinates should be clipped by fill, and not draw outside the surface. color = (25, 25, 25, 25) color2 = (20, 20, 20, 25) fill_rect = pygame.Rect(-10, -10, 16, 16) - s1 = pygame.Surface((32,32), pygame.SRCALPHA, 32) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) r1 = s1.fill(color, fill_rect) - c = s1.get_at((0,0)) + c = s1.get_at((0, 0)) self.assertEqual(c, color) # make subsurface in the middle to test it doesn't over write. s2 = s1.subsurface((5, 5, 5, 5)) r2 = s2.fill(color2, (-3, -3, 5, 5)) - c2 = s1.get_at((4,4)) + c2 = s1.get_at((4, 4)) self.assertEqual(c, color) # rect returns the area we actually fill. @@ -244,7 +443,7 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_get_alpha(self): """Ensure a surface's alpha value can be retrieved.""" - s1 = pygame.Surface((32,32), pygame.SRCALPHA, 32) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) self.assertEqual(s1.get_alpha(), 255) @@ -259,29 +458,104 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_get_bytesize(self): """Ensure a surface's bit and byte sizes can be retrieved.""" - depth = 32 - depth_bytes = 4 - s1 = pygame.Surface((32, 32), pygame.SRCALPHA, depth) + pygame.display.init() + try: + depth = 32 + depth_bytes = 4 + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) + + depth = 15 + depth_bytes = 2 + s1 = pygame.Surface((32, 32), 0, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) - self.assertEqual(s1.get_bytesize(), depth_bytes) - self.assertEqual(s1.get_bitsize(), depth) + depth = 12 + depth_bytes = 2 + s1 = pygame.Surface((32, 32), 0, depth) + + self.assertEqual(s1.get_bytesize(), depth_bytes) + self.assertEqual(s1.get_bitsize(), depth) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_bytesize() + finally: + pygame.display.quit() ######################################################################## def test_get_flags(self): """Ensure a surface's flags can be retrieved.""" - s1 = pygame.Surface((32,32), pygame.SRCALPHA, 32) + s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32) self.assertEqual(s1.get_flags(), pygame.SRCALPHA) + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'requires a non-"dummy" SDL_VIDEODRIVER', + ) + def test_get_flags__display_surf(self): + pygame.display.init() + try: + # FULLSCREEN + screen_surf = pygame.display.set_mode((600, 400), flags=0) + self.assertFalse(screen_surf.get_flags() & pygame.FULLSCREEN) + + screen_surf = pygame.display.set_mode((600, 400), flags=pygame.FULLSCREEN) + self.assertTrue(screen_surf.get_flags() & pygame.FULLSCREEN) + + # NOFRAME + screen_surf = pygame.display.set_mode((600, 400), flags=0) + self.assertFalse(screen_surf.get_flags() & pygame.NOFRAME) + + screen_surf = pygame.display.set_mode((600, 400), flags=pygame.NOFRAME) + self.assertTrue(screen_surf.get_flags() & pygame.NOFRAME) + + # RESIZABLE + screen_surf = pygame.display.set_mode((600, 400), flags=0) + self.assertFalse(screen_surf.get_flags() & pygame.RESIZABLE) + + screen_surf = pygame.display.set_mode((600, 400), flags=pygame.RESIZABLE) + self.assertTrue(screen_surf.get_flags() & pygame.RESIZABLE) + + # OPENGL + screen_surf = pygame.display.set_mode((600, 400), flags=0) + # it can have an OPENGL flag by default on Macos? + if not (screen_surf.get_flags() & pygame.OPENGL): + self.assertFalse(screen_surf.get_flags() & pygame.OPENGL) + + try: + pygame.display.set_mode((200, 200), pygame.OPENGL, 32) + except pygame.error: + pass # If we can't create OPENGL surface don't try this test + else: + self.assertTrue(screen_surf.get_flags() & pygame.OPENGL) + finally: + pygame.display.quit() + ######################################################################## def test_get_parent(self): """Ensure a surface's parent can be retrieved.""" - parent = pygame.Surface((16, 16)) - child = parent.subsurface((0,0,5,5)) + pygame.display.init() + try: + parent = pygame.Surface((16, 16)) + child = parent.subsurface((0, 0, 5, 5)) - self.assertIs(child.get_parent(), parent) + self.assertIs(child.get_parent(), parent) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_parent() + finally: + pygame.display.quit() ######################################################################## @@ -297,8 +571,8 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_get_width__size_and_height(self): """Ensure a surface's size, width and height can be retrieved.""" - for w in xrange_(0, 255, 32): - for h in xrange_(0, 127, 15): + for w in range(0, 255, 32): + for h in range(0, 127, 15): s = pygame.Surface((w, h)) self.assertEqual(s.get_width(), w) self.assertEqual(s.get_height(), h) @@ -310,17 +584,17 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): # ValueErrors returned otherwise. Error = ValueError s = pygame.Surface((5, 7), 0, 8) - v2 = s.get_view('2') + v2 = s.get_view("2") - self.assertRaises(Error, s.get_view, '0') - self.assertRaises(Error, s.get_view, '1') + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") self.assertIsInstance(v2, BufferProxy) - self.assertRaises(Error, s.get_view, '3') + self.assertRaises(Error, s.get_view, "3") s = pygame.Surface((8, 7), 0, 8) length = s.get_bytesize() * s.get_width() * s.get_height() - v0 = s.get_view('0') - v1 = s.get_view('1') + v0 = s.get_view("0") + v1 = s.get_view("1") self.assertIsInstance(v0, BufferProxy) self.assertEqual(v0.length, length) @@ -328,17 +602,17 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(v1.length, length) s = pygame.Surface((5, 7), 0, 16) - v2 = s.get_view('2') + v2 = s.get_view("2") - self.assertRaises(Error, s.get_view, '0') - self.assertRaises(Error, s.get_view, '1') + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") self.assertIsInstance(v2, BufferProxy) - self.assertRaises(Error, s.get_view, '3') + self.assertRaises(Error, s.get_view, "3") s = pygame.Surface((8, 7), 0, 16) length = s.get_bytesize() * s.get_width() * s.get_height() - v0 = s.get_view('0') - v1 = s.get_view('1') + v0 = s.get_view("0") + v1 = s.get_view("1") self.assertIsInstance(v0, BufferProxy) self.assertEqual(v0.length, length) @@ -346,24 +620,24 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(v1.length, length) s = pygame.Surface((5, 7), pygame.SRCALPHA, 16) - v2 = s.get_view('2') + v2 = s.get_view("2") self.assertIsInstance(v2, BufferProxy) - self.assertRaises(Error, s.get_view, '3') + self.assertRaises(Error, s.get_view, "3") s = pygame.Surface((5, 7), 0, 24) - v2 = s.get_view('2') - v3 = s.get_view('3') + v2 = s.get_view("2") + v3 = s.get_view("3") - self.assertRaises(Error, s.get_view, '0') - self.assertRaises(Error, s.get_view, '1') + self.assertRaises(Error, s.get_view, "0") + self.assertRaises(Error, s.get_view, "1") self.assertIsInstance(v2, BufferProxy) self.assertIsInstance(v3, BufferProxy) s = pygame.Surface((8, 7), 0, 24) length = s.get_bytesize() * s.get_width() * s.get_height() - v0 = s.get_view('0') - v1 = s.get_view('1') + v0 = s.get_view("0") + v1 = s.get_view("1") self.assertIsInstance(v0, BufferProxy) self.assertEqual(v0.length, length) @@ -372,10 +646,10 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): s = pygame.Surface((5, 7), 0, 32) length = s.get_bytesize() * s.get_width() * s.get_height() - v0 = s.get_view('0') - v1 = s.get_view('1') - v2 = s.get_view('2') - v3 = s.get_view('3') + v0 = s.get_view("0") + v1 = s.get_view("1") + v2 = s.get_view("2") + v3 = s.get_view("3") self.assertIsInstance(v0, BufferProxy) self.assertEqual(v0.length, length) @@ -386,13 +660,13 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): s2 = s.subsurface((0, 0, 4, 7)) - self.assertRaises(Error, s2.get_view, '0') - self.assertRaises(Error, s2.get_view, '1') + self.assertRaises(Error, s2.get_view, "0") + self.assertRaises(Error, s2.get_view, "1") s2 = None s = pygame.Surface((5, 7), pygame.SRCALPHA, 32) - for kind in ('2', '3', 'a', 'A', 'r', 'R', 'g', 'G', 'b', 'B'): + for kind in ("2", "3", "a", "A", "r", "R", "g", "G", "b", "B"): self.assertIsInstance(s.get_view(kind), BufferProxy) # Check default argument value: '2' @@ -407,7 +681,7 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertFalse(s.get_locked()) - v = s.get_view('2') + v = s.get_view("2") self.assertFalse(s.get_locked()) @@ -427,20 +701,20 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): # Check invalid view kind values. s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) - self.assertRaises(TypeError, s.get_view, '') - self.assertRaises(TypeError, s.get_view, '9') - self.assertRaises(TypeError, s.get_view, 'RGBA') + self.assertRaises(TypeError, s.get_view, "") + self.assertRaises(TypeError, s.get_view, "9") + self.assertRaises(TypeError, s.get_view, "RGBA") self.assertRaises(TypeError, s.get_view, 2) # Both unicode and bytes strings are allowed for kind. s = pygame.Surface((2, 4), 0, 32) - s.get_view(as_unicode('2')) - s.get_view(as_bytes('2')) + s.get_view("2") + s.get_view(b"2") # Garbage collection s = pygame.Surface((2, 4), 0, 32) weak_s = weakref.ref(s) - v = s.get_view('3') + v = s.get_view("3") weak_v = weakref.ref(v) gc.collect() self.assertTrue(weak_s() is s) @@ -464,7 +738,7 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertIsInstance(v, BufferProxy) self.assertEqual(v.length, length) - self.assertEqual(repr(v), "") + self.assertEqual(repr(v), f"") # Check for a subsurface (not contiguous) s = pygame.Surface((7, 10), 0, 32) @@ -483,9 +757,9 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): gc.collect() self.assertFalse(s.get_locked()) - OLDBUF = hasattr(pygame.bufferproxy, 'get_segcount') + OLDBUF = hasattr(pygame.bufferproxy, "get_segcount") - @unittest.skipIf(not OLDBUF, 'old buffer not available') + @unittest.skipIf(not OLDBUF, "old buffer not available") def test_get_buffer_oldbuf(self): from pygame.bufferproxy import get_segcount, get_write_buffer @@ -498,12 +772,12 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(segaddr, s._pixels_address) self.assertEqual(seglen, buflen) - @unittest.skipIf(not OLDBUF, 'old buffer not available') + @unittest.skipIf(not OLDBUF, "old buffer not available") def test_get_view_oldbuf(self): from pygame.bufferproxy import get_segcount, get_write_buffer s = pygame.Surface((2, 4), pygame.SRCALPHA, 32) - v = s.get_view('1') + v = s.get_view("1") segcount, buflen = get_segcount(v) self.assertEqual(segcount, 8) self.assertEqual(buflen, s.get_pitch() * s.get_height()) @@ -512,16 +786,15 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(seglen, s.get_bytesize()) def test_set_colorkey(self): - # __doc__ (as of 2008-06-25) for pygame.surface.Surface.set_colorkey: - # Surface.set_colorkey(Color, flags=0): return None - # Surface.set_colorkey(None): return None - # Set the transparent colorkey + # Surface.set_colorkey(Color, flags=0): return None + # Surface.set_colorkey(None): return None + # Set the transparent colorkey - s = pygame.Surface((16,16), pygame.SRCALPHA, 32) + s = pygame.Surface((16, 16), pygame.SRCALPHA, 32) - colorkeys = ((20,189,20, 255),(128,50,50,255), (23, 21, 255,255)) + colorkeys = ((20, 189, 20, 255), (128, 50, 50, 255), (23, 21, 255, 255)) for colorkey in colorkeys: s.set_colorkey(colorkey) @@ -532,165 +805,312 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): self.assertEqual(s.get_colorkey(), colorkey) def test_set_masks(self): - s = pygame.Surface((32,32)) - r,g,b,a = s.get_masks() - s.set_masks((b,g,r,a)) - r2,g2,b2,a2 = s.get_masks() - self.assertEqual((r,g,b,a), (b2,g2,r2,a2)) - + s = pygame.Surface((32, 32)) + r, g, b, a = s.get_masks() + self.assertRaises(TypeError, s.set_masks, (b, g, r, a)) def test_set_shifts(self): - s = pygame.Surface((32,32)) - r,g,b,a = s.get_shifts() - s.set_shifts((b,g,r,a)) - r2,g2,b2,a2 = s.get_shifts() - self.assertEqual((r,g,b,a), (b2,g2,r2,a2)) + s = pygame.Surface((32, 32)) + r, g, b, a = s.get_shifts() + self.assertRaises(TypeError, s.set_shifts, (b, g, r, a)) def test_blit_keyword_args(self): color = (1, 2, 3, 255) s1 = pygame.Surface((4, 4), 0, 32) s2 = pygame.Surface((2, 2), 0, 32) s2.fill((1, 2, 3)) - s1.blit(special_flags=BLEND_ADD, source=s2, - dest=(1, 1), area=s2.get_rect()) + s1.blit(special_flags=BLEND_ADD, source=s2, dest=(1, 1), area=s2.get_rect()) self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255)) self.assertEqual(s1.get_at((1, 1)), color) - def todo_test_blit(self): - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.blit: - - # Surface.blit(source, dest, area=None, special_flags = 0): return Rect - # draw one image onto another - # - # Draws a source Surface onto this Surface. The draw can be positioned - # with the dest argument. Dest can either be pair of coordinates - # representing the upper left corner of the source. A Rect can also be - # passed as the destination and the topleft corner of the rectangle - # will be used as the position for the blit. The size of the - # destination rectangle does not effect the blit. - # - # An optional area rectangle can be passed as well. This represents a - # smaller portion of the source Surface to draw. - # - # An optional special flags is for passing in new in 1.8.0: BLEND_ADD, - # BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX new in 1.8.1: - # BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN, - # BLEND_RGBA_MAX BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, - # BLEND_RGB_MIN, BLEND_RGB_MAX With other special blitting flags - # perhaps added in the future. - # - # The return rectangle is the area of the affected pixels, excluding - # any pixels outside the destination Surface, or outside the clipping - # area. - # - # Pixel alphas will be ignored when blitting to an 8 bit Surface. - # special_flags new in pygame 1.8. + def test_blit_big_rects(self): + """SDL2 can have more than 16 bits for x, y, width, height.""" + big_surf = pygame.Surface((100, 68000), 0, 32) + big_surf_color = (255, 0, 0) + big_surf.fill(big_surf_color) + + background = pygame.Surface((500, 500), 0, 32) + background_color = (0, 255, 0) + background.fill(background_color) + + # copy parts of the big_surf using more than 16bit parts. + background.blit(big_surf, (100, 100), area=(0, 16000, 100, 100)) + background.blit(big_surf, (200, 200), area=(0, 32000, 100, 100)) + background.blit(big_surf, (300, 300), area=(0, 66000, 100, 100)) + + # check that all three areas are drawn. + self.assertEqual(background.get_at((101, 101)), big_surf_color) + self.assertEqual(background.get_at((201, 201)), big_surf_color) + self.assertEqual(background.get_at((301, 301)), big_surf_color) + + # areas outside the 3 blitted areas not covered by those blits. + self.assertEqual(background.get_at((400, 301)), background_color) + self.assertEqual(background.get_at((400, 201)), background_color) + self.assertEqual(background.get_at((100, 201)), background_color) + self.assertEqual(background.get_at((99, 99)), background_color) + self.assertEqual(background.get_at((450, 450)), background_color) - self.fail() + +class TestSurfaceBlit(unittest.TestCase): + """Tests basic blitting functionality and options.""" + + # __doc__ (as of 2008-08-02) for pygame.surface.Surface.blit: + + # Surface.blit(source, dest, area=None, special_flags = 0): return Rect + # draw one image onto another + # + # Draws a source Surface onto this Surface. The draw can be positioned + # with the dest argument. Dest can either be pair of coordinates + # representing the upper left corner of the source. A Rect can also be + # passed as the destination and the topleft corner of the rectangle + # will be used as the position for the blit. The size of the + # destination rectangle does not effect the blit. + # + # An optional area rectangle can be passed as well. This represents a + # smaller portion of the source Surface to draw. + # + # An optional special flags is for passing in new in 1.8.0: BLEND_ADD, + # BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX new in 1.8.1: + # BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN, + # BLEND_RGBA_MAX BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, + # BLEND_RGB_MIN, BLEND_RGB_MAX With other special blitting flags + # perhaps added in the future. + # + # The return rectangle is the area of the affected pixels, excluding + # any pixels outside the destination Surface, or outside the clipping + # area. + # + # Pixel alphas will be ignored when blitting to an 8 bit Surface. + # special_flags new in pygame 1.8. + + def setUp(self): + """Resets starting surfaces.""" + self.src_surface = pygame.Surface((256, 256), 32) + self.src_surface.fill(pygame.Color(255, 255, 255)) + self.dst_surface = pygame.Surface((64, 64), 32) + self.dst_surface.fill(pygame.Color(0, 0, 0)) + + def test_blit_overflow_coord(self): + """Full coverage w/ overflow, specified with Coordinate""" + result = self.dst_surface.blit(self.src_surface, (0, 0)) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (64, 64)) + for k in [(x, x) for x in range(64)]: + self.assertEqual(self.dst_surface.get_at(k), (255, 255, 255)) + + def test_blit_overflow_rect(self): + """Full coverage w/ overflow, specified with a Rect""" + result = self.dst_surface.blit(self.src_surface, pygame.Rect(-1, -1, 300, 300)) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (64, 64)) + for k in [(x, x) for x in range(64)]: + self.assertEqual(self.dst_surface.get_at(k), (255, 255, 255)) + + def test_blit_overflow_nonorigin(self): + """Test Rectangle Dest, with overflow but with starting rect with top-left at (1,1)""" + result = self.dst_surface.blit(self.src_surface, dest=pygame.Rect((1, 1, 1, 1))) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (63, 63)) + self.assertEqual(self.dst_surface.get_at((0, 0)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((63, 0)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((1, 1)), (255, 255, 255)) + self.assertEqual(self.dst_surface.get_at((63, 63)), (255, 255, 255)) + + def test_blit_area_contraint(self): + """Testing area constraint""" + result = self.dst_surface.blit( + self.src_surface, + dest=pygame.Rect((1, 1, 1, 1)), + area=pygame.Rect((2, 2, 2, 2)), + ) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (2, 2)) + self.assertEqual(self.dst_surface.get_at((0, 0)), (0, 0, 0)) # Corners + self.assertEqual(self.dst_surface.get_at((63, 0)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0)) + self.assertEqual(self.dst_surface.get_at((63, 63)), (0, 0, 0)) + self.assertEqual( + self.dst_surface.get_at((1, 1)), (255, 255, 255) + ) # Blitted Area + self.assertEqual(self.dst_surface.get_at((2, 2)), (255, 255, 255)) + self.assertEqual(self.dst_surface.get_at((3, 3)), (0, 0, 0)) + # Should stop short of filling in (3,3) + + def test_blit_zero_overlap(self): + """Testing zero-overlap condition.""" + result = self.dst_surface.blit( + self.src_surface, + dest=pygame.Rect((-256, -256, 1, 1)), + area=pygame.Rect((2, 2, 256, 256)), + ) + self.assertIsInstance(result, pygame.Rect) + self.assertEqual(result.size, (0, 0)) # No blitting expected + for k in [(x, x) for x in range(64)]: + self.assertEqual(self.dst_surface.get_at(k), (0, 0, 0)) # Diagonal + self.assertEqual( + self.dst_surface.get_at((63, 0)), (0, 0, 0) + ) # Remaining corners + self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0)) def test_blit__SRCALPHA_opaque_source(self): - src = pygame.Surface( (256,256), SRCALPHA ,32) + src = pygame.Surface((256, 256), SRCALPHA, 32) dst = src.copy() for i, j in test_utils.rect_area_pts(src.get_rect()): - dst.set_at( (i,j), (i,0,0,j) ) - src.set_at( (i,j), (0,i,0,255) ) + dst.set_at((i, j), (i, 0, 0, j)) + src.set_at((i, j), (0, i, 0, 255)) - dst.blit(src, (0,0)) + dst.blit(src, (0, 0)) for pt in test_utils.rect_area_pts(src.get_rect()): self.assertEqual(dst.get_at(pt)[1], src.get_at(pt)[1]) - def todo_test_blit__blit_to_self(self): #TODO - src = pygame.Surface( (256,256), SRCALPHA, 32) - rect = src.get_rect() - - for pt, color in test_utils.gradient(rect.width, rect.height): - src.set_at(pt, color) + def test_blit__blit_to_self(self): + """Test that blit operation works on self, alpha value is + correct, and that no RGB distortion occurs.""" + test_surface = pygame.Surface((128, 128), SRCALPHA, 32) + area = test_surface.get_rect() - src.blit(src, (0, 0)) + for pt, test_color in test_utils.gradient(area.width, area.height): + test_surface.set_at(pt, test_color) - def todo_test_blit__SRCALPHA_to_SRCALPHA_non_zero(self): #TODO - # " There is no unit test for blitting a SRCALPHA source with non-zero - # alpha to a SRCALPHA destination with non-zero alpha " LL + reference_surface = test_surface.copy() - w,h = size = 32,32 + test_surface.blit(test_surface, (0, 0)) - s = pygame.Surface(size, pygame.SRCALPHA, 32) - s2 = s.copy() + for x in range(area.width): + for y in range(area.height): + (r, g, b, a) = reference_color = reference_surface.get_at((x, y)) + expected_color = (r, g, b, (a + (a * ((256 - a) // 256)))) + self.assertEqual(reference_color, expected_color) - s.fill((32,32,32,111)) - s2.fill((32,32,32,31)) + self.assertEqual(reference_surface.get_rect(), test_surface.get_rect()) - s.blit(s2, (0,0)) - - # TODO: - # what is the correct behaviour ?? should it blend? what algorithm? + def test_blit__SRCALPHA_to_SRCALPHA_non_zero(self): + """Tests blitting a nonzero alpha surface to another nonzero alpha surface + both straight alpha compositing method. Test is fuzzy (+/- 1/256) to account for + different implementations in SDL1 and SDL2. + """ - self.assertEqual(s.get_at((0,0)), (32,32,32,31)) + size = (32, 32) + + def check_color_diff(color1, color2): + """Returns True if two colors are within (1, 1, 1, 1) of each other.""" + for val in color1 - color2: + if abs(val) > 1: + return False + return True + + def high_a_onto_low(high, low): + """Tests straight alpha case. Source is low alpha, destination is high alpha""" + high_alpha_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + low_alpha_surface = high_alpha_surface.copy() + high_alpha_color = Color( + (high, high, low, high) + ) # Injecting some RGB variance. + low_alpha_color = Color((high, low, low, low)) + high_alpha_surface.fill(high_alpha_color) + low_alpha_surface.fill(low_alpha_color) + + high_alpha_surface.blit(low_alpha_surface, (0, 0)) + + expected_color = low_alpha_color + Color( + tuple( + ((x * (255 - low_alpha_color.a)) // 255) for x in high_alpha_color + ) + ) + self.assertTrue( + check_color_diff(high_alpha_surface.get_at((0, 0)), expected_color) + ) + + def low_a_onto_high(high, low): + """Tests straight alpha case. Source is high alpha, destination is low alpha""" + high_alpha_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + low_alpha_surface = high_alpha_surface.copy() + high_alpha_color = Color( + (high, high, low, high) + ) # Injecting some RGB variance. + low_alpha_color = Color((high, low, low, low)) + high_alpha_surface.fill(high_alpha_color) + low_alpha_surface.fill(low_alpha_color) + + low_alpha_surface.blit(high_alpha_surface, (0, 0)) + + expected_color = high_alpha_color + Color( + tuple( + ((x * (255 - high_alpha_color.a)) // 255) for x in low_alpha_color + ) + ) + self.assertTrue( + check_color_diff(low_alpha_surface.get_at((0, 0)), expected_color) + ) + + for low_a in range(0, 128): + for high_a in range(128, 256): + high_a_onto_low(high_a, low_a) + low_a_onto_high(high_a, low_a) def test_blit__SRCALPHA32_to_8(self): # Bug: fatal # SDL_DisplayConvert segfaults when video is uninitialized. target = pygame.Surface((11, 8), 0, 8) - color = target.get_palette_at(2) + test_color = target.get_palette_at(2) source = pygame.Surface((1, 1), pygame.SRCALPHA, 32) - source.set_at((0, 0), color) + source.set_at((0, 0), test_color) target.blit(source, (0, 0)) - @unittest.skipIf(os.environ.get('SDL_VIDEODRIVER') == 'dummy', - 'requires a non-"dummy" SDL_VIDEODRIVER') + +class GeneralSurfaceTests(unittest.TestCase): + @unittest.skipIf( + os.environ.get("SDL_VIDEODRIVER") == "dummy", + 'requires a non-"dummy" SDL_VIDEODRIVER', + ) def test_image_convert_bug_131(self): - # Bitbucket bug #131: Unable to Surface.convert(32) some 1-bit images. - # https://bitbucket.org/pygame/pygame/issue/131/unable-to-surfaceconvert-32-some-1-bit + # bug #131: Unable to Surface.convert(32) some 1-bit images. + # https://github.com/pygame/pygame/issues/131 pygame.display.init() try: - pygame.display.set_mode((640,480)) + pygame.display.set_mode((640, 480)) - im = pygame.image.load(example_path( - os.path.join("data", "city.png"))) - im2 = pygame.image.load(example_path( - os.path.join("data", "brick.png"))) + im = pygame.image.load(example_path(os.path.join("data", "city.png"))) + im2 = pygame.image.load(example_path(os.path.join("data", "brick.png"))) - self.assertEqual(im.get_palette(), - ((0, 0, 0, 255), (255, 255, 255, 255))) - self.assertEqual(im2.get_palette(), - ((0, 0, 0, 255), (0, 0, 0, 255))) + self.assertEqual(im.get_palette(), ((0, 0, 0, 255), (255, 255, 255, 255))) + self.assertEqual(im2.get_palette(), ((0, 0, 0, 255), (0, 0, 0, 255))) - self.assertEqual(repr(im.convert(32)), '') - self.assertEqual(repr(im2.convert(32)), '') + self.assertEqual(repr(im.convert(32)), "") + self.assertEqual(repr(im2.convert(32)), "") # Ensure a palette format to palette format works. im3 = im.convert(8) - self.assertEqual(repr(im3), '') + self.assertEqual(repr(im3), "") self.assertEqual(im3.get_palette(), im.get_palette()) finally: pygame.display.quit() def test_convert_init(self): - """ Ensure initialization exceptions are raised - for surf.convert().""" + """Ensure initialization exceptions are raised + for surf.convert().""" pygame.display.quit() surf = pygame.Surface((1, 1)) - self.assertRaisesRegex(pygame.error, 'display initialized', - surf.convert) + self.assertRaisesRegex(pygame.error, "display initialized", surf.convert) pygame.display.init() try: - if os.environ.get('SDL_VIDEODRIVER') != 'dummy': + if os.environ.get("SDL_VIDEODRIVER") != "dummy": try: surf.convert(32) surf.convert(pygame.Surface((1, 1))) except pygame.error: self.fail("convert() should not raise an exception here.") - self.assertRaisesRegex(pygame.error, 'No video mode', - surf.convert) + self.assertRaisesRegex(pygame.error, "No video mode", surf.convert) - pygame.display.set_mode((640,480)) + pygame.display.set_mode((640, 480)) try: surf.convert() except pygame.error: @@ -699,20 +1119,18 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): pygame.display.quit() def test_convert_alpha_init(self): - """ Ensure initialization exceptions are raised - for surf.convert_alpha().""" + """Ensure initialization exceptions are raised + for surf.convert_alpha().""" pygame.display.quit() surf = pygame.Surface((1, 1)) - self.assertRaisesRegex(pygame.error, 'display initialized', - surf.convert_alpha) + self.assertRaisesRegex(pygame.error, "display initialized", surf.convert_alpha) pygame.display.init() try: - self.assertRaisesRegex(pygame.error, 'No video mode', - surf.convert_alpha) + self.assertRaisesRegex(pygame.error, "No video mode", surf.convert_alpha) - pygame.display.set_mode((640,480)) + pygame.display.set_mode((640, 480)) try: surf.convert_alpha() except pygame.error: @@ -720,33 +1138,506 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): finally: pygame.display.quit() - def todo_test_convert(self): + def test_convert_alpha_SRCALPHA(self): + """Ensure that the surface returned by surf.convert_alpha() + has alpha blending enabled""" + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.convert: - - # Surface.convert(Surface): return Surface - # Surface.convert(depth, flags=0): return Surface - # Surface.convert(masks, flags=0): return Surface - # Surface.convert(): return Surface - # change the pixel format of an image - # - # Creates a new copy of the Surface with the pixel format changed. The - # new pixel format can be determined from another existing Surface. - # Otherwise depth, flags, and masks arguments can be used, similar to - # the pygame.Surface() call. - # - # If no arguments are passed the new Surface will have the same pixel - # format as the display Surface. This is always the fastest format for - # blitting. It is a good idea to convert all Surfaces before they are - # blitted many times. - # - # The converted Surface will have no pixel alphas. They will be - # stripped if the original had them. See Surface.convert_alpha() for - # preserving or creating per-pixel alphas. - # + s1 = pygame.Surface((100, 100), 0, 32) + # s2=pygame.Surface((100,100), pygame.SRCALPHA, 32) + s1_alpha = s1.convert_alpha() + self.assertEqual(s1_alpha.get_flags() & SRCALPHA, SRCALPHA) + self.assertEqual(s1_alpha.get_alpha(), 255) + finally: + pygame.display.quit() + def test_src_alpha_issue_1289(self): + """blit should be white.""" + surf1 = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + surf1.fill((255, 255, 255, 100)) + + surf2 = pygame.Surface((1, 1), pygame.SRCALPHA, 32) + self.assertEqual(surf2.get_at((0, 0)), (0, 0, 0, 0)) + surf2.blit(surf1, (0, 0)) + + self.assertEqual(surf1.get_at((0, 0)), (255, 255, 255, 100)) + self.assertEqual(surf2.get_at((0, 0)), (255, 255, 255, 100)) + + def test_src_alpha_compatible(self): + """ "What pygame 1.9.x did". Is the alpha blitter as before?""" + + # The table below was generated with the SDL1 blit. + # def print_table(): + # nums = [0, 1, 65, 126, 127, 199, 254, 255] + # results = {} + # for dest_r, dest_b, dest_a in zip(nums, reversed(nums), reversed(nums)): + # for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + # src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + # src_surf.fill((src_r, 255, src_b, src_a)) + # dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + # dest_surf.fill((dest_r, 255, dest_b, dest_a)) + # dest_surf.blit(src_surf, (0, 0)) + # key = ((dest_r, dest_b, dest_a), (src_r, src_b, src_a)) + # results[key] = dest_surf.get_at((65, 33)) + # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color") + # pprint(results) + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255), + ((0, 255, 255), (65, 199, 65)): (16, 255, 241, 255), + ((0, 255, 255), (126, 127, 126)): (62, 255, 192, 255), + ((0, 255, 255), (127, 126, 127)): (63, 255, 191, 255), + ((0, 255, 255), (199, 65, 199)): (155, 255, 107, 255), + ((0, 255, 255), (254, 1, 254)): (253, 255, 2, 255), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254), + ((1, 254, 254), (1, 254, 1)): (1, 255, 254, 255), + ((1, 254, 254), (65, 199, 65)): (17, 255, 240, 255), + ((1, 254, 254), (126, 127, 126)): (63, 255, 191, 255), + ((1, 254, 254), (127, 126, 127)): (64, 255, 190, 255), + ((1, 254, 254), (199, 65, 199)): (155, 255, 107, 255), + ((1, 254, 254), (254, 1, 254)): (253, 255, 2, 255), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199), + ((65, 199, 199), (1, 254, 1)): (64, 255, 200, 200), + ((65, 199, 199), (65, 199, 65)): (65, 255, 199, 214), + ((65, 199, 199), (126, 127, 126)): (95, 255, 164, 227), + ((65, 199, 199), (127, 126, 127)): (96, 255, 163, 227), + ((65, 199, 199), (199, 65, 199)): (169, 255, 95, 243), + ((65, 199, 199), (254, 1, 254)): (253, 255, 2, 255), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127), + ((126, 127, 127), (1, 254, 1)): (125, 255, 128, 128), + ((126, 127, 127), (65, 199, 65)): (110, 255, 146, 160), + ((126, 127, 127), (126, 127, 126)): (126, 255, 127, 191), + ((126, 127, 127), (127, 126, 127)): (126, 255, 126, 191), + ((126, 127, 127), (199, 65, 199)): (183, 255, 79, 227), + ((126, 127, 127), (254, 1, 254)): (253, 255, 1, 255), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126), + ((127, 126, 126), (1, 254, 1)): (126, 255, 127, 127), + ((127, 126, 126), (65, 199, 65)): (111, 255, 145, 159), + ((127, 126, 126), (126, 127, 126)): (127, 255, 126, 190), + ((127, 126, 126), (127, 126, 127)): (127, 255, 126, 191), + ((127, 126, 126), (199, 65, 199)): (183, 255, 78, 227), + ((127, 126, 126), (254, 1, 254)): (254, 255, 1, 255), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65), + ((199, 65, 65), (1, 254, 1)): (198, 255, 66, 66), + ((199, 65, 65), (65, 199, 65)): (165, 255, 99, 114), + ((199, 65, 65), (126, 127, 126)): (163, 255, 96, 159), + ((199, 65, 65), (127, 126, 127)): (163, 255, 95, 160), + ((199, 65, 65), (199, 65, 199)): (199, 255, 65, 214), + ((199, 65, 65), (254, 1, 254)): (254, 255, 1, 255), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1), + ((254, 1, 1), (1, 254, 1)): (253, 255, 2, 2), + ((254, 1, 1), (65, 199, 65)): (206, 255, 52, 66), + ((254, 1, 1), (126, 127, 126)): (191, 255, 63, 127), + ((254, 1, 1), (127, 126, 127)): (191, 255, 63, 128), + ((254, 1, 1), (199, 65, 199)): (212, 255, 51, 200), + ((254, 1, 1), (254, 1, 254)): (254, 255, 1, 255), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0), + ((255, 0, 0), (1, 254, 1)): (1, 255, 254, 1), + ((255, 0, 0), (65, 199, 65)): (65, 255, 199, 65), + ((255, 0, 0), (126, 127, 126)): (126, 255, 127, 126), + ((255, 0, 0), (127, 126, 127)): (127, 255, 126, 127), + ((255, 0, 0), (199, 65, 199)): (199, 255, 65, 199), + ((255, 0, 0), (254, 1, 254)): (254, 255, 1, 254), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + src_surf.fill((src_r, 255, src_b, src_a)) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit(src_surf, (0, 0)) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = dest_surf.get_at((65, 33)) + self.assertEqual(results[key], results_expected[key]) + + self.assertEqual(results, results_expected) + + def test_src_alpha_compatible_16bit(self): + """ "What pygame 1.9.x did". Is the alpha blitter as before?""" + + # The table below was generated with the SDL1 blit. + # def print_table(): + # nums = [0, 1, 65, 126, 127, 199, 254, 255] + # results = {} + # for dest_r, dest_b, dest_a in zip(nums, reversed(nums), reversed(nums)): + # for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + # src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + # src_surf.fill((src_r, 255, src_b, src_a)) + # dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + # dest_surf.fill((dest_r, 255, dest_b, dest_a)) + # dest_surf.blit(src_surf, (0, 0)) + # key = ((dest_r, dest_b, dest_a), (src_r, src_b, src_a)) + # results[key] = dest_surf.get_at((65, 33)) + # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color") + # pprint(results) + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255), + ((0, 255, 255), (65, 199, 65)): (17, 255, 255, 255), + ((0, 255, 255), (126, 127, 126)): (51, 255, 204, 255), + ((0, 255, 255), (127, 126, 127)): (51, 255, 204, 255), + ((0, 255, 255), (199, 65, 199)): (170, 255, 102, 255), + ((0, 255, 255), (254, 1, 254)): (255, 255, 0, 255), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (0, 255, 255, 255), + ((1, 254, 254), (1, 254, 1)): (0, 255, 255, 255), + ((1, 254, 254), (65, 199, 65)): (17, 255, 255, 255), + ((1, 254, 254), (126, 127, 126)): (51, 255, 204, 255), + ((1, 254, 254), (127, 126, 127)): (51, 255, 204, 255), + ((1, 254, 254), (199, 65, 199)): (170, 255, 102, 255), + ((1, 254, 254), (254, 1, 254)): (255, 255, 0, 255), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (68, 255, 204, 204), + ((65, 199, 199), (1, 254, 1)): (68, 255, 204, 204), + ((65, 199, 199), (65, 199, 65)): (68, 255, 204, 221), + ((65, 199, 199), (126, 127, 126)): (85, 255, 170, 238), + ((65, 199, 199), (127, 126, 127)): (85, 255, 170, 238), + ((65, 199, 199), (199, 65, 199)): (187, 255, 85, 255), + ((65, 199, 199), (254, 1, 254)): (255, 255, 0, 255), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (119, 255, 119, 119), + ((126, 127, 127), (1, 254, 1)): (119, 255, 119, 119), + ((126, 127, 127), (65, 199, 65)): (102, 255, 136, 153), + ((126, 127, 127), (126, 127, 126)): (119, 255, 119, 187), + ((126, 127, 127), (127, 126, 127)): (119, 255, 119, 187), + ((126, 127, 127), (199, 65, 199)): (187, 255, 68, 238), + ((126, 127, 127), (254, 1, 254)): (255, 255, 0, 255), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (119, 255, 119, 119), + ((127, 126, 126), (1, 254, 1)): (119, 255, 119, 119), + ((127, 126, 126), (65, 199, 65)): (102, 255, 136, 153), + ((127, 126, 126), (126, 127, 126)): (119, 255, 119, 187), + ((127, 126, 126), (127, 126, 127)): (119, 255, 119, 187), + ((127, 126, 126), (199, 65, 199)): (187, 255, 68, 238), + ((127, 126, 126), (254, 1, 254)): (255, 255, 0, 255), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (204, 255, 68, 68), + ((199, 65, 65), (1, 254, 1)): (204, 255, 68, 68), + ((199, 65, 65), (65, 199, 65)): (170, 255, 102, 119), + ((199, 65, 65), (126, 127, 126)): (170, 255, 85, 153), + ((199, 65, 65), (127, 126, 127)): (170, 255, 85, 153), + ((199, 65, 65), (199, 65, 199)): (204, 255, 68, 221), + ((199, 65, 65), (254, 1, 254)): (255, 255, 0, 255), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (0, 255, 255, 0), + ((254, 1, 1), (1, 254, 1)): (0, 255, 255, 0), + ((254, 1, 1), (65, 199, 65)): (68, 255, 204, 68), + ((254, 1, 1), (126, 127, 126)): (119, 255, 119, 119), + ((254, 1, 1), (127, 126, 127)): (119, 255, 119, 119), + ((254, 1, 1), (199, 65, 199)): (204, 255, 68, 204), + ((254, 1, 1), (254, 1, 254)): (255, 255, 0, 255), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0), + ((255, 0, 0), (1, 254, 1)): (0, 255, 255, 0), + ((255, 0, 0), (65, 199, 65)): (68, 255, 204, 68), + ((255, 0, 0), (126, 127, 126)): (119, 255, 119, 119), + ((255, 0, 0), (127, 126, 127)): (119, 255, 119, 119), + ((255, 0, 0), (199, 65, 199)): (204, 255, 68, 204), + ((255, 0, 0), (254, 1, 254)): (255, 255, 0, 255), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + src_surf.fill((src_r, 255, src_b, src_a)) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit(src_surf, (0, 0)) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = dest_surf.get_at((65, 33)) + self.assertEqual(results[key], results_expected[key]) + + self.assertEqual(results, results_expected) + + def test_sdl1_mimic_blitter_with_set_alpha(self): + """does the SDL 1 style blitter in pygame 2 work with set_alpha(), + this feature only exists in pygame 2/SDL2 SDL1 did not support + combining surface and pixel alpha""" + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255), + ((0, 255, 255), (65, 199, 65)): (16, 255, 241, 255), + ((0, 255, 255), (126, 127, 126)): (62, 255, 192, 255), + ((0, 255, 255), (127, 126, 127)): (63, 255, 191, 255), + ((0, 255, 255), (199, 65, 199)): (155, 255, 107, 255), + ((0, 255, 255), (254, 1, 254)): (253, 255, 2, 255), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254), + ((1, 254, 254), (1, 254, 1)): (1, 255, 254, 255), + ((1, 254, 254), (65, 199, 65)): (17, 255, 240, 255), + ((1, 254, 254), (126, 127, 126)): (63, 255, 191, 255), + ((1, 254, 254), (127, 126, 127)): (64, 255, 190, 255), + ((1, 254, 254), (199, 65, 199)): (155, 255, 107, 255), + ((1, 254, 254), (254, 1, 254)): (253, 255, 2, 255), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199), + ((65, 199, 199), (1, 254, 1)): (64, 255, 200, 200), + ((65, 199, 199), (65, 199, 65)): (65, 255, 199, 214), + ((65, 199, 199), (126, 127, 126)): (95, 255, 164, 227), + ((65, 199, 199), (127, 126, 127)): (96, 255, 163, 227), + ((65, 199, 199), (199, 65, 199)): (169, 255, 95, 243), + ((65, 199, 199), (254, 1, 254)): (253, 255, 2, 255), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127), + ((126, 127, 127), (1, 254, 1)): (125, 255, 128, 128), + ((126, 127, 127), (65, 199, 65)): (110, 255, 146, 160), + ((126, 127, 127), (126, 127, 126)): (126, 255, 127, 191), + ((126, 127, 127), (127, 126, 127)): (126, 255, 126, 191), + ((126, 127, 127), (199, 65, 199)): (183, 255, 79, 227), + ((126, 127, 127), (254, 1, 254)): (253, 255, 1, 255), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126), + ((127, 126, 126), (1, 254, 1)): (126, 255, 127, 127), + ((127, 126, 126), (65, 199, 65)): (111, 255, 145, 159), + ((127, 126, 126), (126, 127, 126)): (127, 255, 126, 190), + ((127, 126, 126), (127, 126, 127)): (127, 255, 126, 191), + ((127, 126, 126), (199, 65, 199)): (183, 255, 78, 227), + ((127, 126, 126), (254, 1, 254)): (254, 255, 1, 255), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65), + ((199, 65, 65), (1, 254, 1)): (198, 255, 66, 66), + ((199, 65, 65), (65, 199, 65)): (165, 255, 99, 114), + ((199, 65, 65), (126, 127, 126)): (163, 255, 96, 159), + ((199, 65, 65), (127, 126, 127)): (163, 255, 95, 160), + ((199, 65, 65), (199, 65, 199)): (199, 255, 65, 214), + ((199, 65, 65), (254, 1, 254)): (254, 255, 1, 255), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1), + ((254, 1, 1), (1, 254, 1)): (253, 255, 2, 2), + ((254, 1, 1), (65, 199, 65)): (206, 255, 52, 66), + ((254, 1, 1), (126, 127, 126)): (191, 255, 63, 127), + ((254, 1, 1), (127, 126, 127)): (191, 255, 63, 128), + ((254, 1, 1), (199, 65, 199)): (212, 255, 51, 200), + ((254, 1, 1), (254, 1, 254)): (254, 255, 1, 255), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0), + ((255, 0, 0), (1, 254, 1)): (1, 255, 254, 1), + ((255, 0, 0), (65, 199, 65)): (65, 255, 199, 65), + ((255, 0, 0), (126, 127, 126)): (126, 255, 127, 126), + ((255, 0, 0), (127, 126, 127)): (127, 255, 126, 127), + ((255, 0, 0), (199, 65, 199)): (199, 255, 65, 199), + ((255, 0, 0), (254, 1, 254)): (254, 255, 1, 254), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + src_surf.fill((src_r, 255, src_b, 255)) + src_surf.set_alpha(src_a) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit(src_surf, (0, 0)) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = dest_surf.get_at((65, 33)) + self.assertEqual(results[key], results_expected[key]) + + self.assertEqual(results, results_expected) + + @unittest.skipIf( + "arm" in platform.machine() or "aarch64" in platform.machine(), + "sdl2 blitter produces different results on arm", + ) + def test_src_alpha_sdl2_blitter(self): + """Checking that the BLEND_ALPHA_SDL2 flag works - this feature + only exists when using SDL2""" + + results_expected = { + ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255), + ((0, 255, 255), (1, 254, 1)): (0, 253, 253, 253), + ((0, 255, 255), (65, 199, 65)): (16, 253, 239, 253), + ((0, 255, 255), (126, 127, 126)): (62, 253, 190, 253), + ((0, 255, 255), (127, 126, 127)): (63, 253, 189, 253), + ((0, 255, 255), (199, 65, 199)): (154, 253, 105, 253), + ((0, 255, 255), (254, 1, 254)): (252, 253, 0, 253), + ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255), + ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254), + ((1, 254, 254), (1, 254, 1)): (0, 253, 252, 252), + ((1, 254, 254), (65, 199, 65)): (16, 253, 238, 252), + ((1, 254, 254), (126, 127, 126)): (62, 253, 189, 252), + ((1, 254, 254), (127, 126, 127)): (63, 253, 189, 253), + ((1, 254, 254), (199, 65, 199)): (154, 253, 105, 253), + ((1, 254, 254), (254, 1, 254)): (252, 253, 0, 253), + ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255), + ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199), + ((65, 199, 199), (1, 254, 1)): (64, 253, 197, 197), + ((65, 199, 199), (65, 199, 65)): (64, 253, 197, 211), + ((65, 199, 199), (126, 127, 126)): (94, 253, 162, 225), + ((65, 199, 199), (127, 126, 127)): (95, 253, 161, 225), + ((65, 199, 199), (199, 65, 199)): (168, 253, 93, 241), + ((65, 199, 199), (254, 1, 254)): (252, 253, 0, 253), + ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255), + ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127), + ((126, 127, 127), (1, 254, 1)): (125, 253, 126, 126), + ((126, 127, 127), (65, 199, 65)): (109, 253, 144, 158), + ((126, 127, 127), (126, 127, 126)): (125, 253, 125, 188), + ((126, 127, 127), (127, 126, 127)): (126, 253, 125, 189), + ((126, 127, 127), (199, 65, 199)): (181, 253, 77, 225), + ((126, 127, 127), (254, 1, 254)): (252, 253, 0, 253), + ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255), + ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126), + ((127, 126, 126), (1, 254, 1)): (126, 253, 125, 125), + ((127, 126, 126), (65, 199, 65)): (110, 253, 143, 157), + ((127, 126, 126), (126, 127, 126)): (125, 253, 125, 188), + ((127, 126, 126), (127, 126, 127)): (126, 253, 125, 189), + ((127, 126, 126), (199, 65, 199)): (181, 253, 77, 225), + ((127, 126, 126), (254, 1, 254)): (252, 253, 0, 253), + ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255), + ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65), + ((199, 65, 65), (1, 254, 1)): (197, 253, 64, 64), + ((199, 65, 65), (65, 199, 65)): (163, 253, 98, 112), + ((199, 65, 65), (126, 127, 126)): (162, 253, 94, 157), + ((199, 65, 65), (127, 126, 127)): (162, 253, 94, 158), + ((199, 65, 65), (199, 65, 199)): (197, 253, 64, 212), + ((199, 65, 65), (254, 1, 254)): (252, 253, 0, 253), + ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255), + ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1), + ((254, 1, 1), (1, 254, 1)): (252, 253, 0, 0), + ((254, 1, 1), (65, 199, 65)): (204, 253, 50, 64), + ((254, 1, 1), (126, 127, 126)): (189, 253, 62, 125), + ((254, 1, 1), (127, 126, 127)): (190, 253, 62, 126), + ((254, 1, 1), (199, 65, 199)): (209, 253, 50, 198), + ((254, 1, 1), (254, 1, 254)): (252, 253, 0, 253), + ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255), + ((255, 0, 0), (0, 255, 0)): (255, 255, 0, 0), + ((255, 0, 0), (1, 254, 1)): (253, 253, 0, 0), + ((255, 0, 0), (65, 199, 65)): (205, 253, 50, 64), + ((255, 0, 0), (126, 127, 126)): (190, 253, 62, 125), + ((255, 0, 0), (127, 126, 127)): (190, 253, 62, 126), + ((255, 0, 0), (199, 65, 199)): (209, 253, 50, 198), + ((255, 0, 0), (254, 1, 254)): (252, 253, 0, 253), + ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255), + } + + # chosen because they contain edge cases. + nums = [0, 1, 65, 126, 127, 199, 254, 255] + results = {} + for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)): + for src_r, src_b, src_a in zip(nums, reversed(nums), nums): + with self.subTest( + src_r=src_r, + src_b=src_b, + src_a=src_a, + dest_r=dst_r, + dest_b=dst_b, + dest_a=dst_a, + ): + src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + src_surf.fill((src_r, 255, src_b, src_a)) + dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32) + dest_surf.fill((dst_r, 255, dst_b, dst_a)) + + dest_surf.blit( + src_surf, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2 + ) + key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a)) + results[key] = tuple(dest_surf.get_at((65, 33))) + for i in range(4): + self.assertAlmostEqual( + results[key][i], results_expected[key][i], delta=4 + ) + + # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color") + # pprint(results) + + def test_opaque_destination_blit_with_set_alpha(self): + # no set_alpha() + src_surf = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + src_surf.fill((255, 255, 255, 200)) + dest_surf = pygame.Surface((32, 32)) + dest_surf.fill((100, 100, 100)) + + dest_surf.blit(src_surf, (0, 0)) + + no_surf_alpha_col = dest_surf.get_at((0, 0)) + + dest_surf.fill((100, 100, 100)) + dest_surf.set_alpha(200) + dest_surf.blit(src_surf, (0, 0)) + + surf_alpha_col = dest_surf.get_at((0, 0)) + + self.assertEqual(no_surf_alpha_col, surf_alpha_col) + + def todo_test_convert(self): self.fail() + # Below should not use a display Surface, but create one and check it is converted + # to the depth of the display surface. + + # def test_convert(self): + # """Ensure to creates a new copy of the Surface with the pixel format changed""" + # width = 23 + # height = 17 + # size = (width, height) + # flags = 0 + # depth = 32 + # pygame.display.init() + + # try: + # convert_surface = pygame.display.set_mode(size) + # surface = pygame.surface.Surface.convert(convert_surface) + # self.assertIsNot(surface, convert_surface) + # self.assertNotEqual(surface.get_size(), size) + + # depth_surface = pygame.display.set_mode(size, flags, depth) + # surface2 = pygame.surface.Surface.convert(depth_surface) + # self.assertIsNot(surface2, depth_surface) + # self.assertEqual(surface2.get_size(), size) + # finally: + # pygame.display.quit() + def test_convert__pixel_format_as_surface_subclass(self): """Ensure convert accepts a Surface subclass argument.""" expected_size = (23, 17) @@ -765,25 +1656,57 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): finally: pygame.display.quit() - def todo_test_convert_alpha(self): + def test_convert_alpha(self): + """Ensure the surface returned by surf.convert_alpha + has alpha values added""" + pygame.display.init() + try: + pygame.display.set_mode((640, 480)) - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.convert_alpha: + s1 = pygame.Surface((100, 100), 0, 32) + s1_alpha = pygame.Surface.convert_alpha(s1) - # Surface.convert_alpha(Surface): return Surface - # Surface.convert_alpha(): return Surface - # change the pixel format of an image including per pixel alphas - # - # Creates a new copy of the surface with the desired pixel format. The - # new surface will be in a format suited for quick blitting to the - # given format with per pixel alpha. If no surface is given, the new - # surface will be optimized for blitting to the current display. - # - # Unlike the Surface.convert() method, the pixel format for the new - # image will not be exactly the same as the requested source, but it - # will be optimized for fast alpha blitting to the destination. - # + s2 = pygame.Surface((100, 100), 0, 32) + s2_alpha = s2.convert_alpha() - self.fail() + s3 = pygame.Surface((100, 100), 0, 8) + s3_alpha = s3.convert_alpha() + + s4 = pygame.Surface((100, 100), 0, 12) + s4_alpha = s4.convert_alpha() + + s5 = pygame.Surface((100, 100), 0, 15) + s5_alpha = s5.convert_alpha() + + s6 = pygame.Surface((100, 100), 0, 16) + s6_alpha = s6.convert_alpha() + + s7 = pygame.Surface((100, 100), 0, 24) + s7_alpha = s7.convert_alpha() + + self.assertEqual(s1_alpha.get_alpha(), 255) + self.assertEqual(s2_alpha.get_alpha(), 255) + self.assertEqual(s3_alpha.get_alpha(), 255) + self.assertEqual(s4_alpha.get_alpha(), 255) + self.assertEqual(s5_alpha.get_alpha(), 255) + self.assertEqual(s6_alpha.get_alpha(), 255) + self.assertEqual(s7_alpha.get_alpha(), 255) + + self.assertEqual(s1_alpha.get_bitsize(), 32) + self.assertEqual(s2_alpha.get_bitsize(), 32) + self.assertEqual(s3_alpha.get_bitsize(), 32) + self.assertEqual(s4_alpha.get_bitsize(), 32) + self.assertEqual(s5_alpha.get_bitsize(), 32) + self.assertEqual(s6_alpha.get_bitsize(), 32) + self.assertEqual(s6_alpha.get_bitsize(), 32) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.convert_alpha() + + finally: + pygame.display.quit() def test_convert_alpha__pixel_format_as_surface_subclass(self): """Ensure convert_alpha accepts a Surface subclass argument.""" @@ -807,32 +1730,75 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): finally: pygame.display.quit() - def todo_test_get_abs_offset(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_abs_offset: - - # Surface.get_abs_offset(): return (x, y) - # find the absolute position of a child subsurface inside its top level parent - # - # Get the offset position of a child subsurface inside of its top - # level parent Surface. If the Surface is not a subsurface this will - # return (0, 0). - # - - self.fail() - - def todo_test_get_abs_parent(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_abs_parent: - - # Surface.get_abs_parent(): return Surface - # find the top level parent of a subsurface - # - # Returns the parent Surface of a subsurface. If this is not a - # subsurface then this surface will be returned. - # + def test_get_abs_offset(self): + pygame.display.init() + try: + parent = pygame.Surface((64, 64), SRCALPHA, 32) + + # Stack bunch of subsurfaces + sub_level_1 = parent.subsurface((2, 2), (34, 37)) + sub_level_2 = sub_level_1.subsurface((0, 0), (30, 29)) + sub_level_3 = sub_level_2.subsurface((3, 7), (20, 21)) + sub_level_4 = sub_level_3.subsurface((6, 1), (14, 14)) + sub_level_5 = sub_level_4.subsurface((5, 6), (3, 4)) + + # Parent is always (0, 0) + self.assertEqual(parent.get_abs_offset(), (0, 0)) + # Total offset: (0+2, 0+2) = (2, 2) + self.assertEqual(sub_level_1.get_abs_offset(), (2, 2)) + # Total offset: (0+2+0, 0+2+0) = (2, 2) + self.assertEqual(sub_level_2.get_abs_offset(), (2, 2)) + # Total offset: (0+2+0+3, 0+2+0+7) = (5, 9) + self.assertEqual(sub_level_3.get_abs_offset(), (5, 9)) + # Total offset: (0+2+0+3+6, 0+2+0+7+1) = (11, 10) + self.assertEqual(sub_level_4.get_abs_offset(), (11, 10)) + # Total offset: (0+2+0+3+6+5, 0+2+0+7+1+6) = (16, 16) + self.assertEqual(sub_level_5.get_abs_offset(), (16, 16)) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_abs_offset() + finally: + pygame.display.quit() - self.fail() + def test_get_abs_parent(self): + pygame.display.init() + try: + parent = pygame.Surface((32, 32), SRCALPHA, 32) + + # Stack bunch of subsurfaces + sub_level_1 = parent.subsurface((1, 1), (15, 15)) + sub_level_2 = sub_level_1.subsurface((1, 1), (12, 12)) + sub_level_3 = sub_level_2.subsurface((1, 1), (9, 9)) + sub_level_4 = sub_level_3.subsurface((1, 1), (8, 8)) + sub_level_5 = sub_level_4.subsurface((2, 2), (3, 4)) + sub_level_6 = sub_level_5.subsurface((0, 0), (2, 1)) + + # Can't have subsurfaces bigger than parents + self.assertRaises(ValueError, parent.subsurface, (5, 5), (100, 100)) + self.assertRaises(ValueError, sub_level_3.subsurface, (0, 0), (11, 5)) + self.assertRaises(ValueError, sub_level_6.subsurface, (0, 0), (5, 5)) + + # Calling get_abs_parent on parent should return itself + self.assertEqual(parent.get_abs_parent(), parent) + + # On subclass "depth" of 1, get_abs_parent and get_parent should return the same + self.assertEqual(sub_level_1.get_abs_parent(), sub_level_1.get_parent()) + self.assertEqual(sub_level_2.get_abs_parent(), parent) + self.assertEqual(sub_level_3.get_abs_parent(), parent) + self.assertEqual(sub_level_4.get_abs_parent(), parent) + self.assertEqual(sub_level_5.get_abs_parent(), parent) + self.assertEqual( + sub_level_6.get_abs_parent(), sub_level_6.get_parent().get_abs_parent() + ) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_abs_parent() + finally: + pygame.display.quit() def test_get_at(self): surf = pygame.Surface((2, 2), 0, 24) @@ -859,227 +1825,442 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): surf = pygame.Surface((2, 2), 0, bitsize) surf.fill(color) pixel = surf.get_at_mapped((0, 0)) - self.assertEqual(pixel, surf.map_rgb(color), - "%i != %i, bitsize: %i" % - (pixel, surf.map_rgb(color), bitsize)) - - def todo_test_get_bitsize(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_bitsize: - - # Surface.get_bitsize(): return int - # get the bit depth of the Surface pixel format - # - # Returns the number of bits used to represent each pixel. This value - # may not exactly fill the number of bytes used per pixel. For example - # a 15 bit Surface still requires a full 2 bytes. - # - - self.fail() - - def todo_test_get_clip(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_clip: + self.assertEqual( + pixel, + surf.map_rgb(color), + "%i != %i, bitsize: %i" % (pixel, surf.map_rgb(color), bitsize), + ) - # Surface.get_clip(): return Rect - # get the current clipping area of the Surface - # - # Return a rectangle of the current clipping area. The Surface will - # always return a valid rectangle that will never be outside the - # bounds of the image. If the Surface has had None set for the - # clipping area, the Surface will return a rectangle with the full - # area of the Surface. - # - - self.fail() + def test_get_bitsize(self): + pygame.display.init() + try: + expected_size = (11, 21) - def todo_test_get_colorkey(self): - surf = pygame.surface((2, 2), 0, 24) - self.assertIsNone(surf.get_colorykey()) - colorkey = pygame.Color(20, 40, 60) - surf.set_colorkey(colorkey) - ck = surf.get_colorkey() - self.assertIsInstance(ck, pygame.Color) - self.assertEqual(ck, colorkey) + # Check that get_bitsize returns passed depth + expected_depth = 32 + surface = pygame.Surface(expected_size, pygame.SRCALPHA, expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) - def todo_test_get_height(self): + expected_depth = 16 + surface = pygame.Surface(expected_size, pygame.SRCALPHA, expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_height: + expected_depth = 15 + surface = pygame.Surface(expected_size, 0, expected_depth) + self.assertEqual(surface.get_size(), expected_size) + self.assertEqual(surface.get_bitsize(), expected_depth) + # Check for invalid depths + expected_depth = -1 + self.assertRaises( + ValueError, pygame.Surface, expected_size, 0, expected_depth + ) + expected_depth = 11 + self.assertRaises( + ValueError, pygame.Surface, expected_size, 0, expected_depth + ) + expected_depth = 1024 + self.assertRaises( + ValueError, pygame.Surface, expected_size, 0, expected_depth + ) + + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_bitsize() + finally: + pygame.display.quit() - # Surface.get_height(): return height - # get the height of the Surface - # - # Return the height of the Surface in pixels. + def test_get_clip(self): + s = pygame.Surface((800, 600)) + rectangle = s.get_clip() + self.assertEqual(rectangle, (0, 0, 800, 600)) - self.fail() + def test_get_colorkey(self): + pygame.display.init() + try: + # if set_colorkey is not used + s = pygame.Surface((800, 600), 0, 32) + self.assertIsNone(s.get_colorkey()) - def todo_test_get_locked(self): + # if set_colorkey is used + s.set_colorkey(None) + self.assertIsNone(s.get_colorkey()) - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_locked: + # setting up remainder of tests... + r, g, b, a = 20, 40, 60, 12 + colorkey = pygame.Color(r, g, b) + s.set_colorkey(colorkey) - # Surface.get_locked(): return bool - # test if the Surface is current locked - # - # Returns True when the Surface is locked. It doesn't matter how many - # times the Surface is locked. - # + # test for ideal case + self.assertEqual(s.get_colorkey(), (r, g, b, 255)) - self.fail() + # test for if the color_key is set using pygame.RLEACCEL + s.set_colorkey(colorkey, pygame.RLEACCEL) + self.assertEqual(s.get_colorkey(), (r, g, b, 255)) - def todo_test_get_locks(self): + # test for if the color key is not what's expected + s.set_colorkey(pygame.Color(r + 1, g + 1, b + 1)) + self.assertNotEqual(s.get_colorkey(), (r, g, b, 255)) + s.set_colorkey(pygame.Color(r, g, b, a)) + # regardless of whether alpha is not 255 + # colorkey returned from surface is always 255 + self.assertEqual(s.get_colorkey(), (r, g, b, 255)) + finally: + # test for using method after display.quit() is called... + s = pygame.display.set_mode((200, 200), 0, 32) + pygame.display.quit() + with self.assertRaises(pygame.error): + s.get_colorkey() + + def test_get_height(self): + sizes = ((1, 1), (119, 10), (10, 119), (1, 1000), (1000, 1), (1000, 1000)) + for width, height in sizes: + surf = pygame.Surface((width, height)) + found_height = surf.get_height() + self.assertEqual(height, found_height) + + def test_get_locked(self): + def blit_locked_test(surface): + newSurf = pygame.Surface((10, 10)) + try: + newSurf.blit(surface, (0, 0)) + except pygame.error: + return True + else: + return False + + surf = pygame.Surface((100, 100)) + + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + # Surface should lock + surf.lock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + # Surface should unlock + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + + # Check multiple locks + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + + # Check many locks + surf = pygame.Surface((100, 100)) + for i in range(1000): + surf.lock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Locked + for i in range(1000): + surf.unlock() + self.assertFalse(surf.get_locked()) # Unlocked + + # Unlocking an unlocked surface + surf = pygame.Surface((100, 100)) + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + surf.unlock() + self.assertIs(surf.get_locked(), blit_locked_test(surf)) # Unlocked + + def test_get_locks(self): # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_locks: - # Surface.get_locks(): return tuple - # Gets the locks for the Surface - # - # Returns the currently existing locks for the Surface. - - self.fail() - - def todo_test_get_losses(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_losses: - - # Surface.get_losses(): return (R, G, B, A) - # the significant bits used to convert between a color and a mapped integer - # - # Return the least significant number of bits stripped from each color - # in a mapped integer. - # - # This value is not needed for normal Pygame usage. - - self.fail() - - def todo_test_get_masks(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_masks: - - # Surface.get_masks(): return (R, G, B, A) - # the bitmasks needed to convert between a color and a mapped integer - # - # Returns the bitmasks used to isolate each color in a mapped integer. - # This value is not needed for normal Pygame usage. - - self.fail() - - def todo_test_get_offset(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_offset: - - # Surface.get_offset(): return (x, y) - # find the position of a child subsurface inside a parent - # - # Get the offset position of a child subsurface inside of a parent. If - # the Surface is not a subsurface this will return (0, 0). - # - - self.fail() - - def test_get_palette(self): + # Surface.get_locks(): return tuple + # Gets the locks for the Surface + # + # Returns the currently existing locks for the Surface. + + # test on a surface that is not initially locked + surface = pygame.Surface((100, 100)) + self.assertEqual(surface.get_locks(), ()) + + # test on the same surface after it has been locked + surface.lock() + self.assertEqual(surface.get_locks(), (surface,)) + + # test on the same surface after it has been unlocked + surface.unlock() + self.assertEqual(surface.get_locks(), ()) + + # test with PixelArray initialization: locks surface + pxarray = pygame.PixelArray(surface) + self.assertNotEqual(surface.get_locks(), ()) + + # closing the PixelArray releases the surface lock + pxarray.close() + self.assertEqual(surface.get_locks(), ()) + + # AttributeError raised when called on invalid object type (i.e. not a pygame.Surface object) + with self.assertRaises(AttributeError): + "DUMMY".get_locks() + + # test multiple locks and unlocks on the same surface + surface.lock() + surface.lock() + surface.lock() + self.assertEqual(surface.get_locks(), (surface, surface, surface)) + + surface.unlock() + surface.unlock() + self.assertEqual(surface.get_locks(), (surface,)) + surface.unlock() + self.assertEqual(surface.get_locks(), ()) + + def test_get_losses(self): + """Ensure a surface's losses can be retrieved""" pygame.display.init() try: - palette = [Color(i, i, i) for i in range(256)] - pygame.display.set_mode((100, 50)) - surf = pygame.Surface((2, 2), 0, 8) - surf.set_palette(palette) - palette2 = surf.get_palette() - r,g,b = palette2[0] - - self.assertEqual(len(palette2), len(palette)) - for c2, c in zip(palette2, palette): - self.assertEqual(c2, c) - for c in palette2: - self.assertIsInstance(c, pygame.Color) + # Masks for different color component configurations + mask8 = (224, 28, 3, 0) + mask15 = (31744, 992, 31, 0) + mask16 = (63488, 2016, 31, 0) + mask24 = (16711680, 65280, 255, 0) + mask32 = (4278190080, 16711680, 65280, 255) + + # Surfaces with standard depths and masks + display_surf = pygame.display.set_mode((100, 100)) + surf = pygame.Surface((100, 100)) + surf_8bit = pygame.Surface((100, 100), depth=8, masks=mask8) + surf_15bit = pygame.Surface((100, 100), depth=15, masks=mask15) + surf_16bit = pygame.Surface((100, 100), depth=16, masks=mask16) + surf_24bit = pygame.Surface((100, 100), depth=24, masks=mask24) + surf_32bit = pygame.Surface((100, 100), depth=32, masks=mask32) + + # Test output is correct type, length, and value range + losses = surf.get_losses() + self.assertIsInstance(losses, tuple) + self.assertEqual(len(losses), 4) + for loss in losses: + self.assertIsInstance(loss, int) + self.assertGreaterEqual(loss, 0) + self.assertLessEqual(loss, 8) + + # Test each surface for correct losses + # Display surface losses gives idea of default surface losses + if display_surf.get_losses() == (0, 0, 0, 8): + self.assertEqual(losses, (0, 0, 0, 8)) + elif display_surf.get_losses() == (8, 8, 8, 8): + self.assertEqual(losses, (8, 8, 8, 8)) + + self.assertEqual(surf_8bit.get_losses(), (5, 5, 6, 8)) + self.assertEqual(surf_15bit.get_losses(), (3, 3, 3, 8)) + self.assertEqual(surf_16bit.get_losses(), (3, 2, 3, 8)) + self.assertEqual(surf_24bit.get_losses(), (0, 0, 0, 8)) + self.assertEqual(surf_32bit.get_losses(), (0, 0, 0, 0)) + + # Method should fail when display is not initialized + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode((100, 100)) + pygame.display.quit() + surface.get_losses() finally: pygame.display.quit() - def test_get_palette_at(self): - # See also test_get_palette - pygame.display.init() - try: - pygame.display.set_mode((100, 50)) - surf = pygame.Surface((2, 2), 0, 8) - color = pygame.Color(1, 2, 3, 255) - surf.set_palette_at(0, color) - color2 = surf.get_palette_at(0) - self.assertIsInstance(color2, pygame.Color) - self.assertEqual(color2, color) - self.assertRaises(IndexError, surf.get_palette_at, -1) - self.assertRaises(IndexError, surf.get_palette_at, 256) - finally: + def test_get_masks__rgba(self): + """ + Ensure that get_mask can return RGBA mask. + """ + masks = [ + (0x0F00, 0x00F0, 0x000F, 0xF000), + (0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000), + ] + depths = [16, 32] + for expected, depth in list(zip(masks, depths)): + surface = pygame.Surface((10, 10), pygame.SRCALPHA, depth) + self.assertEqual(expected, surface.get_masks()) + + def test_get_masks__rgb(self): + """ + Ensure that get_mask can return RGB mask. + """ + masks = [ + (0x60, 0x1C, 0x03, 0x00), + (0xF00, 0x0F0, 0x00F, 0x000), + (0x7C00, 0x03E0, 0x001F, 0x0000), + (0xF800, 0x07E0, 0x001F, 0x0000), + (0xFF0000, 0x00FF00, 0x0000FF, 0x000000), + (0xFF0000, 0x00FF00, 0x0000FF, 0x000000), + ] + depths = [8, 12, 15, 16, 24, 32] + for expected, depth in list(zip(masks, depths)): + surface = pygame.Surface((10, 10), 0, depth) + if depth == 8: + expected = (0x00, 0x00, 0x00, 0x00) + self.assertEqual(expected, surface.get_masks()) + + def test_get_masks__no_surface(self): + """ + Ensure that after display.quit, calling get_masks raises pygame.error. + """ + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode((10, 10)) pygame.display.quit() + surface.get_masks() - def todo_test_get_pitch(self): + def test_get_offset(self): + """get_offset returns the (0,0) if surface is not a child + returns the position of child subsurface inside of parent + """ + pygame.display.init() + try: + surf = pygame.Surface((100, 100)) + self.assertEqual(surf.get_offset(), (0, 0)) - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_pitch: + # subsurface offset test + subsurf = surf.subsurface(1, 1, 10, 10) + self.assertEqual(subsurf.get_offset(), (1, 1)) - # Surface.get_pitch(): return int - # get the number of bytes used per Surface row - # - # Return the number of bytes separating each row in the Surface. - # Surfaces in video memory are not always linearly packed. Subsurfaces - # will also have a larger pitch than their real width. - # - # This value is not needed for normal Pygame usage. + with self.assertRaises(pygame.error): + surface = pygame.display.set_mode() + pygame.display.quit() + surface.get_offset() + finally: + pygame.display.quit() - self.fail() + def test_get_palette(self): + palette = [Color(i, i, i) for i in range(256)] + surf = pygame.Surface((2, 2), 0, 8) + surf.set_palette(palette) + palette2 = surf.get_palette() - def todo_test_get_shifts(self): + self.assertEqual(len(palette2), len(palette)) + for c2, c in zip(palette2, palette): + self.assertEqual(c2, c) + for c in palette2: + self.assertIsInstance(c, pygame.Color) + def test_get_palette_at(self): + # See also test_get_palette + surf = pygame.Surface((2, 2), 0, 8) + color = pygame.Color(1, 2, 3, 255) + surf.set_palette_at(0, color) + color2 = surf.get_palette_at(0) + self.assertIsInstance(color2, pygame.Color) + self.assertEqual(color2, color) + self.assertRaises(IndexError, surf.get_palette_at, -1) + self.assertRaises(IndexError, surf.get_palette_at, 256) + + def test_get_pitch(self): + # Test get_pitch() on several surfaces of varying size/depth + sizes = ((2, 2), (7, 33), (33, 7), (2, 734), (734, 2), (734, 734)) + depths = [8, 24, 32] + for width, height in sizes: + for depth in depths: + # Test get_pitch() on parent surface + surf = pygame.Surface((width, height), depth=depth) + buff = surf.get_buffer() + pitch = buff.length / surf.get_height() + test_pitch = surf.get_pitch() + self.assertEqual(pitch, test_pitch) + # Test get_pitch() on subsurface with same rect as parent + rect1 = surf.get_rect() + subsurf1 = surf.subsurface(rect1) + sub_buff1 = subsurf1.get_buffer() + sub_pitch1 = sub_buff1.length / subsurf1.get_height() + test_sub_pitch1 = subsurf1.get_pitch() + self.assertEqual(sub_pitch1, test_sub_pitch1) + # Test get_pitch on subsurface with modified rect + rect2 = rect1.inflate(-width / 2, -height / 2) + subsurf2 = surf.subsurface(rect2) + sub_buff2 = subsurf2.get_buffer() + sub_pitch2 = sub_buff2.length / float(subsurf2.get_height()) + test_sub_pitch2 = subsurf2.get_pitch() + self.assertEqual(sub_pitch2, test_sub_pitch2) + + def test_get_shifts(self): + """ + Tests whether Surface.get_shifts returns proper + RGBA shifts under various conditions. + """ # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_shifts: - - # Surface.get_shifts(): return (R, G, B, A) - # the bit shifts needed to convert between a color and a mapped integer - # - # Returns the pixel shifts need to convert between each color and a - # mapped integer. - # - # This value is not needed for normal Pygame usage. - - self.fail() - - def todo_test_get_size(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_size: - - # Surface.get_size(): return (width, height) - # get the dimensions of the Surface - # - # Return the width and height of the Surface in pixels. - - self.fail() - - def todo_test_lock(self): - + # Surface.get_shifts(): return (R, G, B, A) + # the bit shifts needed to convert between color and mapped integer. + # Returns the pixel shifts need to convert between each color and a + # mapped integer. + # This value is not needed for normal Pygame usage. + + # Test for SDL2 on surfaces with various depths and alpha on/off + depths = [8, 24, 32] + alpha = 128 + off = None + for bit_depth in depths: + surface = pygame.Surface((32, 32), depth=bit_depth) + surface.set_alpha(alpha) + r1, g1, b1, a1 = surface.get_shifts() + surface.set_alpha(off) + r2, g2, b2, a2 = surface.get_shifts() + self.assertEqual((r1, g1, b1, a1), (r2, g2, b2, a2)) + + def test_get_size(self): + sizes = ((1, 1), (119, 10), (1000, 1000), (1, 5000), (1221, 1), (99, 999)) + for width, height in sizes: + surf = pygame.Surface((width, height)) + found_size = surf.get_size() + self.assertEqual((width, height), found_size) + + def test_lock(self): # __doc__ (as of 2008-08-02) for pygame.surface.Surface.lock: - # Surface.lock(): return None - # lock the Surface memory for pixel access - # - # Lock the pixel data of a Surface for access. On accelerated - # Surfaces, the pixel data may be stored in volatile video memory or - # nonlinear compressed forms. When a Surface is locked the pixel - # memory becomes available to access by regular software. Code that - # reads or writes pixel values will need the Surface to be locked. - # - # Surfaces should not remain locked for more than necessary. A locked - # Surface can often not be displayed or managed by Pygame. - # - # Not all Surfaces require locking. The Surface.mustlock() method can - # determine if it is actually required. There is no performance - # penalty for locking and unlocking a Surface that does not need it. - # - # All pygame functions will automatically lock and unlock the Surface - # data as needed. If a section of code is going to make calls that - # will repeatedly lock and unlock the Surface many times, it can be - # helpful to wrap the block inside a lock and unlock pair. - # - # It is safe to nest locking and unlocking calls. The surface will - # only be unlocked after the final lock is released. - # + # Surface.lock(): return None + # lock the Surface memory for pixel access + # + # Lock the pixel data of a Surface for access. On accelerated + # Surfaces, the pixel data may be stored in volatile video memory or + # nonlinear compressed forms. When a Surface is locked the pixel + # memory becomes available to access by regular software. Code that + # reads or writes pixel values will need the Surface to be locked. + # + # Surfaces should not remain locked for more than necessary. A locked + # Surface can often not be displayed or managed by Pygame. + # + # Not all Surfaces require locking. The Surface.mustlock() method can + # determine if it is actually required. There is no performance + # penalty for locking and unlocking a Surface that does not need it. + # + # All pygame functions will automatically lock and unlock the Surface + # data as needed. If a section of code is going to make calls that + # will repeatedly lock and unlock the Surface many times, it can be + # helpful to wrap the block inside a lock and unlock pair. + # + # It is safe to nest locking and unlocking calls. The surface will + # only be unlocked after the final lock is released. + # - self.fail() + # Basic + surf = pygame.Surface((100, 100)) + surf.lock() + self.assertTrue(surf.get_locked()) + + # Nested + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + surf.lock() + surf.lock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Already Locked + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) def test_map_rgb(self): color = Color(0, 128, 255, 64) @@ -1096,51 +2277,49 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): surf.set_at((0, 0), c) self.assertEqual(surf.get_at((0, 0)), color) - def todo_test_mustlock(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.mustlock: - - # Surface.mustlock(): return bool - # test if the Surface requires locking - # - # Returns True if the Surface is required to be locked to access pixel - # data. Usually pure software Surfaces do not require locking. This - # method is rarely needed, since it is safe and quickest to just lock - # all Surfaces as needed. - # - # All pygame functions will automatically lock and unlock the Surface - # data as needed. If a section of code is going to make calls that - # will repeatedly lock and unlock the Surface many times, it can be - # helpful to wrap the block inside a lock and unlock pair. - # - - self.fail() + def test_mustlock(self): + # Test that subsurfaces mustlock + surf = pygame.Surface((1024, 1024)) + subsurf = surf.subsurface((0, 0, 1024, 1024)) + self.assertTrue(subsurf.mustlock()) + self.assertFalse(surf.mustlock()) + # Tests nested subsurfaces + rects = ((0, 0, 512, 512), (0, 0, 256, 256), (0, 0, 128, 128)) + surf_stack = [] + surf_stack.append(surf) + surf_stack.append(subsurf) + for rect in rects: + surf_stack.append(surf_stack[-1].subsurface(rect)) + self.assertTrue(surf_stack[-1].mustlock()) + self.assertTrue(surf_stack[-2].mustlock()) def test_set_alpha_none(self): """surf.set_alpha(None) disables blending""" - s = pygame.Surface((1,1), SRCALPHA, 32) + s = pygame.Surface((1, 1), SRCALPHA, 32) s.fill((0, 255, 0, 128)) s.set_alpha(None) self.assertEqual(None, s.get_alpha()) - s2 = pygame.Surface((1,1), SRCALPHA, 32) + s2 = pygame.Surface((1, 1), SRCALPHA, 32) s2.fill((255, 0, 0, 255)) s2.blit(s, (0, 0)) self.assertEqual(s2.get_at((0, 0))[0], 0, "the red component should be 0") def test_set_alpha_value(self): """surf.set_alpha(x), where x != None, enables blending""" - s = pygame.Surface((1,1), SRCALPHA, 32) + s = pygame.Surface((1, 1), SRCALPHA, 32) s.fill((0, 255, 0, 128)) s.set_alpha(255) - s2 = pygame.Surface((1,1), SRCALPHA, 32) + s2 = pygame.Surface((1, 1), SRCALPHA, 32) s2.fill((255, 0, 0, 255)) s2.blit(s, (0, 0)) - self.assertGreater(s2.get_at((0, 0))[0], 0, "the red component should be above 0") + self.assertGreater( + s2.get_at((0, 0))[0], 0, "the red component should be above 0" + ) def test_palette_colorkey(self): - """ test bug discovered by robertpfeiffer + """test bug discovered by robertpfeiffer https://github.com/pygame/pygame/issues/721 """ surf = pygame.image.load(example_path(os.path.join("data", "alien2.png"))) @@ -1161,195 +2340,199 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): def test_set_palette(self): palette = [pygame.Color(i, i, i) for i in range(256)] - palette[10] = tuple(palette[10]) # 4 element tuple - palette[11] = tuple(palette[11])[0:3] # 3 element tuple + palette[10] = tuple(palette[10]) # 4 element tuple + palette[11] = tuple(palette[11])[0:3] # 3 element tuple surf = pygame.Surface((2, 2), 0, 8) - pygame.display.init() - try: - pygame.display.set_mode((100, 50)) - surf.set_palette(palette) - for i in range(256): - self.assertEqual(surf.map_rgb(palette[i]), i, - "palette color %i" % (i,)) - c = palette[i] - surf.fill(c) - self.assertEqual(surf.get_at((0, 0)), c, - "palette color %i" % (i,)) - for i in range(10): - palette[i] = pygame.Color(255 - i, 0, 0) - surf.set_palette(palette[0:10]) - for i in range(256): - self.assertEqual(surf.map_rgb(palette[i]), i, - "palette color %i" % (i,)) - c = palette[i] - surf.fill(c) - self.assertEqual(surf.get_at((0, 0)), c, - "palette color %i" % (i,)) - self.assertRaises(ValueError, surf.set_palette, - [Color(1, 2, 3, 254)]) - self.assertRaises(ValueError, surf.set_palette, - (1, 2, 3, 254)) - finally: - pygame.display.quit() + surf.set_palette(palette) + for i in range(256): + self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,)) + c = palette[i] + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,)) + for i in range(10): + palette[i] = pygame.Color(255 - i, 0, 0) + surf.set_palette(palette[0:10]) + for i in range(256): + self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,)) + c = palette[i] + surf.fill(c) + self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,)) + self.assertRaises(ValueError, surf.set_palette, [Color(1, 2, 3, 254)]) + self.assertRaises(ValueError, surf.set_palette, (1, 2, 3, 254)) def test_set_palette__fail(self): - pygame.init() palette = 256 * [(10, 20, 30)] surf = pygame.Surface((2, 2), 0, 32) self.assertRaises(pygame.error, surf.set_palette, palette) - pygame.quit() + + def test_set_palette__set_at(self): + surf = pygame.Surface((2, 2), depth=8) + palette = 256 * [(10, 20, 30)] + palette[1] = (50, 40, 30) + surf.set_palette(palette) + + # calling set_at on a palettized surface should set the pixel to + # the closest color in the palette. + surf.set_at((0, 0), (60, 50, 40)) + self.assertEqual(surf.get_at((0, 0)), (50, 40, 30, 255)) + self.assertEqual(surf.get_at((1, 0)), (10, 20, 30, 255)) def test_set_palette_at(self): - pygame.display.init() - try: - pygame.display.set_mode((100, 50)) - surf = pygame.Surface((2, 2), 0, 8) - original = surf.get_palette_at(10) - replacement = Color(1, 1, 1, 255) - if replacement == original: - replacement = Color(2, 2, 2, 255) - surf.set_palette_at(10, replacement) - self.assertEqual(surf.get_palette_at(10), replacement) - next = tuple(original) - surf.set_palette_at(10, next) - self.assertEqual(surf.get_palette_at(10), next) - next = tuple(original)[0:3] - surf.set_palette_at(10, next) - self.assertEqual(surf.get_palette_at(10), next) - self.assertRaises(IndexError, - surf.set_palette_at, - 256, replacement) - self.assertRaises(IndexError, - surf.set_palette_at, - -1, replacement) - finally: - pygame.display.quit() + surf = pygame.Surface((2, 2), 0, 8) + original = surf.get_palette_at(10) + replacement = Color(1, 1, 1, 255) + if replacement == original: + replacement = Color(2, 2, 2, 255) + surf.set_palette_at(10, replacement) + self.assertEqual(surf.get_palette_at(10), replacement) + next = tuple(original) + surf.set_palette_at(10, next) + self.assertEqual(surf.get_palette_at(10), next) + next = tuple(original)[0:3] + surf.set_palette_at(10, next) + self.assertEqual(surf.get_palette_at(10), next) + self.assertRaises(IndexError, surf.set_palette_at, 256, replacement) + self.assertRaises(IndexError, surf.set_palette_at, -1, replacement) def test_subsurface(self): - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.subsurface: - # Surface.subsurface(Rect): return Surface - # create a new surface that references its parent - # - # Returns a new Surface that shares its pixels with its new parent. - # The new Surface is considered a child of the original. Modifications - # to either Surface pixels will effect each other. Surface information - # like clipping area and color keys are unique to each Surface. - # - # The new Surface will inherit the palette, color key, and alpha - # settings from its parent. - # - # It is possible to have any number of subsurfaces and subsubsurfaces - # on the parent. It is also possible to subsurface the display Surface - # if the display mode is not hardware accelerated. - # - # See the Surface.get_offset(), Surface.get_parent() to learn more - # about the state of a subsurface. - # + # Surface.subsurface(Rect): return Surface + # create a new surface that references its parent + # + # Returns a new Surface that shares its pixels with its new parent. + # The new Surface is considered a child of the original. Modifications + # to either Surface pixels will effect each other. Surface information + # like clipping area and color keys are unique to each Surface. + # + # The new Surface will inherit the palette, color key, and alpha + # settings from its parent. + # + # It is possible to have any number of subsurfaces and subsubsurfaces + # on the parent. It is also possible to subsurface the display Surface + # if the display mode is not hardware accelerated. + # + # See the Surface.get_offset(), Surface.get_parent() to learn more + # about the state of a subsurface. + # surf = pygame.Surface((16, 16)) - s = surf.subsurface(0,0,1,1) - s = surf.subsurface((0,0,1,1)) + s = surf.subsurface(0, 0, 1, 1) + s = surf.subsurface((0, 0, 1, 1)) - #s = surf.subsurface((0,0,1,1), 1) + # s = surf.subsurface((0,0,1,1), 1) # This form is not acceptable. - #s = surf.subsurface(0,0,10,10, 1) - - self.assertRaises(ValueError, surf.subsurface, (0,0,1,1,666)) + # s = surf.subsurface(0,0,10,10, 1) + self.assertRaises(ValueError, surf.subsurface, (0, 0, 1, 1, 666)) self.assertEqual(s.get_shifts(), surf.get_shifts()) self.assertEqual(s.get_masks(), surf.get_masks()) self.assertEqual(s.get_losses(), surf.get_losses()) - # Issue 2 at Bitbucket.org/pygame/pygame + # Issue https://github.com/pygame/pygame/issues/2 surf = pygame.Surface.__new__(pygame.Surface) self.assertRaises(pygame.error, surf.subsurface, (0, 0, 0, 0)) - def todo_test_unlock(self): - - # __doc__ (as of 2008-08-02) for pygame.surface.Surface.unlock: - - # Surface.unlock(): return None - # unlock the Surface memory from pixel access - # - # Unlock the Surface pixel data after it has been locked. The unlocked - # Surface can once again be drawn and managed by Pygame. See the - # Surface.lock() documentation for more details. - # - # All pygame functions will automatically lock and unlock the Surface - # data as needed. If a section of code is going to make calls that - # will repeatedly lock and unlock the Surface many times, it can be - # helpful to wrap the block inside a lock and unlock pair. - # - # It is safe to nest locking and unlocking calls. The surface will - # only be unlocked after the final lock is released. - # - - self.fail() + def test_unlock(self): + # Basic + surf = pygame.Surface((100, 100)) + surf.lock() + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Nested + surf = pygame.Surface((100, 100)) + surf.lock() + surf.lock() + surf.unlock() + self.assertTrue(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Already Unlocked + surf = pygame.Surface((100, 100)) + surf.unlock() + self.assertFalse(surf.get_locked()) + surf.unlock() + self.assertFalse(surf.get_locked()) + + # Surface can be relocked + surf = pygame.Surface((100, 100)) + surf.lock() + surf.unlock() + self.assertFalse(surf.get_locked()) + surf.lock() + surf.unlock() + self.assertFalse(surf.get_locked()) def test_unmap_rgb(self): # Special case, 8 bit-per-pixel surface (has a palette). surf = pygame.Surface((2, 2), 0, 8) c = (1, 1, 1) # Unlikely to be in a default palette. i = 67 - pygame.display.init() - try: - pygame.display.set_mode((100, 50)) - surf.set_palette_at(i, c) - unmapped_c = surf.unmap_rgb(i) - self.assertEqual(unmapped_c, c) - # Confirm it is a Color instance - self.assertIsInstance(unmapped_c, pygame.Color) - finally: - pygame.display.quit() + surf.set_palette_at(i, c) + unmapped_c = surf.unmap_rgb(i) + self.assertEqual(unmapped_c, c) + # Confirm it is a Color instance + self.assertIsInstance(unmapped_c, pygame.Color) # Remaining, non-pallete, cases. c = (128, 64, 12, 255) - formats = [(0, 16), (0, 24), (0, 32), - (SRCALPHA, 16), (SRCALPHA, 32)] + formats = [(0, 16), (0, 24), (0, 32), (SRCALPHA, 16), (SRCALPHA, 32)] for flags, bitsize in formats: surf = pygame.Surface((2, 2), flags, bitsize) unmapped_c = surf.unmap_rgb(surf.map_rgb(c)) surf.fill(c) comparison_c = surf.get_at((0, 0)) - self.assertEqual(unmapped_c, comparison_c, - "%s != %s, flags: %i, bitsize: %i" % - (unmapped_c, comparison_c, flags, bitsize)) + self.assertEqual( + unmapped_c, + comparison_c, + "%s != %s, flags: %i, bitsize: %i" + % (unmapped_c, comparison_c, flags, bitsize), + ) # Confirm it is a Color instance self.assertIsInstance(unmapped_c, pygame.Color) def test_scroll(self): - scrolls = [(8, 2, 3), - (16, 2, 3), - (24, 2, 3), - (32, 2, 3), - (32, -1, -3), - (32, 0, 0), - (32, 11, 0), - (32, 0, 11), - (32, -11, 0), - (32, 0, -11), - (32, -11, 2), - (32, 2, -11)] + scrolls = [ + (8, 2, 3), + (16, 2, 3), + (24, 2, 3), + (32, 2, 3), + (32, -1, -3), + (32, 0, 0), + (32, 11, 0), + (32, 0, 11), + (32, -11, 0), + (32, 0, -11), + (32, -11, 2), + (32, 2, -11), + ] for bitsize, dx, dy in scrolls: surf = pygame.Surface((10, 10), 0, bitsize) surf.fill((255, 0, 0)) - surf.fill((0, 255, 0), (2, 2, 2, 2,)) + surf.fill((0, 255, 0), (2, 2, 2, 2)) comp = surf.copy() comp.blit(surf, (dx, dy)) surf.scroll(dx, dy) w, h = surf.get_size() for x in range(w): for y in range(h): - self.assertEqual(surf.get_at((x, y)), - comp.get_at((x, y)), - "%s != %s, bpp:, %i, x: %i, y: %i" % - (surf.get_at((x, y)), - comp.get_at((x, y)), - bitsize, dx, dy)) + with self.subTest(x=x, y=y): + self.assertEqual( + surf.get_at((x, y)), + comp.get_at((x, y)), + "%s != %s, bpp:, %i, x: %i, y: %i" + % ( + surf.get_at((x, y)), + comp.get_at((x, y)), + bitsize, + dx, + dy, + ), + ) # Confirm clip rect containment surf = pygame.Surface((20, 13), 0, 32) surf.fill((255, 0, 0)) @@ -1363,8 +2546,7 @@ class SurfaceTypeTest(AssertRaisesRegexMixin, unittest.TestCase): w, h = surf.get_size() for x in range(w): for y in range(h): - self.assertEqual(surf.get_at((x, y)), - comp.get_at((x, y))) + self.assertEqual(surf.get_at((x, y)), comp.get_at((x, y))) # Confirm keyword arguments and per-pixel alpha spot_color = (0, 255, 0, 128) surf = pygame.Surface((4, 4), pygame.SRCALPHA, 32) @@ -1473,46 +2655,44 @@ class SurfaceSubtypeTest(unittest.TestCase): class SurfaceGetBufferTest(unittest.TestCase): - # These tests requires ctypes. They are disabled if ctypes # is not installed. - # try: ArrayInterface except NameError: - __tags__ = ('ignore', 'subprocess_ignore') + __tags__ = ("ignore", "subprocess_ignore") lilendian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN def _check_interface_2D(self, s): s_w, s_h = s.get_size() - s_bytesize = s.get_bytesize(); + s_bytesize = s.get_bytesize() s_pitch = s.get_pitch() s_pixels = s._pixels_address # check the array interface structure fields. - v = s.get_view('2') + v = s.get_view("2") if not IS_PYPY: flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE - if (s.get_pitch() == s_w * s_bytesize): + if s.get_pitch() == s_w * s_bytesize: flags |= PAI_FORTRAN inter = ArrayInterface(v) self.assertEqual(inter.two, 2) self.assertEqual(inter.nd, 2) - self.assertEqual(inter.typekind, 'u') + self.assertEqual(inter.typekind, "u") self.assertEqual(inter.itemsize, s_bytesize) self.assertEqual(inter.shape[0], s_w) self.assertEqual(inter.shape[1], s_h) self.assertEqual(inter.strides[0], s_bytesize) self.assertEqual(inter.strides[1], s_pitch) self.assertEqual(inter.flags, flags) - self.assertEqual(inter.data, s_pixels); + self.assertEqual(inter.data, s_pixels) def _check_interface_3D(self, s): s_w, s_h = s.get_size() - s_bytesize = s.get_bytesize(); + s_bytesize = s.get_bytesize() s_pitch = s.get_pitch() s_pixels = s._pixels_address s_shifts = list(s.get_shifts()) @@ -1558,13 +2738,13 @@ class SurfaceGetBufferTest(unittest.TestCase): return # check the array interface structure fields. - v = s.get_view('3') + v = s.get_view("3") if not IS_PYPY: inter = ArrayInterface(v) flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE self.assertEqual(inter.two, 2) self.assertEqual(inter.nd, 3) - self.assertEqual(inter.typekind, 'u') + self.assertEqual(inter.typekind, "u") self.assertEqual(inter.itemsize, 1) self.assertEqual(inter.shape[0], s_w) self.assertEqual(inter.shape[1], s_h) @@ -1573,11 +2753,11 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(inter.strides[1], s_pitch) self.assertEqual(inter.strides[2], step) self.assertEqual(inter.flags, flags) - self.assertEqual(inter.data, s_pixels + offset); + self.assertEqual(inter.data, s_pixels + offset) def _check_interface_rgba(self, s, plane): s_w, s_h = s.get_size() - s_bytesize = s.get_bytesize(); + s_bytesize = s.get_bytesize() s_pitch = s.get_pitch() s_pixels = s._pixels_address s_shifts = s.get_shifts() @@ -1592,27 +2772,27 @@ class SurfaceGetBufferTest(unittest.TestCase): offset = s_bytesize - offset - 1 # check the array interface structure fields. - v = s.get_view('rgba'[plane]) + v = s.get_view("rgba"[plane]) if not IS_PYPY: inter = ArrayInterface(v) flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE self.assertEqual(inter.two, 2) self.assertEqual(inter.nd, 2) - self.assertEqual(inter.typekind, 'u') + self.assertEqual(inter.typekind, "u") self.assertEqual(inter.itemsize, 1) self.assertEqual(inter.shape[0], s_w) self.assertEqual(inter.shape[1], s_h) self.assertEqual(inter.strides[0], s_bytesize) self.assertEqual(inter.strides[1], s_pitch) self.assertEqual(inter.flags, flags) - self.assertEqual(inter.data, s_pixels + offset); + self.assertEqual(inter.data, s_pixels + offset) def test_array_interface(self): self._check_interface_2D(pygame.Surface((5, 7), 0, 8)) self._check_interface_2D(pygame.Surface((5, 7), 0, 16)) self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 16)) self._check_interface_3D(pygame.Surface((5, 7), 0, 24)) - self._check_interface_3D(pygame.Surface((8, 4), 0, 24)) # No gaps + self._check_interface_3D(pygame.Surface((8, 4), 0, 24)) # No gaps self._check_interface_2D(pygame.Surface((5, 7), 0, 32)) self._check_interface_3D(pygame.Surface((5, 7), 0, 32)) self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 32)) @@ -1625,38 +2805,29 @@ class SurfaceGetBufferTest(unittest.TestCase): # Reversed RGB byte order s = pygame.Surface(sz, 0, 32) s_masks = list(s.get_masks()) - masks = [0xff, 0xff00, 0xff0000] + masks = [0xFF, 0xFF00, 0xFF0000] if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]: masks = s_masks[2::-1] + s_masks[3:4] self._check_interface_3D(pygame.Surface(sz, 0, 32, masks)) s = pygame.Surface(sz, 0, 24) s_masks = list(s.get_masks()) - masks = [0xff, 0xff00, 0xff0000] + masks = [0xFF, 0xFF00, 0xFF0000] if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]: masks = s_masks[2::-1] + s_masks[3:4] self._check_interface_3D(pygame.Surface(sz, 0, 24, masks)) - masks = [0xff00, 0xff0000, 0xff000000, 0] + masks = [0xFF00, 0xFF0000, 0xFF000000, 0] self._check_interface_3D(pygame.Surface(sz, 0, 32, masks)) - # Unsupported RGB byte orders - if pygame.get_sdl_version()[0] == 1: - # Invalid mask values with SDL2 - masks = [0xff00, 0xff, 0xff0000, 0] - self.assertRaises(ValueError, - pygame.Surface(sz, 0, 24, masks).get_view, '3') - def test_array_interface_alpha(self): - for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], - [24, 16, 8, 0], [16, 8, 0, 24]]: - masks = [0xff << s for s in shifts] + for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]: + masks = [0xFF << s for s in shifts] s = pygame.Surface((4, 2), pygame.SRCALPHA, 32, masks) self._check_interface_rgba(s, 3) def test_array_interface_rgb(self): - for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], - [24, 16, 8, 0], [16, 8, 0, 24]]: - masks = [0xff << s for s in shifts] + for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]: + masks = [0xFF << s for s in shifts] masks[3] = 0 for plane in range(3): s = pygame.Surface((4, 2), 0, 24) @@ -1664,9 +2835,10 @@ class SurfaceGetBufferTest(unittest.TestCase): s = pygame.Surface((4, 2), 0, 32) self._check_interface_rgba(s, plane) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf_PyBUF_flags_bytes(self): from pygame.tests.test_utils import buftools + Importer = buftools.Importer s = pygame.Surface((10, 6), 0, 32) a = s.get_buffer() @@ -1686,7 +2858,7 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertFalse(b.readonly) b = Importer(a, buftools.PyBUF_FORMAT) self.assertEqual(b.ndim, 0) - self.assertEqual(b.format, 'B') + self.assertEqual(b.format, "B") b = Importer(a, buftools.PyBUF_ND) self.assertEqual(b.ndim, 1) self.assertTrue(b.format is None) @@ -1701,7 +2873,7 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.ndim, 1) self.assertTrue(b.format is None) self.assertEqual(b.strides, (1,)) - s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous + s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous a = s2.get_buffer() b = Importer(a, buftools.PyBUF_SIMPLE) self.assertEqual(b.ndim, 0) @@ -1723,14 +2895,15 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.ndim, 1) self.assertEqual(b.strides, (1,)) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf_PyBUF_flags_0D(self): # This is the same handler as used by get_buffer(), so just # confirm that it succeeds for one case. from pygame.tests.test_utils import buftools + Importer = buftools.Importer s = pygame.Surface((10, 6), 0, 32) - a = s.get_view('0') + a = s.get_view("0") b = Importer(a, buftools.PyBUF_SIMPLE) self.assertEqual(b.ndim, 0) self.assertTrue(b.format is None) @@ -1742,12 +2915,13 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertFalse(b.readonly) self.assertEqual(b.buf, s._pixels_address) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf_PyBUF_flags_1D(self): from pygame.tests.test_utils import buftools + Importer = buftools.Importer s = pygame.Surface((10, 6), 0, 32) - a = s.get_view('1') + a = s.get_view("1") b = Importer(a, buftools.PyBUF_SIMPLE) self.assertEqual(b.ndim, 0) self.assertTrue(b.format is None) @@ -1764,7 +2938,7 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertFalse(b.readonly) b = Importer(a, buftools.PyBUF_FORMAT) self.assertEqual(b.ndim, 0) - self.assertEqual(b.format, '=I') + self.assertEqual(b.format, "=I") b = Importer(a, buftools.PyBUF_ND) self.assertEqual(b.ndim, 1) self.assertTrue(b.format is None) @@ -1780,12 +2954,13 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertTrue(b.format is None) self.assertEqual(b.strides, (s.get_bytesize(),)) - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf_PyBUF_flags_2D(self): from pygame.tests.test_utils import buftools + Importer = buftools.Importer s = pygame.Surface((10, 6), 0, 32) - a = s.get_view('2') + a = s.get_view("2") # Non dimensional requests, no PyDEF_ND, are handled by the # 1D surface buffer code, so only need to confirm a success. b = Importer(a, buftools.PyBUF_SIMPLE) @@ -1811,11 +2986,11 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.buf, s._pixels_address) b = Importer(a, buftools.PyBUF_RECORDS_RO) self.assertEqual(b.ndim, 2) - self.assertEqual(b.format, '=I') + self.assertEqual(b.format, "=I") self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) b = Importer(a, buftools.PyBUF_RECORDS) self.assertEqual(b.ndim, 2) - self.assertEqual(b.format, '=I') + self.assertEqual(b.format, "=I") self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) b = Importer(a, buftools.PyBUF_F_CONTIGUOUS) self.assertEqual(b.ndim, 2) @@ -1826,10 +3001,9 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.format, None) self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch())) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous - a = s2.get_view('2') + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + s2 = s.subsurface((1, 1, 7, 4)) # Not contiguous + a = s2.get_view("2") b = Importer(a, buftools.PyBUF_STRIDES) self.assertEqual(b.ndim, 2) self.assertTrue(b.format is None) @@ -1842,40 +3016,37 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.buf, s2._pixels_address) b = Importer(a, buftools.PyBUF_RECORDS) self.assertEqual(b.ndim, 2) - self.assertEqual(b.format, '=I') + self.assertEqual(b.format, "=I") self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_WRITABLE) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) - - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf_PyBUF_flags_3D(self): from pygame.tests.test_utils import buftools + Importer = buftools.Importer s = pygame.Surface((12, 6), 0, 24) rmask, gmask, bmask, amask = s.get_masks() if self.lilendian: - if rmask == 0x0000ff: + if rmask == 0x0000FF: color_step = 1 addr_offset = 0 else: color_step = -1 addr_offset = 2 else: - if (rmask == 0xff0000): + if rmask == 0xFF0000: color_step = 1 addr_offset = 0 else: color_step = -1 addr_offset = 2 - a = s.get_view('3') + a = s.get_view("3") b = Importer(a, buftools.PyBUF_STRIDES) w, h = s.get_size() shape = w, h, 3 @@ -1891,42 +3062,40 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.buf, s._pixels_address + addr_offset) b = Importer(a, buftools.PyBUF_RECORDS_RO) self.assertEqual(b.ndim, 3) - self.assertEqual(b.format, 'B') + self.assertEqual(b.format, "B") self.assertEqual(b.strides, strides) b = Importer(a, buftools.PyBUF_RECORDS) self.assertEqual(b.ndim, 3) - self.assertEqual(b.format, 'B') + self.assertEqual(b.format, "B") self.assertEqual(b.strides, strides) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) - - @unittest.skipIf(not pygame.HAVE_NEWBUF, 'newbuf not implemented') + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) + + @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented") def test_newbuf_PyBUF_flags_rgba(self): # All color plane views are handled by the same routine, # so only one plane need be checked. from pygame.tests.test_utils import buftools + Importer = buftools.Importer s = pygame.Surface((12, 6), 0, 24) rmask, gmask, bmask, amask = s.get_masks() if self.lilendian: - if rmask == 0x0000ff: + if rmask == 0x0000FF: addr_offset = 0 else: addr_offset = 2 else: - if rmask == 0xff0000: + if rmask == 0xFF0000: addr_offset = 0 else: addr_offset = 2 - a = s.get_view('R') + a = s.get_view("R") b = Importer(a, buftools.PyBUF_STRIDES) w, h = s.get_size() shape = w, h @@ -1942,26 +3111,22 @@ class SurfaceGetBufferTest(unittest.TestCase): self.assertEqual(b.buf, s._pixels_address + addr_offset) b = Importer(a, buftools.PyBUF_RECORDS_RO) self.assertEqual(b.ndim, 2) - self.assertEqual(b.format, 'B') + self.assertEqual(b.format, "B") self.assertEqual(b.strides, strides) b = Importer(a, buftools.PyBUF_RECORDS) self.assertEqual(b.ndim, 2) - self.assertEqual(b.format, 'B') + self.assertEqual(b.format, "B") self.assertEqual(b.strides, strides) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE) self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_C_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_F_CONTIGUOUS) - self.assertRaises(BufferError, Importer, a, - buftools.PyBUF_ANY_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS) + self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS) class SurfaceBlendTest(unittest.TestCase): - def setUp(self): # Needed for 8 bits-per-pixel color palette surface tests. pygame.display.init() @@ -1969,15 +3134,24 @@ class SurfaceBlendTest(unittest.TestCase): def tearDown(self): pygame.display.quit() - _test_palette = [(0, 0, 0, 255), - (10, 30, 60, 0), - (25, 75, 100, 128), - (200, 150, 100, 200), - (0, 100, 200, 255)] + _test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 0), + (25, 75, 100, 128), + (200, 150, 100, 200), + (0, 100, 200, 255), + ] surf_size = (10, 12) - _test_points = [((0, 0), 1), ((4, 5), 1), ((9, 0), 2), - ((5, 5), 2), ((0, 11), 3), ((4, 6), 3), - ((9, 11), 4), ((5, 6), 4)] + _test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] def _make_surface(self, bitsize, srcalpha=False, palette=None): if palette is None: @@ -2009,37 +3183,47 @@ class SurfaceBlendTest(unittest.TestCase): if surf.get_bitsize() == 16: palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette] for posn, i in self._test_points: - self.assertEqual(surf.get_at(posn), palette[i], - "%s != %s: flags: %i, bpp: %i, posn: %s%s" % - (surf.get_at(posn), - palette[i], surf.get_flags(), - surf.get_bitsize(), posn, msg)) + self.assertEqual( + surf.get_at(posn), + palette[i], + "%s != %s: flags: %i, bpp: %i, posn: %s%s" + % ( + surf.get_at(posn), + palette[i], + surf.get_flags(), + surf.get_bitsize(), + posn, + msg, + ), + ) def test_blit_blend(self): - sources = [self._make_src_surface(8), - self._make_src_surface(16), - self._make_src_surface(16, srcalpha=True), - self._make_src_surface(24), - self._make_src_surface(32), - self._make_src_surface(32, srcalpha=True)] - destinations = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True)] - blend = [('BLEND_ADD', (0, 25, 100, 255), - lambda a, b: min(a + b, 255)), - ('BLEND_SUB', (100, 25, 0, 100), - lambda a, b: max(a - b, 0)), - ('BLEND_MULT', (100, 200, 0, 0), - lambda a, b: (a * b) // 256), - ('BLEND_MIN', (255, 0, 0, 255), min), - ('BLEND_MAX', (0, 255, 0, 255), max)] + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_SUB", (100, 25, 0, 100), lambda a, b: max(a - b, 0)), + ("BLEND_MULT", (100, 200, 0, 0), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_MIN", (255, 0, 0, 255), min), + ("BLEND_MAX", (0, 255, 0, 255), max), + ] for src in sources: - src_palette = [src.unmap_rgb(src.map_rgb(c)) - for c in self._test_palette] + src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette] for dst in destinations: for blend_name, dst_color, op in blend: dc = dst.unmap_rgb(dst.map_rgb(dst_color)) @@ -2053,20 +3237,22 @@ class SurfaceBlendTest(unittest.TestCase): c = dst.unmap_rgb(dst.map_rgb(c)) p.append(c) dst.fill(dst_color) - dst.blit(src, - (0, 0), - special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, - (", op: %s, src bpp: %i" - ", src flags: %i" % - (blend_name, - src.get_bitsize(), - src.get_flags()))) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface( + dst, + p, + ( + ", op: %s, src bpp: %i" + ", src flags: %i" + % (blend_name, src.get_bitsize(), src.get_flags()) + ), + ) src = self._make_src_surface(32) masks = src.get_masks() - dst = pygame.Surface(src.get_size(), 0, 32, - [masks[2], masks[1], masks[0], masks[3]]) + dst = pygame.Surface( + src.get_size(), 0, 32, [masks[2], masks[1], masks[0], masks[3]] + ) for blend_name, dst_color, op in blend: p = [] for src_color in self._test_palette: @@ -2074,10 +3260,8 @@ class SurfaceBlendTest(unittest.TestCase): c.append(255) p.append(tuple(c)) dst.fill(dst_color) - dst.blit(src, - (0, 0), - special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, ", %s" % blend_name) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") # Blend blits are special cased for 32 to 32 bit surfaces. # @@ -2085,10 +3269,10 @@ class SurfaceBlendTest(unittest.TestCase): # least significant bytes. pat = self._make_src_surface(32) masks = pat.get_masks() - if min(masks) == intify(0xFF000000): - masks = [longify(m) >> 8 for m in masks] + if min(masks) == 0xFF000000: + masks = [m >> 8 for m in masks] else: - masks = [intify(m << 8) for m in masks] + masks = [m << 8 for m in masks] src = pygame.Surface(pat.get_size(), 0, 32, masks) self._fill_surface(src) dst = pygame.Surface(src.get_size(), 0, 32, masks) @@ -2099,36 +3283,36 @@ class SurfaceBlendTest(unittest.TestCase): c.append(255) p.append(tuple(c)) dst.fill(dst_color) - dst.blit(src, - (0, 0), - special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, ", %s" % blend_name) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") def test_blit_blend_rgba(self): - sources = [self._make_src_surface(8), - self._make_src_surface(16), - self._make_src_surface(16, srcalpha=True), - self._make_src_surface(24), - self._make_src_surface(32), - self._make_src_surface(32, srcalpha=True)] - destinations = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True)] - blend = [('BLEND_RGBA_ADD', (0, 25, 100, 255), - lambda a, b: min(a + b, 255)), - ('BLEND_RGBA_SUB', (0, 25, 100, 255), - lambda a, b: max(a - b, 0)), - ('BLEND_RGBA_MULT', (0, 7, 100, 255), - lambda a, b: (a * b) // 256), - ('BLEND_RGBA_MIN', (0, 255, 0, 255), min), - ('BLEND_RGBA_MAX', (0, 255, 0, 255), max)] + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_RGBA_MIN", (0, 255, 0, 255), min), + ("BLEND_RGBA_MAX", (0, 255, 0, 255), max), + ] for src in sources: - src_palette = [src.unmap_rgb(src.map_rgb(c)) - for c in self._test_palette] + src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette] for dst in destinations: for blend_name, dst_color, op in blend: dc = dst.unmap_rgb(dst.map_rgb(dst_color)) @@ -2140,15 +3324,16 @@ class SurfaceBlendTest(unittest.TestCase): c = dst.unmap_rgb(dst.map_rgb(c)) p.append(c) dst.fill(dst_color) - dst.blit(src, - (0, 0), - special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, - (", op: %s, src bpp: %i" - ", src flags: %i" % - (blend_name, - src.get_bitsize(), - src.get_flags()))) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface( + dst, + p, + ( + ", op: %s, src bpp: %i" + ", src flags: %i" + % (blend_name, src.get_bitsize(), src.get_flags()) + ), + ) # Blend blits are special cased for 32 to 32 bit surfaces # with per-pixel alpha. @@ -2156,16 +3341,17 @@ class SurfaceBlendTest(unittest.TestCase): # Confirm the general case is used instead when the formats differ. src = self._make_src_surface(32, srcalpha=True) masks = src.get_masks() - dst = pygame.Surface(src.get_size(), SRCALPHA, 32, - (masks[2], masks[1], masks[0], masks[3])) + dst = pygame.Surface( + src.get_size(), SRCALPHA, 32, (masks[2], masks[1], masks[0], masks[3]) + ) for blend_name, dst_color, op in blend: - p = [tuple([op(dst_color[i], src_color[i]) for i in range(4)]) - for src_color in self._test_palette] + p = [ + tuple(op(dst_color[i], src_color[i]) for i in range(4)) + for src_color in self._test_palette + ] dst.fill(dst_color) - dst.blit(src, - (0, 0), - special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, ", %s" % blend_name) + dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name)) + self._assert_surface(dst, p, f", {blend_name}") # Confirm this special case handles subsurfaces. src = pygame.Surface((8, 10), SRCALPHA, 32) @@ -2180,14 +3366,295 @@ class SurfaceBlendTest(unittest.TestCase): tst.fill((41, 32, 23, 14), (2, 3, 4, 4)) for x in range(8): for y in range(10): - self.assertEqual(dst.get_at((x, y)), tst.get_at((x, y)), - "%s != %s at (%i, %i)" % - (dst.get_at((x, y)), tst.get_at((x, y)), - x, y)) + self.assertEqual( + dst.get_at((x, y)), + tst.get_at((x, y)), + "%s != %s at (%i, %i)" + % (dst.get_at((x, y)), tst.get_at((x, y)), x, y), + ) + + def test_blit_blend_premultiplied(self): + def test_premul_surf( + src_col, + dst_col, + src_size=(16, 16), + dst_size=(16, 16), + src_bit_depth=32, + dst_bit_depth=32, + src_has_alpha=True, + dst_has_alpha=True, + ): + if src_bit_depth == 8: + src = pygame.Surface(src_size, 0, src_bit_depth) + palette = [src_col, dst_col] + src.set_palette(palette) + src.fill(palette[0]) + elif src_has_alpha: + src = pygame.Surface(src_size, SRCALPHA, src_bit_depth) + src.fill(src_col) + else: + src = pygame.Surface(src_size, 0, src_bit_depth) + src.fill(src_col) + + if dst_bit_depth == 8: + dst = pygame.Surface(dst_size, 0, dst_bit_depth) + palette = [src_col, dst_col] + dst.set_palette(palette) + dst.fill(palette[1]) + elif dst_has_alpha: + dst = pygame.Surface(dst_size, SRCALPHA, dst_bit_depth) + dst.fill(dst_col) + else: + dst = pygame.Surface(dst_size, 0, dst_bit_depth) + dst.fill(dst_col) + + dst.blit(src, (0, 0), special_flags=BLEND_PREMULTIPLIED) + + actual_col = dst.get_at( + (int(float(src_size[0] / 2.0)), int(float(src_size[0] / 2.0))) + ) + + # This is the blend pre-multiplied formula + if src_col.a == 0: + expected_col = dst_col + elif src_col.a == 255: + expected_col = src_col + else: + # sC + dC - (((dC + 1) * sA >> 8) + expected_col = pygame.Color( + (src_col.r + dst_col.r - ((dst_col.r + 1) * src_col.a >> 8)), + (src_col.g + dst_col.g - ((dst_col.g + 1) * src_col.a >> 8)), + (src_col.b + dst_col.b - ((dst_col.b + 1) * src_col.a >> 8)), + (src_col.a + dst_col.a - ((dst_col.a + 1) * src_col.a >> 8)), + ) + if not dst_has_alpha: + expected_col.a = 255 + + return (expected_col, actual_col) + + # # Colour Tests + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(0, 0, 0, 0), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(0, 0, 0, 0)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(0, 0, 0, 0), pygame.Color(0, 0, 0, 0)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(2, 2, 2, 2), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(2, 2, 2, 2)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(2, 2, 2, 2), pygame.Color(2, 2, 2, 2)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(9, 9, 9, 9), pygame.Color(40, 20, 0, 51)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(9, 9, 9, 9)) + ) + + self.assertEqual( + *test_premul_surf(pygame.Color(9, 9, 9, 9), pygame.Color(9, 9, 9, 9)) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(127, 127, 127, 127), pygame.Color(40, 20, 0, 51) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), pygame.Color(127, 127, 127, 127) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(127, 127, 127, 127), pygame.Color(127, 127, 127, 127) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(200, 200, 200, 200), pygame.Color(40, 20, 0, 51) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), pygame.Color(200, 200, 200, 200) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(200, 200, 200, 200), pygame.Color(200, 200, 200, 200) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(255, 255, 255, 255), pygame.Color(40, 20, 0, 51) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), pygame.Color(255, 255, 255, 255) + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(255, 255, 255, 255), pygame.Color(255, 255, 255, 255) + ) + ) + + # Surface format tests + self.assertRaises( + IndexError, + test_premul_surf, + pygame.Color(255, 255, 255, 255), + pygame.Color(255, 255, 255, 255), + src_size=(0, 0), + dst_size=(0, 0), + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(40, 20, 0, 51), + pygame.Color(30, 20, 0, 51), + src_size=(4, 4), + dst_size=(9, 9), + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 51), + pygame.Color(40, 20, 0, 51), + src_size=(17, 67), + dst_size=(69, 69), + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 51), + src_size=(17, 67), + dst_size=(69, 69), + src_has_alpha=True, + ) + ) + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 51), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_has_alpha=False, + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + dst_bit_depth=24, + src_has_alpha=True, + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=24, + src_has_alpha=False, + dst_has_alpha=True, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=24, + dst_bit_depth=24, + src_has_alpha=False, + dst_has_alpha=False, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=8, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + dst_bit_depth=8, + ) + ) + + self.assertEqual( + *test_premul_surf( + pygame.Color(30, 20, 0, 255), + pygame.Color(40, 20, 0, 255), + src_size=(17, 67), + dst_size=(69, 69), + src_bit_depth=8, + dst_bit_depth=8, + ) + ) def test_blit_blend_big_rect(self): - """ test that an oversized rect works ok. - """ + """test that an oversized rect works ok.""" color = (1, 2, 3, 255) area = (1, 1, 30, 30) s1 = pygame.Surface((4, 4), 0, 32) @@ -2228,24 +3695,24 @@ class SurfaceBlendTest(unittest.TestCase): self.assertEqual(dst.get_at((0, 0)), (0, 0, 0, 255)) def test_fill_blend(self): - destinations = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True)] - blend = [('BLEND_ADD', (0, 25, 100, 255), - lambda a, b: min(a + b, 255)), - ('BLEND_SUB', (0, 25, 100, 255), - lambda a, b: max(a - b, 0)), - ('BLEND_MULT', (0, 7, 100, 255), - lambda a, b: (a * b) // 256), - ('BLEND_MIN', (0, 255, 0, 255), min), - ('BLEND_MAX', (0, 255, 0, 255), max)] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_MIN", (0, 255, 0, 255), min), + ("BLEND_MAX", (0, 255, 0, 255), max), + ] for dst in destinations: - dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) - for c in self._test_palette] + dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette] for blend_name, fill_color, op in blend: fc = dst.unmap_rgb(dst.map_rgb(fill_color)) self._fill_surface(dst) @@ -2259,27 +3726,27 @@ class SurfaceBlendTest(unittest.TestCase): c = dst.unmap_rgb(dst.map_rgb(c)) p.append(c) dst.fill(fill_color, special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, ", %s" % blend_name) + self._assert_surface(dst, p, f", {blend_name}") def test_fill_blend_rgba(self): - destinations = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True)] - blend = [('BLEND_RGBA_ADD', (0, 25, 100, 255), - lambda a, b: min(a + b, 255)), - ('BLEND_RGBA_SUB', (0, 25, 100, 255), - lambda a, b: max(a - b, 0)), - ('BLEND_RGBA_MULT', (0, 7, 100, 255), - lambda a, b: (a * b) // 256), - ('BLEND_RGBA_MIN', (0, 255, 0, 255), min), - ('BLEND_RGBA_MAX', (0, 255, 0, 255), max)] + destinations = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] + blend = [ + ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)), + ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)), + ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8), + ("BLEND_RGBA_MIN", (0, 255, 0, 255), min), + ("BLEND_RGBA_MAX", (0, 255, 0, 255), max), + ] for dst in destinations: - dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) - for c in self._test_palette] + dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette] for blend_name, fill_color, op in blend: fc = dst.unmap_rgb(dst.map_rgb(fill_color)) self._fill_surface(dst) @@ -2291,13 +3758,81 @@ class SurfaceBlendTest(unittest.TestCase): c = dst.unmap_rgb(dst.map_rgb(c)) p.append(c) dst.fill(fill_color, special_flags=getattr(pygame, blend_name)) - self._assert_surface(dst, p, ", %s" % blend_name) + self._assert_surface(dst, p, f", {blend_name}") + + def test_surface_premul_alpha(self): + """Ensure that .premul_alpha() works correctly""" + + # basic functionality at valid bit depths - 32, 16 & 8 + s1 = pygame.Surface((100, 100), pygame.SRCALPHA, 32) + s1.fill(pygame.Color(255, 255, 255, 100)) + s1_alpha = s1.premul_alpha() + self.assertEqual(s1_alpha.get_at((50, 50)), pygame.Color(100, 100, 100, 100)) + + # 16 bit colour has less precision + s2 = pygame.Surface((100, 100), pygame.SRCALPHA, 16) + s2.fill( + pygame.Color( + int(15 / 15 * 255), + int(15 / 15 * 255), + int(15 / 15 * 255), + int(10 / 15 * 255), + ) + ) + s2_alpha = s2.premul_alpha() + self.assertEqual( + s2_alpha.get_at((50, 50)), + pygame.Color( + int(10 / 15 * 255), + int(10 / 15 * 255), + int(10 / 15 * 255), + int(10 / 15 * 255), + ), + ) + + # invalid surface - we need alpha to pre-multiply + invalid_surf = pygame.Surface((100, 100), 0, 32) + invalid_surf.fill(pygame.Color(255, 255, 255, 100)) + with self.assertRaises(ValueError): + invalid_surf.premul_alpha() + + # churn a bunch of values + test_colors = [ + (200, 30, 74), + (76, 83, 24), + (184, 21, 6), + (74, 4, 74), + (76, 83, 24), + (184, 21, 234), + (160, 30, 74), + (96, 147, 204), + (198, 201, 60), + (132, 89, 74), + (245, 9, 224), + (184, 112, 6), + ] + + for r, g, b in test_colors: + for a in range(255): + with self.subTest(r=r, g=g, b=b, a=a): + surf = pygame.Surface((10, 10), pygame.SRCALPHA, 32) + surf.fill(pygame.Color(r, g, b, a)) + surf = surf.premul_alpha() + self.assertEqual( + surf.get_at((5, 5)), + Color( + ((r + 1) * a) >> 8, + ((g + 1) * a) >> 8, + ((b + 1) * a) >> 8, + a, + ), + ) class SurfaceSelfBlitTest(unittest.TestCase): """Blit to self tests. - This test case is in response to MotherHamster Bugzilla Bug 19. + This test case is in response to https://github.com/pygame/pygame/issues/19 """ def setUp(self): @@ -2307,9 +3842,7 @@ class SurfaceSelfBlitTest(unittest.TestCase): def tearDown(self): pygame.display.quit() - _test_palette = [(0, 0, 0, 255), - (255, 0, 0, 0), - (0, 255, 0, 255)] + _test_palette = [(0, 0, 0, 255), (255, 0, 0, 0), (0, 255, 0, 255)] surf_size = (9, 6) def _fill_surface(self, surf, palette=None): @@ -2334,10 +3867,14 @@ class SurfaceSelfBlitTest(unittest.TestCase): w, h = a.get_size() for x in range(w): for y in range(h): - self.assertEqual(a.get_at((x, y)), b.get_at((x, y)), - ("%s != %s, bpp: %i" % - (a.get_at((x, y)), b.get_at((x, y)), - a.get_bitsize()))) + self.assertEqual( + a.get_at((x, y)), + b.get_at((x, y)), + ( + "%s != %s, bpp: %i" + % (a.get_at((x, y)), b.get_at((x, y)), a.get_bitsize()) + ), + ) def test_overlap_check(self): # Ensure overlapping blits are properly detected. There are two @@ -2350,11 +3887,13 @@ class SurfaceSelfBlitTest(unittest.TestCase): rectc_left = (128, 64, 32, 255) rectc_right = (255, 255, 255, 255) colors = [(255, 255, 255, 255), (128, 64, 32, 255)] - overlaps = [(0, 0, 1, 0, (50, 0)), - (0, 0, 49, 1, (98, 2)), - (0, 0, 49, 49, (98, 98)), - (49, 0, 0, 1, (0, 2)), - (49, 0, 0, 49, (0, 98))] + overlaps = [ + (0, 0, 1, 0, (50, 0)), + (0, 0, 49, 1, (98, 2)), + (0, 0, 49, 49, (98, 98)), + (49, 0, 0, 1, (0, 2)), + (49, 0, 0, 49, (0, 98)), + ] surfs = [pygame.Surface((100, 100), SRCALPHA, 32)] surf = pygame.Surface((100, 100), 0, 32) surf.set_alpha(255) @@ -2371,10 +3910,10 @@ class SurfaceSelfBlitTest(unittest.TestCase): self.assertEqual(surf.get_at(test_posn), rectc_right) # https://github.com/pygame/pygame/issues/370#issuecomment-364625291 - @unittest.skipIf('ppc64le' in platform.uname(), 'known ppc64le issue') + @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue") def test_colorkey(self): # Check a workaround for an SDL 1.2.13 surface self-blit problem - # (MotherHamster Bugzilla bug 19). + # https://github.com/pygame/pygame/issues/19 pygame.display.set_mode((100, 50)) # Needed for 8bit surface bitsizes = [8, 16, 24, 32] for bitsize in bitsizes: @@ -2394,10 +3933,10 @@ class SurfaceSelfBlitTest(unittest.TestCase): self._assert_same(surf, comp) # https://github.com/pygame/pygame/issues/370#issuecomment-364625291 - @unittest.skipIf('ppc64le' in platform.uname(), 'known ppc64le issue') + @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue") def test_blanket_alpha(self): # Check a workaround for an SDL 1.2.13 surface self-blit problem - # (MotherHamster Bugzilla bug 19). + # https://github.com/pygame/pygame/issues/19 pygame.display.set_mode((100, 50)) # Needed for 8bit surface bitsizes = [8, 16, 24, 32] for bitsize in bitsizes: @@ -2426,40 +3965,34 @@ class SurfaceSelfBlitTest(unittest.TestCase): def test_blend(self): bitsizes = [8, 16, 24, 32] - blends = ['BLEND_ADD', - 'BLEND_SUB', - 'BLEND_MULT', - 'BLEND_MIN', - 'BLEND_MAX'] + blends = ["BLEND_ADD", "BLEND_SUB", "BLEND_MULT", "BLEND_MIN", "BLEND_MAX"] for bitsize in bitsizes: surf = self._make_surface(bitsize) comp = self._make_surface(bitsize) for blend in blends: self._fill_surface(surf) self._fill_surface(comp) - comp.blit(surf, (3, 0), - special_flags=getattr(pygame, blend)) - surf.blit(surf, (3, 0), - special_flags=getattr(pygame, blend)) + comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) self._assert_same(surf, comp) def test_blend_rgba(self): bitsizes = [16, 32] - blends = ['BLEND_RGBA_ADD', - 'BLEND_RGBA_SUB', - 'BLEND_RGBA_MULT', - 'BLEND_RGBA_MIN', - 'BLEND_RGBA_MAX'] + blends = [ + "BLEND_RGBA_ADD", + "BLEND_RGBA_SUB", + "BLEND_RGBA_MULT", + "BLEND_RGBA_MIN", + "BLEND_RGBA_MAX", + ] for bitsize in bitsizes: surf = self._make_surface(bitsize, srcalpha=True) comp = self._make_surface(bitsize, srcalpha=True) for blend in blends: self._fill_surface(surf) self._fill_surface(comp) - comp.blit(surf, (3, 0), - special_flags=getattr(pygame, blend)) - surf.blit(surf, (3, 0), - special_flags=getattr(pygame, blend)) + comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) + surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend)) self._assert_same(surf, comp) def test_subsurface(self): @@ -2471,11 +4004,13 @@ class SurfaceSelfBlitTest(unittest.TestCase): sub.blit(surf, (0, 0)) del sub self._assert_same(surf, comp) + # Blitting a subsurface to its owner is forbidden because of - # lock conficts. This limitation allows the overlap check + # lock conflicts. This limitation allows the overlap check # in PySurface_Blit of alphablit.c to be simplified. def do_blit(d, s): d.blit(s, (0, 0)) + sub = surf.subsurface((1, 1, 2, 2)) self.assertRaises(pygame.error, do_blit, surf, sub) @@ -2488,7 +4023,6 @@ class SurfaceSelfBlitTest(unittest.TestCase): class SurfaceFillTest(unittest.TestCase): - def setUp(self): pygame.display.init() @@ -2505,10 +4039,10 @@ class SurfaceFillTest(unittest.TestCase): screen.fill((0, 0, 255), (0, 240, 320, 240)) # Now apply a clip rect, such that only the left side of the - # screen should be effected by blit opperations. + # screen should be effected by blit operations. screen.set_clip((0, 0, 320, 480)) - # Test fills with each special flag, and additionaly without any. + # Test fills with each special flag, and additionally without any. screen.fill((255, 0, 0, 127), (160, 0, 320, 30), 0) screen.fill((255, 0, 0, 127), (160, 30, 320, 30), pygame.BLEND_ADD) screen.fill((0, 127, 127, 127), (160, 60, 320, 30), pygame.BLEND_SUB) @@ -2530,9 +4064,9 @@ class SurfaceFillTest(unittest.TestCase): pygame.display.flip() # Compare colors on both sides of window - for y in range(5, 480, 10): + for y in range(5, 480, 10): self.assertEqual(screen.get_at((10, y)), screen.get_at((330, 480 - y))) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/surfarray_tags.py b/venv/Lib/site-packages/pygame/tests/surfarray_tags.py index 132d559955ca9b5a48ae55f49bb1f140a7efb90e..baa535cef8165f188af626d495c8bbbfc347cbae 100644 --- a/venv/Lib/site-packages/pygame/tests/surfarray_tags.py +++ b/venv/Lib/site-packages/pygame/tests/surfarray_tags.py @@ -1,4 +1,4 @@ -__tags__ = ['array'] +__tags__ = ["array"] exclude = False @@ -13,4 +13,4 @@ else: exclude = True if exclude: - __tags__.extend(('ignore', 'subprocess_ignore')) + __tags__.extend(("ignore", "subprocess_ignore")) diff --git a/venv/Lib/site-packages/pygame/tests/surfarray_test.py b/venv/Lib/site-packages/pygame/tests/surfarray_test.py index 4f43fcecc3b24b7423b19ebbdc7b4367bfe06c33..0863da77e02d0c2ab8d630424f160350cc58dade 100644 --- a/venv/Lib/site-packages/pygame/tests/surfarray_test.py +++ b/venv/Lib/site-packages/pygame/tests/surfarray_test.py @@ -1,37 +1,53 @@ - import unittest import platform -from numpy import \ - uint8, uint16, uint32, uint64, zeros, \ - float32, float64, alltrue, rint, arange +from numpy import ( + uint8, + uint16, + uint32, + uint64, + zeros, + float32, + float64, + alltrue, + rint, + arange, +) import pygame from pygame.locals import * import pygame.surfarray -arraytype = 'numpy' -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() -@unittest.skipIf(IS_PYPY, 'pypy skip known failure') # TODO -class SurfarrayModuleTest (unittest.TestCase): +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class SurfarrayModuleTest(unittest.TestCase): pixels2d = {8: True, 16: True, 24: False, 32: True} pixels3d = {8: False, 16: False, 24: True, 32: True} array2d = {8: True, 16: True, 24: True, 32: True} array3d = {8: False, 16: False, 24: True, 32: True} - test_palette = [(0, 0, 0, 255), - (10, 30, 60, 255), - (25, 75, 100, 255), - (100, 150, 200, 255), - (0, 100, 200, 255)] + test_palette = [ + (0, 0, 0, 255), + (10, 30, 60, 255), + (25, 75, 100, 255), + (100, 150, 200, 255), + (0, 100, 200, 255), + ] surf_size = (10, 12) - test_points = [((0, 0), 1), ((4, 5), 1), ((9, 0), 2), - ((5, 5), 2), ((0, 11), 3), ((4, 6), 3), - ((9, 11), 4), ((5, 6), 4)] + test_points = [ + ((0, 0), 1), + ((4, 5), 1), + ((9, 0), 2), + ((5, 5), 2), + ((0, 11), 3), + ((4, 6), 3), + ((9, 11), 4), + ((5, 6), 4), + ] @classmethod def setUpClass(cls): @@ -48,9 +64,6 @@ class SurfarrayModuleTest (unittest.TestCase): if not pygame.get_init(): pygame.init() - # Makes sure the same array package is used each time. - pygame.surfarray.use_arraytype(arraytype) - def _make_surface(self, bitsize, srcalpha=False, palette=None): if palette is None: palette = self.test_palette @@ -81,28 +94,36 @@ class SurfarrayModuleTest (unittest.TestCase): if surf.get_bitsize() == 16: palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette] for posn, i in self.test_points: - self.assertEqual(surf.get_at(posn), palette[i], - "%s != %s: flags: %i, bpp: %i, posn: %s%s" % - (surf.get_at(posn), - palette[i], surf.get_flags(), - surf.get_bitsize(), posn, msg)) + self.assertEqual( + surf.get_at(posn), + palette[i], + "%s != %s: flags: %i, bpp: %i, posn: %s%s" + % ( + surf.get_at(posn), + palette[i], + surf.get_flags(), + surf.get_bitsize(), + posn, + msg, + ), + ) def _make_array3d(self, dtype): return zeros((self.surf_size[0], self.surf_size[1], 3), dtype) def _fill_array2d(self, arr, surf): palette = self.test_palette - arr[:5,:6] = surf.map_rgb(palette[1]) - arr[5:,:6] = surf.map_rgb(palette[2]) - arr[:5,6:] = surf.map_rgb(palette[3]) - arr[5:,6:] = surf.map_rgb(palette[4]) + arr[:5, :6] = surf.map_rgb(palette[1]) + arr[5:, :6] = surf.map_rgb(palette[2]) + arr[:5, 6:] = surf.map_rgb(palette[3]) + arr[5:, 6:] = surf.map_rgb(palette[4]) def _fill_array3d(self, arr): palette = self.test_palette - arr[:5,:6] = palette[1][:3] - arr[5:,:6] = palette[2][:3] - arr[:5,6:] = palette[3][:3] - arr[5:,6:] = palette[4][:3] + arr[:5, :6] = palette[1][:3] + arr[5:, :6] = palette[2][:3] + arr[:5, 6:] = palette[3][:3] + arr[5:, 6:] = palette[4][:3] def _make_src_array3d(self, dtype): arr = self._make_array3d(dtype) @@ -113,71 +134,89 @@ class SurfarrayModuleTest (unittest.TestCase): return zeros(self.surf_size, dtype) def test_array2d(self): - - sources = [self._make_src_surface(8), - self._make_src_surface(16), - self._make_src_surface(16, srcalpha=True), - self._make_src_surface(24), - self._make_src_surface(32), - self._make_src_surface(32, srcalpha=True)] + sources = [ + self._make_src_surface(8), + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] palette = self.test_palette alpha_color = (0, 0, 0, 128) for surf in sources: arr = pygame.surfarray.array2d(surf) for posn, i in self.test_points: - self.assertEqual(arr[posn], surf.get_at_mapped(posn), - "%s != %s: flags: %i, bpp: %i, posn: %s" % - (arr[posn], - surf.get_at_mapped(posn), - surf.get_flags(), surf.get_bitsize(), - posn)) + self.assertEqual( + arr[posn], + surf.get_at_mapped(posn), + "%s != %s: flags: %i, bpp: %i, posn: %s" + % ( + arr[posn], + surf.get_at_mapped(posn), + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) if surf.get_masks()[3]: surf.fill(alpha_color) arr = pygame.surfarray.array2d(surf) posn = (0, 0) - self.assertEqual(arr[posn], surf.get_at_mapped(posn), - "%s != %s: bpp: %i" % - (arr[posn], - surf.get_at_mapped(posn), - surf.get_bitsize())) + self.assertEqual( + arr[posn], + surf.get_at_mapped(posn), + "%s != %s: bpp: %i" + % (arr[posn], surf.get_at_mapped(posn), surf.get_bitsize()), + ) def test_array3d(self): - - sources = [self._make_src_surface(16), - self._make_src_surface(16, srcalpha=True), - self._make_src_surface(24), - self._make_src_surface(32), - self._make_src_surface(32, srcalpha=True)] + sources = [ + self._make_src_surface(16), + self._make_src_surface(16, srcalpha=True), + self._make_src_surface(24), + self._make_src_surface(32), + self._make_src_surface(32, srcalpha=True), + ] palette = self.test_palette for surf in sources: arr = pygame.surfarray.array3d(surf) + def same_color(ac, sc): - return (ac[0] == sc[0] and - ac[1] == sc[1] and - ac[2] == sc[2]) + return ac[0] == sc[0] and ac[1] == sc[1] and ac[2] == sc[2] + for posn, i in self.test_points: - self.assertTrue(same_color(arr[posn], surf.get_at(posn)), - "%s != %s: flags: %i, bpp: %i, posn: %s" % ( - tuple(arr[posn]), surf.get_at(posn), - surf.get_flags(), surf.get_bitsize(), - posn)) + self.assertTrue( + same_color(arr[posn], surf.get_at(posn)), + "%s != %s: flags: %i, bpp: %i, posn: %s" + % ( + tuple(arr[posn]), + surf.get_at(posn), + surf.get_flags(), + surf.get_bitsize(), + posn, + ), + ) def test_array_alpha(self): - - palette = [(0, 0, 0, 0), - (10, 50, 100, 255), - (60, 120, 240, 130), - (64, 128, 255, 0), - (255, 128, 0, 65)] - targets = [self._make_src_surface(8, palette=palette), - self._make_src_surface(16, palette=palette), - self._make_src_surface(16, palette=palette, srcalpha=True), - self._make_src_surface(24, palette=palette), - self._make_src_surface(32, palette=palette), - self._make_src_surface(32, palette=palette, srcalpha=True)] + palette = [ + (0, 0, 0, 0), + (10, 50, 100, 255), + (60, 120, 240, 130), + (64, 128, 255, 0), + (255, 128, 0, 65), + ] + targets = [ + self._make_src_surface(8, palette=palette), + self._make_src_surface(16, palette=palette), + self._make_src_surface(16, palette=palette, srcalpha=True), + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] for surf in targets: p = palette @@ -186,12 +225,15 @@ class SurfarrayModuleTest (unittest.TestCase): arr = pygame.surfarray.array_alpha(surf) if surf.get_masks()[3]: for (x, y), i in self.test_points: - self.assertEqual(arr[x, y], p[i][3], - ("%i != %i, posn: (%i, %i), " - "bitsize: %i" % - (arr[x, y], p[i][3], - x, y, - surf.get_bitsize()))) + self.assertEqual( + arr[x, y], + p[i][3], + ( + "%i != %i, posn: (%i, %i), " + "bitsize: %i" + % (arr[x, y], p[i][3], x, y, surf.get_bitsize()) + ), + ) else: self.assertTrue(alltrue(arr == 255)) @@ -200,11 +242,12 @@ class SurfarrayModuleTest (unittest.TestCase): blanket_alpha = surf.get_alpha() surf.set_alpha(None) arr = pygame.surfarray.array_alpha(surf) - self.assertTrue(alltrue(arr == 255), - "All alpha values should be 255 when" - " surf.set_alpha(None) has been set." - " bitsize: %i, flags: %i" % ( - surf.get_bitsize(), surf.get_flags())) + self.assertTrue( + alltrue(arr == 255), + "All alpha values should be 255 when" + " surf.set_alpha(None) has been set." + " bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) surf.set_alpha(blanket_alpha) # Bug for per-pixel alpha surface when blanket alpha 0. @@ -213,28 +256,33 @@ class SurfarrayModuleTest (unittest.TestCase): surf.set_alpha(0) arr = pygame.surfarray.array_alpha(surf) if surf.get_masks()[3]: - self.assertFalse(alltrue(arr == 255), - "bitsize: %i, flags: %i" % - (surf.get_bitsize(), surf.get_flags())) + self.assertFalse( + alltrue(arr == 255), + "bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) else: - self.assertTrue(alltrue(arr == 255), - "bitsize: %i, flags: %i" % ( - surf.get_bitsize(), surf.get_flags())) + self.assertTrue( + alltrue(arr == 255), + "bitsize: %i, flags: %i" % (surf.get_bitsize(), surf.get_flags()), + ) surf.set_alpha(blanket_alpha) def test_array_colorkey(self): - - palette = [(0, 0, 0, 0), - (10, 50, 100, 255), - (60, 120, 240, 130), - (64, 128, 255, 0), - (255, 128, 0, 65)] - targets = [self._make_src_surface(8, palette=palette), - self._make_src_surface(16, palette=palette), - self._make_src_surface(16, palette=palette, srcalpha=True), - self._make_src_surface(24, palette=palette), - self._make_src_surface(32, palette=palette), - self._make_src_surface(32, palette=palette, srcalpha=True)] + palette = [ + (0, 0, 0, 0), + (10, 50, 100, 255), + (60, 120, 240, 130), + (64, 128, 255, 0), + (255, 128, 0, 65), + ] + targets = [ + self._make_src_surface(8, palette=palette), + self._make_src_surface(16, palette=palette), + self._make_src_surface(16, palette=palette, srcalpha=True), + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] for surf in targets: p = palette @@ -250,36 +298,69 @@ class SurfarrayModuleTest (unittest.TestCase): alphas[i] = 0 arr = pygame.surfarray.array_colorkey(surf) for (x, y), j in self.test_points: - self.assertEqual(arr[x, y], alphas[j], - ("%i != %i, posn: (%i, %i), " - "bitsize: %i" % - (arr[x, y], alphas[j], - x, y, - surf.get_bitsize()))) + self.assertEqual( + arr[x, y], + alphas[j], + ( + "%i != %i, posn: (%i, %i), " + "bitsize: %i" + % (arr[x, y], alphas[j], x, y, surf.get_bitsize()) + ), + ) + + def test_array_red(self): + self._test_array_rgb("red", 0) + + def test_array_green(self): + self._test_array_rgb("green", 1) + + def test_array_blue(self): + self._test_array_rgb("blue", 2) + + def _test_array_rgb(self, operation, mask_posn): + method_name = "array_" + operation + + array_rgb = getattr(pygame.surfarray, method_name) + palette = [ + (0, 0, 0, 255), + (5, 13, 23, 255), + (29, 31, 37, 255), + (131, 157, 167, 255), + (179, 191, 251, 255), + ] + plane = [c[mask_posn] for c in palette] - def test_blit_array(self): + targets = [ + self._make_src_surface(24, palette=palette), + self._make_src_surface(32, palette=palette), + self._make_src_surface(32, palette=palette, srcalpha=True), + ] + + for surf in targets: + self.assertFalse(surf.get_locked()) + for (x, y), i in self.test_points: + surf.fill(palette[i]) + arr = array_rgb(surf) + self.assertEqual(arr[x, y], plane[i]) + surf.fill((100, 100, 100, 250)) + self.assertEqual(arr[x, y], plane[i]) + self.assertFalse(surf.get_locked()) + del arr - # bug 24 at http://pygame.motherhamster.org/bugzilla/ - if 'numpy' in pygame.surfarray.get_arraytypes(): - prev = pygame.surfarray.get_arraytype() - # This would raise exception: - # File "[...]\pygame\_numpysurfarray.py", line 381, in blit_array - # (array[:,:,1::3] >> losses[1] << shifts[1]) | \ - # TypeError: unsupported operand type(s) for >>: 'float' and 'int' - pygame.surfarray.use_arraytype('numpy') - s = pygame.Surface((10,10), 0, 24) - a = pygame.surfarray.array3d(s) - pygame.surfarray.blit_array(s, a) - prev = pygame.surfarray.use_arraytype(prev) + def test_blit_array(self): + s = pygame.Surface((10, 10), 0, 24) + a = pygame.surfarray.array3d(s) + pygame.surfarray.blit_array(s, a) # target surfaces - targets = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True), - ] + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] # source arrays arrays3d = [] @@ -288,14 +369,12 @@ class SurfarrayModuleTest (unittest.TestCase): dtypes.append((64, uint64)) except NameError: pass - arrays3d = [(self._make_src_array3d(dtype), None) - for __, dtype in dtypes] + arrays3d = [(self._make_src_array3d(dtype), None) for __, dtype in dtypes] for bitsize in [8, 16, 24, 32]: palette = None if bitsize == 16: - s = pygame.Surface((1,1), 0, 16) - palette = [s.unmap_rgb(s.map_rgb(c)) - for c in self.test_palette] + s = pygame.Surface((1, 1), 0, 16) + palette = [s.unmap_rgb(s.map_rgb(c)) for c in self.test_palette] if self.pixels3d[bitsize]: surf = self._make_src_surface(bitsize) arr = pygame.surfarray.pixels3d(surf) @@ -337,8 +416,9 @@ class SurfarrayModuleTest (unittest.TestCase): pygame.surfarray.blit_array(surf, arr.astype(dtype)) self._assert_surface(surf) else: - self.assertRaises(ValueError, do_blit, - surf, self._make_array2d(dtype)) + self.assertRaises( + ValueError, do_blit, surf, self._make_array2d(dtype) + ) # Check alpha for 2D arrays surf = self._make_surface(16, srcalpha=True) @@ -358,30 +438,24 @@ class SurfarrayModuleTest (unittest.TestCase): # Check shifts arr3d = self._make_src_array3d(uint8) - shift_tests = [(16, - [12, 0, 8, 4], - [0xf000, 0xf, 0xf00, 0xf0]), - (24, - [16, 0, 8, 0], - [0xff0000, 0xff, 0xff00, 0]), - (32, - [0, 16, 24, 8], - [0xff, 0xff0000, 0xff000000, 0xff00])] + shift_tests = [ + (16, [12, 0, 8, 4], [0xF000, 0xF, 0xF00, 0xF0]), + (24, [16, 0, 8, 0], [0xFF0000, 0xFF, 0xFF00, 0]), + (32, [0, 16, 24, 8], [0xFF, 0xFF0000, 0xFF000000, 0xFF00]), + ] for bitsize, shifts, masks in shift_tests: surf = self._make_surface(bitsize, srcalpha=(shifts[3] != 0)) palette = None if bitsize == 16: - palette = [surf.unmap_rgb(surf.map_rgb(c)) - for c in self.test_palette] - surf.set_shifts(shifts) - surf.set_masks(masks) - pygame.surfarray.blit_array(surf, arr3d) - self._assert_surface(surf, palette) + palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in self.test_palette] + + self.assertRaises(TypeError, surf.set_shifts, shifts) + self.assertRaises(TypeError, surf.set_masks, masks) # Invalid arrays - surf = pygame.Surface((1,1), 0, 32) - t = 'abcd' + surf = pygame.Surface((1, 1), 0, 32) + t = "abcd" self.assertRaises(ValueError, do_blit, surf, t) surf_size = self.surf_size @@ -413,36 +487,36 @@ class SurfarrayModuleTest (unittest.TestCase): pygame.surfarray.blit_array(surf, farr) for x in range(w): for y in range(h): - self.assertEqual(surf.get_at_mapped((x, y)), - int(rint(farr[x, y]))) + self.assertEqual( + surf.get_at_mapped((x, y)), int(rint(farr[x, y])) + ) + # this test should be removed soon, when the function is deleted def test_get_arraytype(self): array_type = pygame.surfarray.get_arraytype() - self.assertEqual(array_type, 'numpy', - "unknown array type %s" % array_type) + self.assertEqual(array_type, "numpy", f"unknown array type {array_type}") + # this test should be removed soon, when the function is deleted def test_get_arraytypes(self): - arraytypes = pygame.surfarray.get_arraytypes() - self.assertIn('numpy', arraytypes) + self.assertIn("numpy", arraytypes) for atype in arraytypes: - self.assertEqual(atype, 'numpy', "unknown array type %s" % atype) + self.assertEqual(atype, "numpy", f"unknown array type {atype}") def test_make_surface(self): - # How does one properly test this with 2d arrays. It makes no sense # since the pixel format is not entirely dependent on element size. # Just make sure the surface pixel size is at least as large as the # array element size I guess. # for bitsize, dtype in [(8, uint8), (16, uint16), (24, uint32)]: -## Even this simple assertion fails for 2d arrays. Where's the problem? -## surf = pygame.surfarray.make_surface(self._make_array2d(dtype)) -## self.assertGreaterEqual(surf.get_bitsize(), bitsize, -## "not %i >= %i)" % (surf.get_bitsize(), bitsize)) -## + ## Even this simple assertion fails for 2d arrays. Where's the problem? + ## surf = pygame.surfarray.make_surface(self._make_array2d(dtype)) + ## self.assertGreaterEqual(surf.get_bitsize(), bitsize, + ## "not %i >= %i)" % (surf.get_bitsize(), bitsize)) + ## surf = pygame.surfarray.make_surface(self._make_src_array3d(dtype)) self._assert_surface(surf) @@ -461,38 +535,51 @@ class SurfarrayModuleTest (unittest.TestCase): surf = pygame.surfarray.make_surface(farr) for x in range(w): for y in range(h): - self.assertEqual(surf.get_at_mapped((x, y)), - int(rint(farr[x, y]))) + self.assertEqual( + surf.get_at_mapped((x, y)), int(rint(farr[x, y])) + ) def test_map_array(self): - arr3d = self._make_src_array3d(uint8) - targets = [self._make_surface(8), - self._make_surface(16), - self._make_surface(16, srcalpha=True), - self._make_surface(24), - self._make_surface(32), - self._make_surface(32, srcalpha=True)] + targets = [ + self._make_surface(8), + self._make_surface(16), + self._make_surface(16, srcalpha=True), + self._make_surface(24), + self._make_surface(32), + self._make_surface(32, srcalpha=True), + ] palette = self.test_palette for surf in targets: arr2d = pygame.surfarray.map_array(surf, arr3d) for posn, i in self.test_points: - self.assertEqual(arr2d[posn], surf.map_rgb(palette[i]), - "%i != %i, bitsize: %i, flags: %i" % - (arr2d[posn], surf.map_rgb(palette[i]), - surf.get_bitsize(), surf.get_flags())) + self.assertEqual( + arr2d[posn], + surf.map_rgb(palette[i]), + "%i != %i, bitsize: %i, flags: %i" + % ( + arr2d[posn], + surf.map_rgb(palette[i]), + surf.get_bitsize(), + surf.get_flags(), + ), + ) # Exception checks - self.assertRaises(ValueError, pygame.surfarray.map_array, - self._make_surface(32), - self._make_array2d(uint8)) + self.assertRaises( + ValueError, + pygame.surfarray.map_array, + self._make_surface(32), + self._make_array2d(uint8), + ) def test_pixels2d(self): - - sources = [self._make_surface(8), - self._make_surface(16, srcalpha=True), - self._make_surface(32, srcalpha=True)] + sources = [ + self._make_surface(8), + self._make_surface(16, srcalpha=True), + self._make_surface(32, srcalpha=True), + ] for surf in sources: self.assertFalse(surf.get_locked()) @@ -507,14 +594,10 @@ class SurfarrayModuleTest (unittest.TestCase): self._assert_surface(surf) # Error checks - self.assertRaises(ValueError, - pygame.surfarray.pixels2d, - self._make_surface(24)) + self.assertRaises(ValueError, pygame.surfarray.pixels2d, self._make_surface(24)) def test_pixels3d(self): - - sources = [self._make_surface(24), - self._make_surface(32)] + sources = [self._make_surface(24), self._make_surface(32)] for surf in sources: self.assertFalse(surf.get_locked()) @@ -532,27 +615,24 @@ class SurfarrayModuleTest (unittest.TestCase): color = (1, 2, 3, 0) surf = self._make_surface(32, srcalpha=True) arr = pygame.surfarray.pixels3d(surf) - arr[0,0] = color[:3] + arr[0, 0] = color[:3] self.assertEqual(surf.get_at((0, 0)), color) # Error checks def do_pixels3d(surf): pygame.surfarray.pixels3d(surf) - self.assertRaises(ValueError, - do_pixels3d, - self._make_surface(8)) - self.assertRaises(ValueError, - do_pixels3d, - self._make_surface(16)) + self.assertRaises(ValueError, do_pixels3d, self._make_surface(8)) + self.assertRaises(ValueError, do_pixels3d, self._make_surface(16)) def test_pixels_alpha(self): - - palette = [(0, 0, 0, 0), - (127, 127, 127, 0), - (127, 127, 127, 85), - (127, 127, 127, 170), - (127, 127, 127, 255)] + palette = [ + (0, 0, 0, 0), + (127, 127, 127, 0), + (127, 127, 127, 85), + (127, 127, 127, 170), + (127, 127, 127, 255), + ] alphas = [0, 45, 86, 99, 180] surf = self._make_src_surface(32, srcalpha=True, palette=palette) @@ -570,8 +650,7 @@ class SurfarrayModuleTest (unittest.TestCase): alpha = alphas[i] arr[x, y] = alpha color = (127, 127, 127, alpha) - self.assertEqual(surf.get_at((x, y)), color, - "posn: (%i, %i)" % (x, y)) + self.assertEqual(surf.get_at((x, y)), color, "posn: (%i, %i)" % (x, y)) del arr self.assertFalse(surf.get_locked()) @@ -581,34 +660,33 @@ class SurfarrayModuleTest (unittest.TestCase): def do_pixels_alpha(surf): pygame.surfarray.pixels_alpha(surf) - targets = [(8, False), - (16, False), - (16, True), - (24, False), - (32, False)] + targets = [(8, False), (16, False), (16, True), (24, False), (32, False)] for bitsize, srcalpha in targets: - self.assertRaises(ValueError, do_pixels_alpha, - self._make_surface(bitsize, srcalpha)) + self.assertRaises( + ValueError, do_pixels_alpha, self._make_surface(bitsize, srcalpha) + ) def test_pixels_red(self): - self._test_pixels_rgb('red', 0) + self._test_pixels_rgb("red", 0) def test_pixels_green(self): - self._test_pixels_rgb('green', 1) + self._test_pixels_rgb("green", 1) def test_pixels_blue(self): - self._test_pixels_rgb('blue', 2) + self._test_pixels_rgb("blue", 2) def _test_pixels_rgb(self, operation, mask_posn): method_name = "pixels_" + operation pixels_rgb = getattr(pygame.surfarray, method_name) - palette = [(0, 0, 0, 255), - (5, 13, 23, 255), - (29, 31, 37, 255), - (131, 157, 167, 255), - (179, 191, 251, 255)] + palette = [ + (0, 0, 0, 255), + (5, 13, 23, 255), + (29, 31, 37, 255), + (131, 157, 167, 255), + (179, 191, 251, 255), + ] plane = [c[mask_posn] for c in palette] surf24 = self._make_src_surface(24, srcalpha=False, palette=palette) @@ -630,32 +708,30 @@ class SurfarrayModuleTest (unittest.TestCase): self.assertEqual(surf.get_locks(), ()) # Check exceptions. - targets = [(8, False), - (16, False), - (16, True)] + targets = [(8, False), (16, False), (16, True)] for bitsize, srcalpha in targets: - self.assertRaises(ValueError, pixels_rgb, - self._make_surface(bitsize, srcalpha)) + self.assertRaises( + ValueError, pixels_rgb, self._make_surface(bitsize, srcalpha) + ) def test_use_arraytype(self): - def do_use_arraytype(atype): pygame.surfarray.use_arraytype(atype) - pygame.surfarray.use_arraytype('numpy') - self.assertEqual(pygame.surfarray.get_arraytype(), 'numpy') - self.assertRaises(ValueError, do_use_arraytype, 'not an option') + pygame.surfarray.use_arraytype("numpy") + self.assertEqual(pygame.surfarray.get_arraytype(), "numpy") + self.assertRaises(ValueError, do_use_arraytype, "not an option") - def test_surf_lock (self): - sf = pygame.Surface ((5, 5), 0, 32) - for atype in pygame.surfarray.get_arraytypes (): - pygame.surfarray.use_arraytype (atype) + def test_surf_lock(self): + sf = pygame.Surface((5, 5), 0, 32) + for atype in pygame.surfarray.get_arraytypes(): + pygame.surfarray.use_arraytype(atype) - ar = pygame.surfarray.pixels2d (sf) + ar = pygame.surfarray.pixels2d(sf) self.assertTrue(sf.get_locked()) - sf.unlock () + sf.unlock() self.assertTrue(sf.get_locked()) del ar @@ -663,5 +739,5 @@ class SurfarrayModuleTest (unittest.TestCase): self.assertEqual(sf.get_locks(), ()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/surflock_test.py b/venv/Lib/site-packages/pygame/tests/surflock_test.py index 931e96556de47ab6f61c4c25364130709dd2b40d..19f354bfd94a5256901cbe1da8a8b18e68963062 100644 --- a/venv/Lib/site-packages/pygame/tests/surflock_test.py +++ b/venv/Lib/site-packages/pygame/tests/surflock_test.py @@ -4,11 +4,11 @@ import platform import pygame -IS_PYPY = 'PyPy' == platform.python_implementation() +IS_PYPY = "PyPy" == platform.python_implementation() -@unittest.skipIf(IS_PYPY, 'pypy skip known failure') # TODO -class SurfaceLockTest(unittest.TestCase): +@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO +class SurfaceLockTest(unittest.TestCase): def test_lock(self): sf = pygame.Surface((5, 5)) @@ -39,7 +39,7 @@ class SurfaceLockTest(unittest.TestCase): # Test blitting on self: self.assertRaises(pygame.error, sf.blit, subsf, (0, 0)) - #self.assertRaises(pygame.error, subsf.blit, sf, (0, 0)) + # self.assertRaises(pygame.error, subsf.blit, sf, (0, 0)) # ^ Fails although it should not in my opinion. If I cannot # blit the subsurface to the surface, it should not be allowed # the other way around as well. @@ -139,5 +139,6 @@ class SurfaceLockTest(unittest.TestCase): self.assertEqual(sf.get_locked(), False) self.assertEqual(sf.get_locks(), ()) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/sysfont_test.py b/venv/Lib/site-packages/pygame/tests/sysfont_test.py index 9bfe623365443f543823390f38ccc8c819c7039c..0ae380aaeb8ec5de4b8fe769802de9caa364ea52 100644 --- a/venv/Lib/site-packages/pygame/tests/sysfont_test.py +++ b/venv/Lib/site-packages/pygame/tests/sysfont_test.py @@ -1,30 +1,51 @@ import unittest import platform + class SysfontModuleTest(unittest.TestCase): - def todo_test_create_aliases(self): - self.fail() + def test_create_aliases(self): + import pygame.sysfont + + pygame.sysfont.initsysfonts() + pygame.sysfont.create_aliases() + self.assertTrue(len(pygame.sysfont.Sysalias) > 0) - def todo_test_initsysfonts(self): - self.fail() + def test_initsysfonts(self): + import pygame.sysfont + + pygame.sysfont.initsysfonts() + self.assertTrue(len(pygame.sysfont.get_fonts()) > 0) - @unittest.skipIf('Darwin' not in platform.platform(), 'Not mac we skip.') + @unittest.skipIf("Darwin" not in platform.platform(), "Not mac we skip.") def test_initsysfonts_darwin(self): import pygame.sysfont + self.assertTrue(len(pygame.sysfont.get_fonts()) > 10) def test_sysfont(self): import pygame.font + pygame.font.init() - arial = pygame.font.SysFont('Arial', 40) + arial = pygame.font.SysFont("Arial", 40) + self.assertTrue(isinstance(arial, pygame.font.Font)) + + @unittest.skipIf( + ("Darwin" in platform.platform() or "Windows" in platform.platform()), + "Not unix we skip.", + ) + def test_initsysfonts_unix(self): + import pygame.sysfont + + self.assertTrue(len(pygame.sysfont.get_fonts()) > 0) - def todo_test_initsysfonts_unix(self): - self.fail() + @unittest.skipIf("Windows" not in platform.platform(), "Not windows we skip.") + def test_initsysfonts_win32(self): + import pygame.sysfont + + self.assertTrue(len(pygame.sysfont.get_fonts()) > 10) - def todo_test_initsysfonts_win32(self): - self.fail() -################################################################################ +############################################################################### -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/test_test_.py b/venv/Lib/site-packages/pygame/tests/test_test_.py deleted file mode 100644 index 5708909f6c4c2ea33029cf14e5688b1127d471cc..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/test_test_.py +++ /dev/null @@ -1,3 +0,0 @@ -while True: - pass - \ No newline at end of file diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/__init__.py b/venv/Lib/site-packages/pygame/tests/test_utils/__init__.py index 17d0cbac5c37d5b555f406359257813adb1ac516..a4994f2845880a6cd5cf0ccbcd63736571232526 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/__init__.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/__init__.py @@ -1,209 +1,201 @@ -#################################### IMPORTS ################################### +import os +import pygame +import sys +import tempfile +import time -is_pygame_pkg = __name__.startswith('pygame.tests.') +is_pygame_pkg = __name__.startswith("pygame.tests.") -import tempfile, sys, pygame, time, os +############################################################################### -################################################################################ -# Python 3.x compatibility -try: - xrange_ = xrange -except NameError: - xrange_ = range -try: - raw_input_ = raw_input -except NameError: - raw_input_ = input +def tostring(row): + """Convert row of bytes to string. Expects `row` to be an + ``array``. + """ + return row.tobytes() + def geterror(): return sys.exc_info()[1] -class AssertRaisesRegexMixin(object): - """Provides a way to prevent DeprecationWarnings in python >= 3.2. - - For this mixin to override correctly it needs to be before the - unittest.TestCase in the multiple inheritance hierarchy. - e.g. class TestClass(AssertRaisesRegexMixin, unittest.TestCase) - - This class/mixin and its usage can be removed when pygame no longer - supports python < 3.2. - """ - def assertRaisesRegex(self, *args, **kwargs): - try: - return super(AssertRaisesRegexMixin, self).assertRaisesRegex( - *args, **kwargs) - except AttributeError: - try: - return super(AssertRaisesRegexMixin, self).assertRaisesRegexp( - *args, **kwargs) - except AttributeError: - self.skipTest( - 'No assertRaisesRegex/assertRaisesRegexp method') - - -################################################################################ +############################################################################### this_dir = os.path.dirname(os.path.abspath(__file__)) trunk_dir = os.path.split(os.path.split(this_dir)[0])[0] if is_pygame_pkg: - test_module = 'tests' + test_module = "tests" else: - test_module = 'test' + test_module = "test" + def trunk_relative_path(relative): return os.path.normpath(os.path.join(trunk_dir, relative)) + def fixture_path(path): - return trunk_relative_path(os.path.join(test_module, 'fixtures', path)) + return trunk_relative_path(os.path.join(test_module, "fixtures", path)) + def example_path(path): - return trunk_relative_path(os.path.join('examples', path)) + return trunk_relative_path(os.path.join("examples", path)) -sys.path.insert(0, trunk_relative_path('.')) +sys.path.insert(0, trunk_relative_path(".")) + + +################################## TEMP FILES ################################# -################################## TEMP FILES ################################## def get_tmp_dir(): return tempfile.mkdtemp() -################################################################################ + +############################################################################### + def question(q): - return raw_input_('\n%s (y/n): ' % q.rstrip(' ')).lower().strip() == 'y' + return input(f"\n{q.rstrip(' ')} (y/n): ").lower().strip() == "y" + def prompt(p): - return raw_input_('\n%s (press enter to continue): ' % p.rstrip(' ')) + return input(f"\n{p.rstrip(' ')} (press enter to continue): ") + + +#################################### HELPERS ################################## -#################################### HELPERS ################################### def rgba_between(value, minimum=0, maximum=255): - if value < minimum: return minimum - elif value > maximum: return maximum - else: return value + if value < minimum: + return minimum + elif value > maximum: + return maximum + else: + return value + def combinations(seqs): """ - + Recipe 496807 from ActiveState Python CookBook - - Non recursive technique for getting all possible combinations of a sequence + + Non recursive technique for getting all possible combinations of a sequence of sequences. - + """ - r=[[]] + r = [[]] for x in seqs: - r = [ i + [y] for y in x for i in r ] + r = [i + [y] for y in x for i in r] return r + def gradient(width, height): """ Yields a pt and corresponding RGBA tuple, for every (width, height) combo. Useful for generating gradients. - + Actual gradient may be changed, no tests rely on specific values. - + Used in transform.rotate lossless tests to generate a fixture. """ - for l in xrange_(width): - for t in xrange_(height): - yield (l,t), tuple(map(rgba_between, (l, t, l, l+t))) + for l in range(width): + for t in range(height): + yield (l, t), tuple(map(rgba_between, (l, t, l, l + t))) def rect_area_pts(rect): - for l in xrange_(rect.left, rect.right): - for t in xrange_(rect.top, rect.bottom): + for l in range(rect.left, rect.right): + for t in range(rect.top, rect.bottom): yield l, t + def rect_perimeter_pts(rect): """ - + Returns pts ((L, T) tuples) encompassing the perimeter of a rect. - + The order is clockwise: topleft to topright topright to bottomright bottomright to bottomleft bottomleft to topleft - + Duplicate pts are not returned """ clock_wise_from_top_left = ( - [(l, rect.top) for l in xrange_(rect.left, rect.right) ], - [(rect.right -1, t) for t in xrange_(rect.top + 1, rect.bottom) ], - [(l, rect.bottom -1) for l in xrange_(rect.right -2, rect.left -1, -1)], - [(rect.left, t) for t in xrange_(rect.bottom -2, rect.top, -1)] + [(l, rect.top) for l in range(rect.left, rect.right)], + [(rect.right - 1, t) for t in range(rect.top + 1, rect.bottom)], + [(l, rect.bottom - 1) for l in range(rect.right - 2, rect.left - 1, -1)], + [(rect.left, t) for t in range(rect.bottom - 2, rect.top, -1)], ) - + for line in clock_wise_from_top_left: - for pt in line: yield pt - + yield from line + + def rect_outer_bounds(rect): """ - Returns topleft outerbound if possible and then the other pts, that are - "exclusive" bounds of the rect - - ?------O - |RECT| ?|0)uterbound - |----| - O O + Returns topleft outerbound if possible and then the other pts, that are + "exclusive" bounds of the rect + + ?------O + |RECT| ?|0)uterbound + |----| + O O """ - return ( - (rect.left is not 0 and [(rect.left-1, rect.top)] or []) + - [ rect.topright, - rect.bottomleft, - rect.bottomright] - ) + return ([(rect.left - 1, rect.top)] if rect.left else []) + [ + rect.topright, + rect.bottomleft, + rect.bottomright, + ] + def import_submodule(module): m = __import__(module) - for n in module.split('.')[1:]: + for n in module.split(".")[1:]: m = getattr(m, n) return m class SurfaceSubclass(pygame.Surface): """A subclassed Surface to test inheritance.""" + def __init__(self, *args, **kwargs): - super(SurfaceSubclass, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.test_attribute = True def test(): """ - + Lightweight test for helpers - + """ r = pygame.Rect(0, 0, 10, 10) - assert ( - rect_outer_bounds ( r ) == [(10, 0), # tr - ( 0, 10), # bl - (10, 10)] # br - ) - - assert len(list(rect_area_pts(r))) == 100 - - + assert rect_outer_bounds(r) == [(10, 0), (0, 10), (10, 10)] # tr # bl # br + + assert len(list(rect_area_pts(r))) == 100 + r = pygame.Rect(0, 0, 3, 3) assert list(rect_perimeter_pts(r)) == [ - (0, 0), (1, 0), (2, 0), # tl -> tr - (2, 1), (2, 2), # tr -> br - (1, 2), (0, 2), # br -> bl - (0, 1) # bl -> tl + (0, 0), + (1, 0), + (2, 0), # tl -> tr + (2, 1), + (2, 2), # tr -> br + (1, 2), + (0, 2), # br -> bl + (0, 1), # bl -> tl ] - - print ('Tests: OK') -################################################################################ + print("Tests: OK") diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py b/venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py index b5808f9355abbdf9310be9299fde0302dfcc2bac..626913c9ce032af74dc363ddac889f1f05cc18e6 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/arrinter.py @@ -3,22 +3,25 @@ import ctypes from ctypes import * import unittest -__all__ = ['PAI_CONTIGUOUS', 'PAI_FORTRAN', 'PAI_ALIGNED', - 'PAI_NOTSWAPPED', 'PAI_WRITEABLE', 'PAI_ARR_HAS_DESCR', - 'ArrayInterface',] - -try: - c_ssize_t # Undefined in early Python versions -except NameError: - if sizeof(c_uint) == sizeof(c_void_p): - c_size_t = c_uint - c_ssize_t = c_int - elif sizeof(c_ulong) == sizeof(c_void_p): - c_size_t = c_ulong - c_ssize_t = c_long - elif sizeof(c_ulonglong) == sizeof(c_void_p): - c_size_t = c_ulonglong - c_ssize_t = c_longlong +__all__ = [ + "PAI_CONTIGUOUS", + "PAI_FORTRAN", + "PAI_ALIGNED", + "PAI_NOTSWAPPED", + "PAI_WRITEABLE", + "PAI_ARR_HAS_DESCR", + "ArrayInterface", +] + +if sizeof(c_uint) == sizeof(c_void_p): + c_size_t = c_uint + c_ssize_t = c_int +elif sizeof(c_ulong) == sizeof(c_void_p): + c_size_t = c_ulong + c_ssize_t = c_long +elif sizeof(c_ulonglong) == sizeof(c_void_p): + c_size_t = c_ulonglong + c_ssize_t = c_longlong SIZEOF_VOID_P = sizeof(c_void_p) @@ -26,35 +29,49 @@ if SIZEOF_VOID_P <= sizeof(c_int): Py_intptr_t = c_int elif SIZEOF_VOID_P <= sizeof(c_long): Py_intptr_t = c_long -elif 'c_longlong' in globals() and SIZEOF_VOID_P <= sizeof(c_longlong): +elif "c_longlong" in globals() and SIZEOF_VOID_P <= sizeof(c_longlong): Py_intptr_t = c_longlong else: - raise RuntimeError("Unrecognized pointer size %i" % (pointer_size,)) + raise RuntimeError("Unrecognized pointer size %i" % (SIZEOF_VOID_P,)) + class PyArrayInterface(Structure): - _fields_ = [('two', c_int), ('nd', c_int), ('typekind', c_char), - ('itemsize', c_int), ('flags', c_int), - ('shape', POINTER(Py_intptr_t)), - ('strides', POINTER(Py_intptr_t)), - ('data', c_void_p), ('descr', py_object)] + _fields_ = [ + ("two", c_int), + ("nd", c_int), + ("typekind", c_char), + ("itemsize", c_int), + ("flags", c_int), + ("shape", POINTER(Py_intptr_t)), + ("strides", POINTER(Py_intptr_t)), + ("data", c_void_p), + ("descr", py_object), + ] + PAI_Ptr = POINTER(PyArrayInterface) + try: PyCObject_AsVoidPtr = pythonapi.PyCObject_AsVoidPtr except AttributeError: + def PyCObject_AsVoidPtr(o): raise TypeError("Not available") + else: PyCObject_AsVoidPtr.restype = c_void_p PyCObject_AsVoidPtr.argtypes = [py_object] PyCObject_GetDesc = pythonapi.PyCObject_GetDesc PyCObject_GetDesc.restype = c_void_p PyCObject_GetDesc.argtypes = [py_object] + try: PyCapsule_IsValid = pythonapi.PyCapsule_IsValid except AttributeError: + def PyCapsule_IsValid(capsule, name): return 0 + else: PyCapsule_IsValid.restype = c_int PyCapsule_IsValid.argtypes = [py_object, c_char_p] @@ -65,20 +82,15 @@ else: PyCapsule_GetContext.restype = c_void_p PyCapsule_GetContext.argtypes = [py_object] -if sys.version_info >= (3,): # Python3 - PyCapsule_Destructor = CFUNCTYPE(None, py_object) - PyCapsule_New = pythonapi.PyCapsule_New - PyCapsule_New.restype = py_object - PyCapsule_New.argtypes = [c_void_p, c_char_p, POINTER(PyCapsule_Destructor)] - def capsule_new(p): - return PyCapsule_New(addressof(p), None, None) -else: - PyCObject_Destructor = CFUNCTYPE(None, c_void_p) - PyCObject_FromVoidPtr = pythonapi.PyCObject_FromVoidPtr - PyCObject_FromVoidPtr.restype = py_object - PyCObject_FromVoidPtr.argtypes = [c_void_p, POINTER(PyCObject_Destructor)] - def capsule_new(p): - return PyCObject_FromVoidPtr(addressof(p), None) +PyCapsule_Destructor = CFUNCTYPE(None, py_object) +PyCapsule_New = pythonapi.PyCapsule_New +PyCapsule_New.restype = py_object +PyCapsule_New.argtypes = [c_void_p, c_char_p, POINTER(PyCapsule_Destructor)] + + +def capsule_new(p): + return PyCapsule_New(addressof(p), None, None) + PAI_CONTIGUOUS = 0x01 PAI_FORTRAN = 0x02 @@ -87,7 +99,8 @@ PAI_NOTSWAPPED = 0x200 PAI_WRITEABLE = 0x400 PAI_ARR_HAS_DESCR = 0x800 -class ArrayInterface(object): + +class ArrayInterface: def __init__(self, arr): try: self._cobj = arr.__array_struct__ @@ -108,8 +121,8 @@ class ArrayInterface(object): self._inter = cast(vp, PAI_Ptr)[0] def __getattr__(self, name): - if (name == 'typekind'): - return self._inter.typekind.decode('latin-1') + if name == "typekind": + return self._inter.typekind.decode("latin-1") return getattr(self._inter, name) def __str__(self): @@ -117,42 +130,55 @@ class ArrayInterface(object): ver = self.desc[0] else: ver = "N/A" - return ("nd: %i\n" - "typekind: %s\n" - "itemsize: %i\n" - "flags: %s\n" - "shape: %s\n" - "strides: %s\n" - "ver: %s\n" % - (self.nd, self.typekind, self.itemsize, - format_flags(self.flags), - format_shape(self.nd, self.shape), - format_strides(self.nd, self.strides), ver)) + return ( + "nd: %i\n" + "typekind: %s\n" + "itemsize: %i\n" + "flags: %s\n" + "shape: %s\n" + "strides: %s\n" + "ver: %s\n" + % ( + self.nd, + self.typekind, + self.itemsize, + format_flags(self.flags), + format_shape(self.nd, self.shape), + format_strides(self.nd, self.strides), + ver, + ) + ) + def format_flags(flags): names = [] - for flag, name in [(PAI_CONTIGUOUS, 'CONTIGUOUS'), - (PAI_FORTRAN, 'FORTRAN'), - (PAI_ALIGNED, 'ALIGNED'), - (PAI_NOTSWAPPED, 'NOTSWAPPED'), - (PAI_WRITEABLE, 'WRITEABLE'), - (PAI_ARR_HAS_DESCR, 'ARR_HAS_DESCR')]: + for flag, name in [ + (PAI_CONTIGUOUS, "CONTIGUOUS"), + (PAI_FORTRAN, "FORTRAN"), + (PAI_ALIGNED, "ALIGNED"), + (PAI_NOTSWAPPED, "NOTSWAPPED"), + (PAI_WRITEABLE, "WRITEABLE"), + (PAI_ARR_HAS_DESCR, "ARR_HAS_DESCR"), + ]: if flag & flags: names.append(name) - return ', '.join(names) + return ", ".join(names) + def format_shape(nd, shape): - return ', '.join([str(shape[i]) for i in range(nd)]) + return ", ".join([str(shape[i]) for i in range(nd)]) + def format_strides(nd, strides): - return ', '.join([str(strides[i]) for i in range(nd)]) + return ", ".join([str(strides[i]) for i in range(nd)]) + -class Exporter(object): - def __init__(self, shape, - typekind=None, itemsize=None, strides=None, - descr=None, flags=None): +class Exporter: + def __init__( + self, shape, typekind=None, itemsize=None, strides=None, descr=None, flags=None + ): if typekind is None: - typekind = 'u' + typekind = "u" if itemsize is None: itemsize = 1 if flags is None: @@ -180,17 +206,25 @@ class Exporter(object): else: raise ValueError("Mismatch in length of strides and shape") self.descr = descr - if self.is_contiguous('C'): + if self.is_contiguous("C"): flags |= PAI_CONTIGUOUS - if self.is_contiguous('F'): + if self.is_contiguous("F"): flags |= PAI_FORTRAN self.flags = flags sz = max(shape[i] * strides[i] for i in range(nd)) self._data = (c_ubyte * sz)() self.data = addressof(self._data) - self._inter = PyArrayInterface(2, nd, typekind.encode('latin_1'), - itemsize, flags, self._shape, - self._strides, self.data, descr) + self._inter = PyArrayInterface( + 2, + nd, + typekind.encode("latin_1"), + itemsize, + flags, + self._shape, + self._strides, + self.data, + descr, + ) self.len = itemsize for i in range(nd): self.len *= self.shape[i] @@ -214,18 +248,21 @@ class Exporter(object): return True return False + class Array(Exporter): - _ctypes = {('u', 1): c_uint8, - ('u', 2): c_uint16, - ('u', 4): c_uint32, - ('u', 8): c_uint64, - ('i', 1): c_int8, - ('i', 2): c_int16, - ('i', 4): c_int32, - ('i', 8): c_int64} + _ctypes = { + ("u", 1): c_uint8, + ("u", 2): c_uint16, + ("u", 4): c_uint32, + ("u", 8): c_uint64, + ("i", 1): c_int8, + ("i", 2): c_int16, + ("i", 4): c_int32, + ("i", 8): c_int64, + } def __init__(self, *args, **kwds): - super(Array, self).__init__(*args, **kwds) + super().__init__(*args, **kwds) try: if self.flags & PAI_NOTSWAPPED: ct = self._ctypes[self.typekind, self.itemsize] @@ -237,66 +274,70 @@ class Array(Exporter): ct = c_uint8 * self.itemsize self._ctype = ct self._ctype_p = POINTER(ct) + def __getitem__(self, key): return cast(self._addr_at(key), self._ctype_p)[0] + def __setitem__(self, key, value): cast(self._addr_at(key), self._ctype_p)[0] = value + def _addr_at(self, key): if not isinstance(key, tuple): - key = key, + key = (key,) if len(key) != self.nd: raise ValueError("wrong number of indexes") for i in range(self.nd): if not (0 <= key[i] < self.shape[i]): - raise IndexError("index {} out of range".format(i)) + raise IndexError(f"index {i} out of range") return self.data + sum(i * s for i, s in zip(key, self.strides)) + class ExporterTest(unittest.TestCase): def test_strides(self): - self.check_args(0, (10,), 'u', (2,), 20, 20, 2) - self.check_args(0, (5, 3), 'u', (6, 2), 30, 30, 2) - self.check_args(0, (7, 3, 5), 'u', (30, 10, 2), 210, 210, 2) - self.check_args(0, (13, 5, 11, 3), 'u', (330, 66, 6, 2), 4290, 4290, 2) - self.check_args(3, (7, 3, 5), 'i', (2, 14, 42), 210, 210, 2) - self.check_args(3, (7, 3, 5), 'x', (2, 16, 48), 210, 240, 2) - self.check_args(3, (13, 5, 11, 3), '%', (440, 88, 8, 2), 4290, 5720, 2) - self.check_args(3, (7, 5), '-', (15, 3), 105, 105, 3) - self.check_args(3, (7, 5), '*', (3, 21), 105, 105, 3) - self.check_args(3, (7, 5), ' ', (3, 24), 105, 120, 3) + self.check_args(0, (10,), "u", (2,), 20, 20, 2) + self.check_args(0, (5, 3), "u", (6, 2), 30, 30, 2) + self.check_args(0, (7, 3, 5), "u", (30, 10, 2), 210, 210, 2) + self.check_args(0, (13, 5, 11, 3), "u", (330, 66, 6, 2), 4290, 4290, 2) + self.check_args(3, (7, 3, 5), "i", (2, 14, 42), 210, 210, 2) + self.check_args(3, (7, 3, 5), "x", (2, 16, 48), 210, 240, 2) + self.check_args(3, (13, 5, 11, 3), "%", (440, 88, 8, 2), 4290, 5720, 2) + self.check_args(3, (7, 5), "-", (15, 3), 105, 105, 3) + self.check_args(3, (7, 5), "*", (3, 21), 105, 105, 3) + self.check_args(3, (7, 5), " ", (3, 24), 105, 120, 3) def test_is_contiguous(self): a = Exporter((10,), itemsize=2) - self.assertTrue(a.is_contiguous('C')) - self.assertTrue(a.is_contiguous('F')) - self.assertTrue(a.is_contiguous('A')) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) a = Exporter((10, 4), itemsize=2) - self.assertTrue(a.is_contiguous('C')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('F')) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) a = Exporter((13, 5, 11, 3), itemsize=2, strides=(330, 66, 6, 2)) - self.assertTrue(a.is_contiguous('C')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('F')) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) a = Exporter((10, 4), itemsize=2, strides=(2, 20)) - self.assertTrue(a.is_contiguous('F')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('C')) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) a = Exporter((13, 5, 11, 3), itemsize=2, strides=(2, 26, 130, 1430)) - self.assertTrue(a.is_contiguous('F')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('C')) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) a = Exporter((2, 11, 6, 4), itemsize=2, strides=(576, 48, 8, 2)) - self.assertFalse(a.is_contiguous('A')) + self.assertFalse(a.is_contiguous("A")) a = Exporter((2, 11, 6, 4), itemsize=2, strides=(2, 4, 48, 288)) - self.assertFalse(a.is_contiguous('A')) + self.assertFalse(a.is_contiguous("A")) a = Exporter((3, 2, 2), itemsize=2, strides=(16, 8, 4)) - self.assertFalse(a.is_contiguous('A')) + self.assertFalse(a.is_contiguous("A")) a = Exporter((3, 2, 2), itemsize=2, strides=(4, 12, 24)) - self.assertFalse(a.is_contiguous('A')) + self.assertFalse(a.is_contiguous("A")) - def check_args(self, call_flags, - shape, typekind, strides, length, bufsize, itemsize, - offset=0): + def check_args( + self, call_flags, shape, typekind, strides, length, bufsize, itemsize, offset=0 + ): if call_flags & 1: typekind_arg = typekind else: @@ -311,14 +352,14 @@ class ExporterTest(unittest.TestCase): m = ArrayInterface(a) self.assertEqual(m.data, a.data) self.assertEqual(m.itemsize, itemsize) - self.assertEqual(tuple(m.shape[0:m.nd]), shape) - self.assertEqual(tuple(m.strides[0:m.nd]), strides) + self.assertEqual(tuple(m.shape[0 : m.nd]), shape) + self.assertEqual(tuple(m.strides[0 : m.nd]), strides) -class ArrayTest(unittest.TestCase): +class ArrayTest(unittest.TestCase): def __init__(self, *args, **kwds): unittest.TestCase.__init__(self, *args, **kwds) - self.a = Array((20, 15), 'i', 4) + self.a = Array((20, 15), "i", 4) def setUp(self): # Every test starts with a zeroed array. @@ -348,51 +389,50 @@ class ArrayTest(unittest.TestCase): self.assertRaises(IndexError, a.__getitem__, (0, 15)) self.assertRaises(ValueError, a.__getitem__, 0) self.assertRaises(ValueError, a.__getitem__, (0, 0, 0)) - a = Array((3,), 'i', 4) + a = Array((3,), "i", 4) a[1] = 333 self.assertEqual(a[1], 333) def test_typekind(self): - a = Array((1,), 'i', 4) + a = Array((1,), "i", 4) self.assertTrue(a._ctype is c_int32) self.assertTrue(a._ctype_p is POINTER(c_int32)) - a = Array((1,), 'u', 4) + a = Array((1,), "u", 4) self.assertTrue(a._ctype is c_uint32) self.assertTrue(a._ctype_p is POINTER(c_uint32)) - a = Array((1,), 'f', 4) # float types unsupported: size system dependent + a = Array((1,), "f", 4) # float types unsupported: size system dependent ct = a._ctype self.assertTrue(issubclass(ct, ctypes.Array)) self.assertEqual(sizeof(ct), 4) def test_itemsize(self): for size in [1, 2, 4, 8]: - a = Array((1,), 'i', size) + a = Array((1,), "i", size) ct = a._ctype self.assertTrue(issubclass(ct, ctypes._SimpleCData)) self.assertEqual(sizeof(ct), size) def test_oddball_itemsize(self): for size in [3, 5, 6, 7, 9]: - a = Array((1,), 'i', size) + a = Array((1,), "i", size) ct = a._ctype self.assertTrue(issubclass(ct, ctypes.Array)) self.assertEqual(sizeof(ct), size) def test_byteswapped(self): - a = Array((1,), 'u', 4, flags=(PAI_ALIGNED | PAI_WRITEABLE)) + a = Array((1,), "u", 4, flags=(PAI_ALIGNED | PAI_WRITEABLE)) ct = a._ctype self.assertTrue(ct is not c_uint32) - if sys.byteorder == 'little': + if sys.byteorder == "little": self.assertTrue(ct is c_uint32.__ctype_be__) else: self.assertTrue(ct is c_uint32.__ctype_le__) - i = 0xa0b0c0d + i = 0xA0B0C0D n = c_uint32(i) a[0] = i self.assertEqual(a[0], i) - self.assertEqual(a._data[0:4], - cast(addressof(n), POINTER(c_uint8))[3:-1:-1]) + self.assertEqual(a._data[0:4], cast(addressof(n), POINTER(c_uint8))[3:-1:-1]) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py b/venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py index 53d64836c8aa3265cdf402851c9041f622a7247b..560d377b1287109aee4706497f4b5755891e48d3 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/async_sub.py @@ -16,64 +16,81 @@ import sys import unittest import tempfile -def geterror (): + +def geterror(): return sys.exc_info()[1] -if sys.version_info >= (3,): - null_byte = '\x00'.encode('ascii') -else: - null_byte = '\x00' - -if platform.system() == 'Windows': - if sys.version_info >= (3,): - # Test date should be in ascii. - def encode(s): - return s.encode('ascii') - - def decode(b): - return b.decode('ascii') - else: - # Strings only; do nothing - def encode(s): - return s - - def decode(b): - return b - + +null_byte = "\x00".encode("ascii") + +if platform.system() == "Windows": + + def encode(s): + return s.encode("ascii") + + def decode(b): + return b.decode("ascii") + try: import ctypes from ctypes.wintypes import DWORD + kernel32 = ctypes.windll.kernel32 TerminateProcess = ctypes.windll.kernel32.TerminateProcess - def WriteFile(handle, data, ol = None): + + def WriteFile(handle, data, ol=None): c_written = DWORD() - success = ctypes.windll.kernel32.WriteFile(handle, ctypes.create_string_buffer(encode(data)), len(data), ctypes.byref(c_written), ol) + success = ctypes.windll.kernel32.WriteFile( + handle, + ctypes.create_string_buffer(encode(data)), + len(data), + ctypes.byref(c_written), + ol, + ) return ctypes.windll.kernel32.GetLastError(), c_written.value - def ReadFile(handle, desired_bytes, ol = None): + + def ReadFile(handle, desired_bytes, ol=None): c_read = DWORD() - buffer = ctypes.create_string_buffer(desired_bytes+1) - success = ctypes.windll.kernel32.ReadFile(handle, buffer, desired_bytes, ctypes.byref(c_read), ol) + buffer = ctypes.create_string_buffer(desired_bytes + 1) + success = ctypes.windll.kernel32.ReadFile( + handle, buffer, desired_bytes, ctypes.byref(c_read), ol + ) buffer[c_read.value] = null_byte return ctypes.windll.kernel32.GetLastError(), decode(buffer.value) + def PeekNamedPipe(handle, desired_bytes): c_avail = DWORD() c_message = DWORD() if desired_bytes > 0: c_read = DWORD() - buffer = ctypes.create_string_buffer(desired_bytes+1) - success = ctypes.windll.kernel32.PeekNamedPipe(handle, buffer, desired_bytes, ctypes.byref(c_read), ctypes.byref(c_avail), ctypes.byref(c_message)) + buffer = ctypes.create_string_buffer(desired_bytes + 1) + success = ctypes.windll.kernel32.PeekNamedPipe( + handle, + buffer, + desired_bytes, + ctypes.byref(c_read), + ctypes.byref(c_avail), + ctypes.byref(c_message), + ) buffer[c_read.value] = null_byte return decode(buffer.value), c_avail.value, c_message.value else: - success = ctypes.windll.kernel32.PeekNamedPipe(handle, None, desired_bytes, None, ctypes.byref(c_avail), ctypes.byref(c_message)) + success = ctypes.windll.kernel32.PeekNamedPipe( + handle, + None, + desired_bytes, + None, + ctypes.byref(c_avail), + ctypes.byref(c_message), + ) return "", c_avail.value, c_message.value - + except ImportError: from win32file import ReadFile, WriteFile from win32pipe import PeekNamedPipe from win32api import TerminateProcess import msvcrt - + else: from signal import SIGINT, SIGTERM, SIGKILL import select @@ -85,22 +102,23 @@ PIPE = subprocess.PIPE ################################################################################ + class Popen(subprocess.Popen): def recv(self, maxsize=None): - return self._recv('stdout', maxsize) - + return self._recv("stdout", maxsize) + def recv_err(self, maxsize=None): - return self._recv('stderr', maxsize) + return self._recv("stderr", maxsize) - def send_recv(self, input='', maxsize=None): + def send_recv(self, input="", maxsize=None): return self.send(input), self.recv(maxsize), self.recv_err(maxsize) - - def read_async(self, wait=.1, e=1, tr=5, stderr=0): + + def read_async(self, wait=0.1, e=1, tr=5, stderr=0): if tr < 1: tr = 1 - x = time.time()+ wait + x = time.time() + wait y = [] - r = '' + r = "" pr = self.recv if stderr: pr = self.recv_err @@ -114,35 +132,36 @@ class Popen(subprocess.Popen): elif r: y.append(r) else: - time.sleep(max((x-time.time())/tr, 0)) - return ''.join(y) - + time.sleep(max((x - time.time()) / tr, 0)) + return "".join(y) + def send_all(self, data): while len(data): sent = self.send(data) if sent is None: raise Exception("Other end disconnected!") - data = buffer(data, sent) - + data = memoryview(data, sent) + def get_conn_maxsize(self, which, maxsize): if maxsize is None: maxsize = 1024 elif maxsize < 1: maxsize = 1 return getattr(self, which), maxsize - + def _close(self, which): getattr(self, which).close() setattr(self, which, None) - - if platform.system() == 'Windows': + + if platform.system() == "Windows": + def kill(self): # Recipes - #http://me.in-berlin.de/doc/python/faq/windows.html#how-do-i-emulate-os-kill-in-windows - #http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/347462 - + # http://me.in-berlin.de/doc/python/faq/windows.html#how-do-i-emulate-os-kill-in-windows + # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/347462 + """kill function for Win32""" - TerminateProcess(int(self._handle), 0) # returns None + TerminateProcess(int(self._handle), 0) # returns None def send(self, input): if not self.stdin: @@ -152,10 +171,10 @@ class Popen(subprocess.Popen): x = msvcrt.get_osfhandle(self.stdin.fileno()) (errCode, written) = WriteFile(x, input) except ValueError: - return self._close('stdin') + return self._close("stdin") except (subprocess.pywintypes.error, Exception): if geterror()[0] in (109, errno.ESHUTDOWN): - return self._close('stdin') + return self._close("stdin") raise return written @@ -164,7 +183,7 @@ class Popen(subprocess.Popen): conn, maxsize = self.get_conn_maxsize(which, maxsize) if conn is None: return None - + try: x = msvcrt.get_osfhandle(conn.fileno()) (read, nAvail, nMessage) = PeekNamedPipe(x, 0) @@ -178,7 +197,7 @@ class Popen(subprocess.Popen): if geterror()[0] in (109, errno.ESHUTDOWN): return self._close(which) raise - + if self.universal_newlines: # Translate newlines. For Python 3.x assume read is text. # If bytes then another solution is needed. @@ -186,14 +205,17 @@ class Popen(subprocess.Popen): return read else: + def kill(self): for i, sig in enumerate([SIGTERM, SIGKILL] * 2): - if i % 2 == 0: os.kill(self.pid, sig) - time.sleep((i * (i % 2) / 5.0) + 0.01) + if i % 2 == 0: + os.kill(self.pid, sig) + time.sleep((i * (i % 2) / 5.0) + 0.01) killed_pid, stat = os.waitpid(self.pid, os.WNOHANG) - if killed_pid != 0: return - + if killed_pid != 0: + return + def send(self, input): if not self.stdin: return None @@ -204,8 +226,8 @@ class Popen(subprocess.Popen): try: written = os.write(self.stdin.fileno(), input) except OSError: - if geterror()[0] == errno.EPIPE: #broken pipe - return self._close('stdin') + if geterror()[0] == errno.EPIPE: # broken pipe + return self._close("stdin") raise return written @@ -214,10 +236,10 @@ class Popen(subprocess.Popen): conn, maxsize = self.get_conn_maxsize(which, maxsize) if conn is None: return None - + if not select.select([conn], [], [], 0)[0]: - return '' - + return "" + r = conn.read(maxsize) if not r: return self._close(which) @@ -229,44 +251,51 @@ class Popen(subprocess.Popen): ################################################################################ -def proc_in_time_or_kill(cmd, time_out, wd = None, env = None): - proc = Popen ( - cmd, cwd = wd, env = env, - stdin = subprocess.PIPE, stdout = subprocess.PIPE, - stderr = subprocess.STDOUT, universal_newlines = 1 + +def proc_in_time_or_kill(cmd, time_out, wd=None, env=None): + proc = Popen( + cmd, + cwd=wd, + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=1, ) ret_code = None response = [] t = time.time() - while ret_code is None and ((time.time() -t) < time_out): + while ret_code is None and ((time.time() - t) < time_out): ret_code = proc.poll() response += [proc.read_async(wait=0.1, e=0)] if ret_code is None: - ret_code = '"Process timed out (time_out = %s secs) ' % time_out + ret_code = f'"Process timed out (time_out = {time_out} secs) ' try: proc.kill() ret_code += 'and was successfully terminated"' except Exception: - ret_code += ('and termination failed (exception: %s)"' % - (geterror(),)) + ret_code += f'and termination failed (exception: {geterror()})"' + + return ret_code, "".join(response) - return ret_code, ''.join(response) ################################################################################ + class AsyncTest(unittest.TestCase): def test_proc_in_time_or_kill(self): ret_code, response = proc_in_time_or_kill( - [sys.executable, '-c', 'while 1: pass'], time_out = 1 + [sys.executable, "-c", "while True: pass"], time_out=1 ) - self.assertIn('rocess timed out', ret_code) - self.assertIn('successfully terminated', ret_code) + self.assertIn("rocess timed out", ret_code) + self.assertIn("successfully terminated", ret_code) + ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/buftools.py b/venv/Lib/site-packages/pygame/tests/test_utils/buftools.py index 1e2ab937b66dc109b88acd9db566b96dce5b17d1..19ae89057de32235bcce58c6cba8fe8ce3e0b7b3 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/buftools.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/buftools.py @@ -20,25 +20,35 @@ python -m pygame.tests.test_utils.array """ import pygame + if not pygame.HAVE_NEWBUF: emsg = "This Pygame build does not support the new buffer protocol" raise ImportError(emsg) import pygame.newbuffer -from pygame.newbuffer import (PyBUF_SIMPLE, PyBUF_FORMAT, PyBUF_ND, - PyBUF_WRITABLE, PyBUF_STRIDES, PyBUF_C_CONTIGUOUS, - PyBUF_F_CONTIGUOUS, PyBUF_ANY_CONTIGUOUS, - PyBUF_INDIRECT, PyBUF_STRIDED, PyBUF_STRIDED_RO, - PyBUF_RECORDS, PyBUF_RECORDS_RO, PyBUF_FULL, - PyBUF_FULL_RO, PyBUF_CONTIG, PyBUF_CONTIG_RO) +from pygame.newbuffer import ( + PyBUF_SIMPLE, + PyBUF_FORMAT, + PyBUF_ND, + PyBUF_WRITABLE, + PyBUF_STRIDES, + PyBUF_C_CONTIGUOUS, + PyBUF_F_CONTIGUOUS, + PyBUF_ANY_CONTIGUOUS, + PyBUF_INDIRECT, + PyBUF_STRIDED, + PyBUF_STRIDED_RO, + PyBUF_RECORDS, + PyBUF_RECORDS_RO, + PyBUF_FULL, + PyBUF_FULL_RO, + PyBUF_CONTIG, + PyBUF_CONTIG_RO, +) import unittest -import sys import ctypes import operator -try: - reduce -except NameError: - from functools import reduce +from functools import reduce __all__ = ["Exporter", "Importer"] @@ -55,55 +65,80 @@ except AttributeError: elif ctypes.sizeof(ctypes.c_longlong) == void_p_sz: ctypes.c_ssize_t = ctypes.c_longlong else: - raise RuntimeError("Cannot set c_ssize_t: sizeof(void *) is %i" % - void_p_sz) + raise RuntimeError("Cannot set c_ssize_t: sizeof(void *) is %i" % void_p_sz) + def _prop_get(fn): return property(fn) + class Exporter(pygame.newbuffer.BufferMixin): """An object that exports a multi-dimension new buffer interface - The only array operation this type supports is to export a buffer. + The only array operation this type supports is to export a buffer. """ - prefixes = {'@': '', '=': '=', '<': '=', '>': '=', '!': '=', - '2': '2', '3': '3', '4': '4', '5': '5', - '6': '6', '7': '7', '8': '8', '9': '9'} - types = {'c': ctypes.c_char, 'b': ctypes.c_byte, 'B': ctypes.c_ubyte, - '=c': ctypes.c_int8, '=b': ctypes.c_int8, '=B': ctypes.c_uint8, - '?': ctypes.c_bool, '=?': ctypes.c_int8, - 'h': ctypes.c_short, 'H': ctypes.c_ushort, - '=h': ctypes.c_int16, '=H': ctypes.c_uint16, - 'i': ctypes.c_int, 'I': ctypes.c_uint, - '=i': ctypes.c_int32, '=I': ctypes.c_uint32, - 'l': ctypes.c_long, 'L': ctypes.c_ulong, - '=l': ctypes.c_int32, '=L': ctypes.c_uint32, - 'q': ctypes.c_longlong, 'Q': ctypes.c_ulonglong, - '=q': ctypes.c_int64, '=Q': ctypes.c_uint64, - 'f': ctypes.c_float, 'd': ctypes.c_double, - 'P': ctypes.c_void_p, - 'x': ctypes.c_ubyte * 1, - '2x': ctypes.c_ubyte * 2, - '3x': ctypes.c_ubyte * 3, - '4x': ctypes.c_ubyte * 4, - '5x': ctypes.c_ubyte * 5, - '6x': ctypes.c_ubyte * 6, - '7x': ctypes.c_ubyte * 7, - '8x': ctypes.c_ubyte * 8, - '9x': ctypes.c_ubyte * 9} - - def __init__(self, - shape, - format=None, - strides=None, - readonly=None, - itemsize=None): + + prefixes = { + "@": "", + "=": "=", + "<": "=", + ">": "=", + "!": "=", + "2": "2", + "3": "3", + "4": "4", + "5": "5", + "6": "6", + "7": "7", + "8": "8", + "9": "9", + } + types = { + "c": ctypes.c_char, + "b": ctypes.c_byte, + "B": ctypes.c_ubyte, + "=c": ctypes.c_int8, + "=b": ctypes.c_int8, + "=B": ctypes.c_uint8, + "?": ctypes.c_bool, + "=?": ctypes.c_int8, + "h": ctypes.c_short, + "H": ctypes.c_ushort, + "=h": ctypes.c_int16, + "=H": ctypes.c_uint16, + "i": ctypes.c_int, + "I": ctypes.c_uint, + "=i": ctypes.c_int32, + "=I": ctypes.c_uint32, + "l": ctypes.c_long, + "L": ctypes.c_ulong, + "=l": ctypes.c_int32, + "=L": ctypes.c_uint32, + "q": ctypes.c_longlong, + "Q": ctypes.c_ulonglong, + "=q": ctypes.c_int64, + "=Q": ctypes.c_uint64, + "f": ctypes.c_float, + "d": ctypes.c_double, + "P": ctypes.c_void_p, + "x": ctypes.c_ubyte * 1, + "2x": ctypes.c_ubyte * 2, + "3x": ctypes.c_ubyte * 3, + "4x": ctypes.c_ubyte * 4, + "5x": ctypes.c_ubyte * 5, + "6x": ctypes.c_ubyte * 6, + "7x": ctypes.c_ubyte * 7, + "8x": ctypes.c_ubyte * 8, + "9x": ctypes.c_ubyte * 9, + } + + def __init__(self, shape, format=None, strides=None, readonly=None, itemsize=None): if format is None: - format = 'B' + format = "B" if readonly is None: readonly = False - prefix = '' - typecode = '' + prefix = "" + typecode = "" i = 0 if i < len(format): try: @@ -111,7 +146,7 @@ class Exporter(pygame.newbuffer.BufferMixin): i += 1 except LookupError: pass - if i < len(format) and format[i] == '1': + if i < len(format) and format[i] == "1": i += 1 if i == len(format) - 1: typecode = format[i] @@ -122,7 +157,7 @@ class Exporter(pygame.newbuffer.BufferMixin): raise ValueError("Unknown item format '" + format + "'") self.readonly = bool(readonly) self.format = format - self._format = ctypes.create_string_buffer(format.encode('latin_1')) + self._format = ctypes.create_string_buffer(format.encode("latin_1")) self.ndim = len(shape) self.itemsize = itemsize self.len = reduce(operator.mul, shape, 1) * self.itemsize @@ -139,34 +174,39 @@ class Exporter(pygame.newbuffer.BufferMixin): self._strides = (ctypes.c_ssize_t * self.ndim)(*self.strides) else: raise ValueError("Mismatch in length of strides and shape") - buflen = max(d * abs(s) for d, s in zip(self.shape, self.strides)) + buflen = max(d * abs(s) for d, s in zip(self.shape, self.strides)) self.buflen = buflen self._buf = (ctypes.c_ubyte * buflen)() - offset = sum((d - 1) * abs(s) - for d, s in zip(self.shape, self.strides) if s < 0) + offset = sum( + (d - 1) * abs(s) for d, s in zip(self.shape, self.strides) if s < 0 + ) self.buf = ctypes.addressof(self._buf) + offset def buffer_info(self): - return (addressof(self.buffer), self.shape[0]) + return (ctypes.addressof(self.buffer), self.shape[0]) def tobytes(self): - return cast(self.buffer, POINTER(c_char))[0:self._len] + return ctypes.cast(self.buffer, ctypes.POINTER(ctypes.c_char))[0 : self._len] def __len__(self): return self.shape[0] def _get_buffer(self, view, flags): from ctypes import addressof + if (flags & PyBUF_WRITABLE) == PyBUF_WRITABLE and self.readonly: raise BufferError("buffer is read-only") - if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS and - not self.is_contiguous('C')): + if ( + flags & PyBUF_C_CONTIGUOUS + ) == PyBUF_C_CONTIGUOUS and not self.is_contiguous("C"): raise BufferError("data is not C contiguous") - if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS and - not self.is_contiguous('F')): + if ( + flags & PyBUF_F_CONTIGUOUS + ) == PyBUF_F_CONTIGUOUS and not self.is_contiguous("F"): raise BufferError("data is not F contiguous") - if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS and - not self.is_contiguous('A')): + if ( + flags & PyBUF_ANY_CONTIGUOUS + ) == PyBUF_ANY_CONTIGUOUS and not self.is_contiguous("A"): raise BufferError("data is not contiguous") view.buf = self.buf view.readonly = self.readonly @@ -182,14 +222,13 @@ class Exporter(pygame.newbuffer.BufferMixin): view.format = None if (flags & PyBUF_ND) == PyBUF_ND: view.shape = addressof(self._shape) - elif self.is_contiguous('C'): + elif self.is_contiguous("C"): view.shape = None else: - raise BufferError( - "shape required for {} dimensional data".format(self.ndim)) + raise BufferError(f"shape required for {self.ndim} dimensional data") if (flags & PyBUF_STRIDES) == PyBUF_STRIDES: view.strides = ctypes.addressof(self._strides) - elif view.shape is None or self.is_contiguous('C'): + elif view.shape is None or self.is_contiguous("C"): view.strides = None else: raise BufferError("strides required for none C contiguous data") @@ -215,58 +254,70 @@ class Exporter(pygame.newbuffer.BufferMixin): return False -class Importer(object): +class Importer: """An object that imports a new buffer interface - The fields of the Py_buffer C struct are exposed by identically - named Importer read-only properties. + The fields of the Py_buffer C struct are exposed by identically + named Importer read-only properties. """ + def __init__(self, obj, flags): self._view = pygame.newbuffer.Py_buffer() self._view.get_buffer(obj, flags) + @property def obj(self): """return object or None for NULL field""" return self._view.obj + @property def buf(self): """return int or None for NULL field""" return self._view.buf + @property def len(self): """return int""" return self._view.len + @property def readonly(self): """return bool""" return self._view.readonly + @property def format(self): """return bytes or None for NULL field""" format_addr = self._view.format if format_addr is None: return None - return ctypes.cast(format_addr, ctypes.c_char_p).value.decode('ascii') + return ctypes.cast(format_addr, ctypes.c_char_p).value.decode("ascii") + @property def itemsize(self): """return int""" return self._view.itemsize + @property def ndim(self): """return int""" return self._view.ndim + @property def shape(self): """return int tuple or None for NULL field""" return self._to_ssize_tuple(self._view.shape) + @property def strides(self): """return int tuple or None for NULL field""" return self._to_ssize_tuple(self._view.strides) + @property def suboffsets(self): """return int tuple or None for NULL field""" return self._to_ssize_tuple(self._view.suboffsets) + @property def internal(self): """return int or None for NULL field""" @@ -277,11 +328,12 @@ class Importer(object): if addr is None: return None - return tuple(cast(addr, POINTER(c_ssize_t))[0:self._view.ndim]) + return tuple(cast(addr, POINTER(c_ssize_t))[0 : self._view.ndim]) class ExporterTest(unittest.TestCase): """Class Exporter unit tests""" + def test_formats(self): char_sz = ctypes.sizeof(ctypes.c_char) short_sz = ctypes.sizeof(ctypes.c_short) @@ -293,142 +345,141 @@ class ExporterTest(unittest.TestCase): voidp_sz = ctypes.sizeof(ctypes.c_void_p) bool_sz = ctypes.sizeof(ctypes.c_bool) - self.check_args(0, (1,), 'B', (1,), 1, 1, 1) - self.check_args(1, (1,), 'b', (1,), 1, 1, 1) - self.check_args(1, (1,), 'B', (1,), 1, 1, 1) - self.check_args(1, (1,), 'c', (char_sz,), char_sz, char_sz, char_sz) - self.check_args(1, (1,), 'h', (short_sz,), short_sz, short_sz, short_sz) - self.check_args(1, (1,), 'H', (short_sz,), short_sz, short_sz, short_sz) - self.check_args(1, (1,), 'i', (int_sz,), int_sz, int_sz, int_sz) - self.check_args(1, (1,), 'I', (int_sz,), int_sz, int_sz, int_sz) - self.check_args(1, (1,), 'l', (long_sz,), long_sz, long_sz, long_sz) - self.check_args(1, (1,), 'L', (long_sz,), long_sz, long_sz, long_sz) - self.check_args(1, (1,), 'q', (longlong_sz,), - longlong_sz, longlong_sz, longlong_sz) - self.check_args(1, (1,), 'Q', (longlong_sz,), - longlong_sz, longlong_sz, longlong_sz) - self.check_args(1, (1,), 'f', (float_sz,), float_sz, float_sz, float_sz) - self.check_args(1, (1,), 'd', (double_sz,), - double_sz, double_sz, double_sz) - self.check_args(1, (1,), 'x', (1,), 1, 1, 1) - self.check_args(1, (1,), 'P', (voidp_sz,), voidp_sz, voidp_sz, voidp_sz) - self.check_args(1, (1,), '?', (bool_sz,), bool_sz, bool_sz, bool_sz) - self.check_args(1, (1,), '@b', (1,), 1, 1, 1) - self.check_args(1, (1,), '@B', (1,), 1, 1, 1) - self.check_args(1, (1,), '@c', (char_sz,), char_sz, char_sz, char_sz) - self.check_args(1, (1,), '@h', (short_sz,), - short_sz, short_sz, short_sz) - self.check_args(1, (1,), '@H', (short_sz,), - short_sz, short_sz, short_sz) - self.check_args(1, (1,), '@i', (int_sz,), int_sz, int_sz, int_sz) - self.check_args(1, (1,), '@I', (int_sz,), int_sz, int_sz, int_sz) - self.check_args(1, (1,), '@l', (long_sz,), long_sz, long_sz, long_sz) - self.check_args(1, (1,), '@L', (long_sz,), long_sz, long_sz, long_sz) - self.check_args(1, (1,), '@q', - (longlong_sz,), longlong_sz, longlong_sz, longlong_sz) - self.check_args(1, (1,), '@Q', (longlong_sz,), - longlong_sz, longlong_sz, longlong_sz) - self.check_args(1, (1,), '@f', (float_sz,), - float_sz, float_sz, float_sz) - self.check_args(1, (1,), '@d', (double_sz,), - double_sz, double_sz, double_sz) - self.check_args(1, (1,), '@?', (bool_sz,), bool_sz, bool_sz, bool_sz) - self.check_args(1, (1,), '=b', (1,), 1, 1, 1) - self.check_args(1, (1,), '=B', (1,), 1, 1, 1) - self.check_args(1, (1,), '=c', (1,), 1, 1, 1) - self.check_args(1, (1,), '=h', (2,), 2, 2, 2) - self.check_args(1, (1,), '=H', (2,), 2, 2, 2) - self.check_args(1, (1,), '=i', (4,), 4, 4, 4) - self.check_args(1, (1,), '=I', (4,), 4, 4, 4) - self.check_args(1, (1,), '=l', (4,), 4, 4, 4) - self.check_args(1, (1,), '=L', (4,), 4, 4, 4) - self.check_args(1, (1,), '=q', (8,), 8, 8, 8) - self.check_args(1, (1,), '=Q', (8,), 8, 8, 8) - self.check_args(1, (1,), '=?', (1,), 1, 1, 1) - self.check_args(1, (1,), 'h', (2,), 2, 2, 2) - self.check_args(1, (1,), '!h', (2,), 2, 2, 2) - self.check_args(1, (1,), 'q', (8,), 8, 8, 8) - self.check_args(1, (1,), '!q', (8,), 8, 8, 8) - self.check_args(1, (1,), '1x', (1,), 1, 1, 1) - self.check_args(1, (1,), '2x', (2,), 2, 2, 2) - self.check_args(1, (1,), '3x', (3,), 3, 3, 3) - self.check_args(1, (1,), '4x', (4,), 4, 4, 4) - self.check_args(1, (1,), '5x', (5,), 5, 5, 5) - self.check_args(1, (1,), '6x', (6,), 6, 6, 6) - self.check_args(1, (1,), '7x', (7,), 7, 7, 7) - self.check_args(1, (1,), '8x', (8,), 8, 8, 8) - self.check_args(1, (1,), '9x', (9,), 9, 9, 9) - self.check_args(1, (1,), '1h', (2,), 2, 2, 2) - self.check_args(1, (1,), '=1h', (2,), 2, 2, 2) - self.assertRaises(ValueError, Exporter, (2, 1), '') - self.assertRaises(ValueError, Exporter, (2, 1), 'W') - self.assertRaises(ValueError, Exporter, (2, 1), '^Q') - self.assertRaises(ValueError, Exporter, (2, 1), '=W') - self.assertRaises(ValueError, Exporter, (2, 1), '=f') - self.assertRaises(ValueError, Exporter, (2, 1), '=d') - self.assertRaises(ValueError, Exporter, (2, 1), 'f') - self.assertRaises(ValueError, Exporter, (2, 1), '>d') - self.assertRaises(ValueError, Exporter, (2, 1), '!f') - self.assertRaises(ValueError, Exporter, (2, 1), '!d') - self.assertRaises(ValueError, Exporter, (2, 1), '0x') - self.assertRaises(ValueError, Exporter, (2, 1), '11x') - self.assertRaises(ValueError, Exporter, (2, 1), 'BB') + self.check_args(0, (1,), "B", (1,), 1, 1, 1) + self.check_args(1, (1,), "b", (1,), 1, 1, 1) + self.check_args(1, (1,), "B", (1,), 1, 1, 1) + self.check_args(1, (1,), "c", (char_sz,), char_sz, char_sz, char_sz) + self.check_args(1, (1,), "h", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "H", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "i", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "I", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "l", (long_sz,), long_sz, long_sz, long_sz) + self.check_args(1, (1,), "L", (long_sz,), long_sz, long_sz, long_sz) + self.check_args( + 1, (1,), "q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args( + 1, (1,), "Q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args(1, (1,), "f", (float_sz,), float_sz, float_sz, float_sz) + self.check_args(1, (1,), "d", (double_sz,), double_sz, double_sz, double_sz) + self.check_args(1, (1,), "x", (1,), 1, 1, 1) + self.check_args(1, (1,), "P", (voidp_sz,), voidp_sz, voidp_sz, voidp_sz) + self.check_args(1, (1,), "?", (bool_sz,), bool_sz, bool_sz, bool_sz) + self.check_args(1, (1,), "@b", (1,), 1, 1, 1) + self.check_args(1, (1,), "@B", (1,), 1, 1, 1) + self.check_args(1, (1,), "@c", (char_sz,), char_sz, char_sz, char_sz) + self.check_args(1, (1,), "@h", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "@H", (short_sz,), short_sz, short_sz, short_sz) + self.check_args(1, (1,), "@i", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "@I", (int_sz,), int_sz, int_sz, int_sz) + self.check_args(1, (1,), "@l", (long_sz,), long_sz, long_sz, long_sz) + self.check_args(1, (1,), "@L", (long_sz,), long_sz, long_sz, long_sz) + self.check_args( + 1, (1,), "@q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args( + 1, (1,), "@Q", (longlong_sz,), longlong_sz, longlong_sz, longlong_sz + ) + self.check_args(1, (1,), "@f", (float_sz,), float_sz, float_sz, float_sz) + self.check_args(1, (1,), "@d", (double_sz,), double_sz, double_sz, double_sz) + self.check_args(1, (1,), "@?", (bool_sz,), bool_sz, bool_sz, bool_sz) + self.check_args(1, (1,), "=b", (1,), 1, 1, 1) + self.check_args(1, (1,), "=B", (1,), 1, 1, 1) + self.check_args(1, (1,), "=c", (1,), 1, 1, 1) + self.check_args(1, (1,), "=h", (2,), 2, 2, 2) + self.check_args(1, (1,), "=H", (2,), 2, 2, 2) + self.check_args(1, (1,), "=i", (4,), 4, 4, 4) + self.check_args(1, (1,), "=I", (4,), 4, 4, 4) + self.check_args(1, (1,), "=l", (4,), 4, 4, 4) + self.check_args(1, (1,), "=L", (4,), 4, 4, 4) + self.check_args(1, (1,), "=q", (8,), 8, 8, 8) + self.check_args(1, (1,), "=Q", (8,), 8, 8, 8) + self.check_args(1, (1,), "=?", (1,), 1, 1, 1) + self.check_args(1, (1,), "h", (2,), 2, 2, 2) + self.check_args(1, (1,), "!h", (2,), 2, 2, 2) + self.check_args(1, (1,), "q", (8,), 8, 8, 8) + self.check_args(1, (1,), "!q", (8,), 8, 8, 8) + self.check_args(1, (1,), "1x", (1,), 1, 1, 1) + self.check_args(1, (1,), "2x", (2,), 2, 2, 2) + self.check_args(1, (1,), "3x", (3,), 3, 3, 3) + self.check_args(1, (1,), "4x", (4,), 4, 4, 4) + self.check_args(1, (1,), "5x", (5,), 5, 5, 5) + self.check_args(1, (1,), "6x", (6,), 6, 6, 6) + self.check_args(1, (1,), "7x", (7,), 7, 7, 7) + self.check_args(1, (1,), "8x", (8,), 8, 8, 8) + self.check_args(1, (1,), "9x", (9,), 9, 9, 9) + self.check_args(1, (1,), "1h", (2,), 2, 2, 2) + self.check_args(1, (1,), "=1h", (2,), 2, 2, 2) + self.assertRaises(ValueError, Exporter, (2, 1), "") + self.assertRaises(ValueError, Exporter, (2, 1), "W") + self.assertRaises(ValueError, Exporter, (2, 1), "^Q") + self.assertRaises(ValueError, Exporter, (2, 1), "=W") + self.assertRaises(ValueError, Exporter, (2, 1), "=f") + self.assertRaises(ValueError, Exporter, (2, 1), "=d") + self.assertRaises(ValueError, Exporter, (2, 1), "f") + self.assertRaises(ValueError, Exporter, (2, 1), ">d") + self.assertRaises(ValueError, Exporter, (2, 1), "!f") + self.assertRaises(ValueError, Exporter, (2, 1), "!d") + self.assertRaises(ValueError, Exporter, (2, 1), "0x") + self.assertRaises(ValueError, Exporter, (2, 1), "11x") + self.assertRaises(ValueError, Exporter, (2, 1), "BB") def test_strides(self): - self.check_args(1, (10,), '=h', (2,), 20, 20, 2) - self.check_args(1, (5, 3), '=h', (6, 2), 30, 30, 2) - self.check_args(1, (7, 3, 5), '=h', (30, 10, 2), 210, 210, 2) - self.check_args(1, (13, 5, 11, 3), '=h', (330, 66, 6, 2), 4290, 4290, 2) - self.check_args(3, (7, 3, 5), '=h', (2, 14, 42), 210, 210, 2) - self.check_args(3, (7, 3, 5), '=h', (2, 16, 48), 210, 240, 2) - self.check_args(3, (13, 5, 11, 3), '=h', (440, 88, 8, 2), 4290, 5720, 2) - self.check_args(3, (7, 5), '3x', (15, 3), 105, 105, 3) - self.check_args(3, (7, 5), '3x', (3, 21), 105, 105, 3) - self.check_args(3, (7, 5), '3x', (3, 24), 105, 120, 3) + self.check_args(1, (10,), "=h", (2,), 20, 20, 2) + self.check_args(1, (5, 3), "=h", (6, 2), 30, 30, 2) + self.check_args(1, (7, 3, 5), "=h", (30, 10, 2), 210, 210, 2) + self.check_args(1, (13, 5, 11, 3), "=h", (330, 66, 6, 2), 4290, 4290, 2) + self.check_args(3, (7, 3, 5), "=h", (2, 14, 42), 210, 210, 2) + self.check_args(3, (7, 3, 5), "=h", (2, 16, 48), 210, 240, 2) + self.check_args(3, (13, 5, 11, 3), "=h", (440, 88, 8, 2), 4290, 5720, 2) + self.check_args(3, (7, 5), "3x", (15, 3), 105, 105, 3) + self.check_args(3, (7, 5), "3x", (3, 21), 105, 105, 3) + self.check_args(3, (7, 5), "3x", (3, 24), 105, 120, 3) def test_readonly(self): - a = Exporter((2,), 'h', readonly=True) + a = Exporter((2,), "h", readonly=True) self.assertTrue(a.readonly) b = Importer(a, PyBUF_STRIDED_RO) self.assertRaises(BufferError, Importer, a, PyBUF_STRIDED) b = Importer(a, PyBUF_STRIDED_RO) def test_is_contiguous(self): - a = Exporter((10,), '=h') - self.assertTrue(a.is_contiguous('C')) - self.assertTrue(a.is_contiguous('F')) - self.assertTrue(a.is_contiguous('A')) - a = Exporter((10, 4), '=h') - self.assertTrue(a.is_contiguous('C')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('F')) - a = Exporter((13, 5, 11, 3), '=h', (330, 66, 6, 2)) - self.assertTrue(a.is_contiguous('C')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('F')) - a = Exporter((10, 4), '=h', (2, 20)) - self.assertTrue(a.is_contiguous('F')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('C')) - a = Exporter((13, 5, 11, 3), '=h', (2, 26, 130, 1430)) - self.assertTrue(a.is_contiguous('F')) - self.assertTrue(a.is_contiguous('A')) - self.assertFalse(a.is_contiguous('C')) - a = Exporter((2, 11, 6, 4), '=h', (576, 48, 8, 2)) - self.assertFalse(a.is_contiguous('A')) - a = Exporter((2, 11, 6, 4), '=h', (2, 4, 48, 288)) - self.assertFalse(a.is_contiguous('A')) - a = Exporter((3, 2, 2), '=h', (16, 8, 4)) - self.assertFalse(a.is_contiguous('A')) - a = Exporter((3, 2, 2), '=h', (4, 12, 24)) - self.assertFalse(a.is_contiguous('A')) + a = Exporter((10,), "=h") + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + a = Exporter((10, 4), "=h") + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((13, 5, 11, 3), "=h", (330, 66, 6, 2)) + self.assertTrue(a.is_contiguous("C")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("F")) + a = Exporter((10, 4), "=h", (2, 20)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((13, 5, 11, 3), "=h", (2, 26, 130, 1430)) + self.assertTrue(a.is_contiguous("F")) + self.assertTrue(a.is_contiguous("A")) + self.assertFalse(a.is_contiguous("C")) + a = Exporter((2, 11, 6, 4), "=h", (576, 48, 8, 2)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((2, 11, 6, 4), "=h", (2, 4, 48, 288)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), "=h", (16, 8, 4)) + self.assertFalse(a.is_contiguous("A")) + a = Exporter((3, 2, 2), "=h", (4, 12, 24)) + self.assertFalse(a.is_contiguous("A")) def test_PyBUF_flags(self): - a = Exporter((10, 2), 'd') + a = Exporter((10, 2), "d") b = Importer(a, PyBUF_SIMPLE) self.assertTrue(b.obj is a) self.assertTrue(b.format is None) @@ -459,7 +510,7 @@ class ExporterTest(unittest.TestCase): self.assertTrue(b.suboffsets is None) self.assertTrue(b.internal is None) self.assertFalse(b.readonly) - a = Exporter((5, 10), '=h', (24, 2)) + a = Exporter((5, 10), "=h", (24, 2)) b = Importer(a, PyBUF_STRIDES) self.assertTrue(b.obj is a) self.assertTrue(b.format is None) @@ -472,7 +523,7 @@ class ExporterTest(unittest.TestCase): self.assertFalse(b.readonly) b = Importer(a, PyBUF_FULL) self.assertTrue(b.obj is a) - self.assertEqual(b.format, '=h') + self.assertEqual(b.format, "=h") self.assertEqual(b.len, a.len) self.assertEqual(b.itemsize, a.itemsize) self.assertEqual(b.shape, a.shape) @@ -489,19 +540,19 @@ class ExporterTest(unittest.TestCase): self.assertRaises(BufferError, Importer, a, PyBUF_CONTIG) def test_negative_strides(self): - self.check_args(3, (3, 5, 4), 'B', (20, 4, -1), 60, 60, 1, 3) - self.check_args(3, (3, 5, 3), 'B', (20, 4, -1), 45, 60, 1, 2) - self.check_args(3, (3, 5, 4), 'B', (20, -4, 1), 60, 60, 1, 16) - self.check_args(3, (3, 5, 4), 'B', (-20, -4, -1), 60, 60, 1, 59) - self.check_args(3, (3, 5, 3), 'B', (-20, -4, -1), 45, 60, 1, 58) + self.check_args(3, (3, 5, 4), "B", (20, 4, -1), 60, 60, 1, 3) + self.check_args(3, (3, 5, 3), "B", (20, 4, -1), 45, 60, 1, 2) + self.check_args(3, (3, 5, 4), "B", (20, -4, 1), 60, 60, 1, 16) + self.check_args(3, (3, 5, 4), "B", (-20, -4, -1), 60, 60, 1, 59) + self.check_args(3, (3, 5, 3), "B", (-20, -4, -1), 45, 60, 1, 58) def test_attributes(self): - a = Exporter((13, 5, 11, 3), '=h', (440, 88, 8, 2)) + a = Exporter((13, 5, 11, 3), "=h", (440, 88, 8, 2)) self.assertEqual(a.ndim, 4) self.assertEqual(a.itemsize, 2) self.assertFalse(a.readonly) self.assertEqual(a.shape, (13, 5, 11, 3)) - self.assertEqual(a.format, '=h') + self.assertEqual(a.format, "=h") self.assertEqual(a.strides, (440, 88, 8, 2)) self.assertEqual(a.len, 4290) self.assertEqual(a.buflen, 5720) @@ -511,32 +562,32 @@ class ExporterTest(unittest.TestCase): self.assertEqual(a.itemsize, 1) self.assertFalse(a.readonly) self.assertEqual(a.shape, (8,)) - self.assertEqual(a.format, 'B') + self.assertEqual(a.format, "B") self.assertTrue(isinstance(a.strides, tuple)) self.assertEqual(a.strides, (1,)) self.assertEqual(a.len, 8) self.assertEqual(a.buflen, 8) - a = Exporter([13, 5, 11, 3], '=h', [440, 88, 8, 2]) + a = Exporter([13, 5, 11, 3], "=h", [440, 88, 8, 2]) self.assertTrue(isinstance(a.shape, tuple)) self.assertTrue(isinstance(a.strides, tuple)) self.assertEqual(a.shape, (13, 5, 11, 3)) self.assertEqual(a.strides, (440, 88, 8, 2)) def test_itemsize(self): - exp = Exporter((4, 5), format='B', itemsize=8) + exp = Exporter((4, 5), format="B", itemsize=8) imp = Importer(exp, PyBUF_RECORDS) self.assertEqual(imp.itemsize, 8) - self.assertEqual(imp.format, 'B') + self.assertEqual(imp.format, "B") self.assertEqual(imp.strides, (40, 8)) - exp = Exporter((4, 5), format='weird', itemsize=5) + exp = Exporter((4, 5), format="weird", itemsize=5) imp = Importer(exp, PyBUF_RECORDS) self.assertEqual(imp.itemsize, 5) - self.assertEqual(imp.format, 'weird') + self.assertEqual(imp.format, "weird") self.assertEqual(imp.strides, (25, 5)) - def check_args(self, call_flags, - shape, format, strides, length, bufsize, itemsize, - offset=0): + def check_args( + self, call_flags, shape, format, strides, length, bufsize, itemsize, offset=0 + ): format_arg = format if call_flags & 1 else None strides_arg = strides if call_flags & 2 else None a = Exporter(shape, format_arg, strides_arg) @@ -551,5 +602,5 @@ class ExporterTest(unittest.TestCase): self.assertEqual(m.strides, strides) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/endian.py b/venv/Lib/site-packages/pygame/tests/test_utils/endian.py index ae8fc199505d73314c95e7a0c725a82cc31b4f5d..64ba1b32bb52fdca36aa2bbe391c03b5a23da9cf 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/endian.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/endian.py @@ -5,14 +5,16 @@ import struct + def little_endian_uint32(i): """Return the 32 bit unsigned integer little-endian representation of i""" - s = struct.pack('I', i) - return struct.unpack('=I', s)[0] + s = struct.pack(">I", i) + return struct.unpack("=I", s)[0] diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/png.py b/venv/Lib/site-packages/pygame/tests/test_utils/png.py index 9ca953996f7147911c119cc25fabc9ad84fc930e..fd7814204050b26dff7a1a16b6395e830fac968f 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/png.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/png.py @@ -5,7 +5,7 @@ # png.py - PNG encoder/decoder in pure Python # -# Modified for Pygame in Oct., 2012 to work with Python 3.x. +# Modified for Pygame in Oct., 2012 to work with Python 3.x. # # Copyright (C) 2006 Johann C. Rocholl # Portions Copyright (C) 2009 David Jones @@ -161,8 +161,7 @@ And now, my famous members __version__ = "$URL: http://pypng.googlecode.com/svn/trunk/code/png.py $ $Rev: 228 $" -from pygame.compat import geterror, imap_ -from array import array +import io import itertools import math import operator @@ -170,49 +169,55 @@ import struct import sys import zlib import warnings +from array import array +from functools import reduce +from pygame.tests.test_utils import tostring -__all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array'] +__all__ = ["Image", "Reader", "Writer", "write_chunks", "from_array"] # The PNG signature. # http://www.w3.org/TR/PNG/#5PNG-file-signature -_signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10) +_signature = struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10) + +_adam7 = ( + (0, 0, 8, 8), + (4, 0, 8, 8), + (0, 4, 4, 8), + (2, 0, 4, 4), + (0, 2, 2, 4), + (1, 0, 2, 2), + (0, 1, 1, 2), +) -_adam7 = ((0, 0, 8, 8), - (4, 0, 8, 8), - (0, 4, 4, 8), - (2, 0, 4, 4), - (0, 2, 2, 4), - (1, 0, 2, 2), - (0, 1, 1, 2)) def group(s, n): # See # http://www.python.org/doc/2.6/library/functions.html#zip - return zip(*[iter(s)]*n) + return zip(*[iter(s)] * n) + def isarray(x): - """Same as ``isinstance(x, array)``. - """ + """Same as ``isinstance(x, array)``.""" return isinstance(x, array) -def tostring(row): - """Convert row of bytes to string. Expects `row` to be an - ``array``. - """ - return row.tostring() - # Conditionally convert to bytes. Works on Python 2 and Python 3. try: - bytes('', 'ascii') - def strtobytes(x): return bytes(x, 'iso8859-1') - def bytestostr(x): return str(x, 'iso8859-1') + bytes("", "ascii") + + def strtobytes(x): + return bytes(x, "iso8859-1") + + def bytestostr(x): + return str(x, "iso8859-1") + except: strtobytes = str bytestostr = str + def interleave_planes(ipixels, apixels, ipsize, apsize): """ Interleave (colour) planes, e.g. RGB + A = RGBA. @@ -239,9 +244,10 @@ def interleave_planes(ipixels, apixels, ipsize, apsize): for i in range(ipsize): out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize] for i in range(apsize): - out[i+ipsize:newtotal:newpsize] = apixels[i:atotal:apsize] + out[i + ipsize : newtotal : newpsize] = apixels[i:atotal:apsize] return out + def check_palette(palette): """Check a palette argument (to the :class:`Writer` class) for validity. Returns the palette as a list if okay; raises an exception otherwise. @@ -255,35 +261,40 @@ def check_palette(palette): if not (0 < len(p) <= 256): raise ValueError("a palette must have between 1 and 256 entries") seen_triple = False - for i,t in enumerate(p): - if len(t) not in (3,4): - raise ValueError( - "palette entry %d: entries must be 3- or 4-tuples." % i) + for i, t in enumerate(p): + if len(t) not in (3, 4): + raise ValueError("palette entry %d: entries must be 3- or 4-tuples." % i) if len(t) == 3: seen_triple = True if seen_triple and len(t) == 4: raise ValueError( - "palette entry %d: all 4-tuples must precede all 3-tuples" % i) + "palette entry %d: all 4-tuples must precede all 3-tuples" % i + ) for x in t: - if int(x) != x or not(0 <= x <= 255): + if int(x) != x or not (0 <= x <= 255): raise ValueError( - "palette entry %d: values must be integer: 0 <= x <= 255" % i) + "palette entry %d: values must be integer: 0 <= x <= 255" % i + ) return p + class Error(Exception): - prefix = 'Error' + prefix = "Error" + def __str__(self): - return self.prefix + ': ' + ' '.join(self.args) + return f'{self.prefix}: {" ".join(self.args)}' + class FormatError(Error): """Problem with input file format. In other words, PNG file does not conform to the specification in some way and is invalid. """ - prefix = 'FormatError' + prefix = "FormatError" + class ChunkError(FormatError): - prefix = 'ChunkError' + prefix = "ChunkError" class Writer: @@ -291,22 +302,26 @@ class Writer: PNG encoder in pure Python. """ - def __init__(self, width=None, height=None, - size=None, - greyscale=False, - alpha=False, - bitdepth=8, - palette=None, - transparent=None, - background=None, - gamma=None, - compression=None, - interlace=False, - bytes_per_sample=None, # deprecated - planes=None, - colormap=None, - maxval=None, - chunk_limit=2**20): + def __init__( + self, + width=None, + height=None, + size=None, + greyscale=False, + alpha=False, + bitdepth=8, + palette=None, + transparent=None, + background=None, + gamma=None, + compression=None, + interlace=False, + bytes_per_sample=None, # deprecated + planes=None, + colormap=None, + maxval=None, + chunk_limit=2**20, + ): """ Create a PNG encoder object. @@ -407,15 +422,15 @@ class Writer: ``zlib`` module is used (which is generally acceptable). If `interlace` is true then an interlaced image is created - (using PNG's so far only interace method, *Adam7*). This does not + (using PNG's so far only interlace method, *Adam7*). This does not affect how the pixels should be presented to the encoder, rather it changes how they are arranged into the PNG file. On slow connexions interlaced images can be partially decoded by the browser to give a rough view of the image that is successively refined as more image data appears. - + .. note :: - + Enabling the `interlace` option requires the entire image to be processed in working memory. @@ -453,35 +468,33 @@ class Writer: except TypeError: c = (c,) if len(c) != 1: - raise ValueError("%s for greyscale must be 1-tuple" % - which) + raise ValueError(f"{which} for greyscale must be 1-tuple") if not isinteger(c[0]): - raise ValueError( - "%s colour for greyscale must be integer" % - which) + raise ValueError(f"{which} colour for greyscale must be integer") else: - if not (len(c) == 3 and - isinteger(c[0]) and - isinteger(c[1]) and - isinteger(c[2])): - raise ValueError( - "%s colour must be a triple of integers" % - which) + if not ( + len(c) == 3 + and isinteger(c[0]) + and isinteger(c[1]) + and isinteger(c[2]) + ): + raise ValueError(f"{which} colour must be a triple of integers") return c if size: if len(size) != 2: - raise ValueError( - "size argument should be a pair (width, height)") + raise ValueError("size argument should be a pair (width, height)") if width is not None and width != size[0]: raise ValueError( - "size[0] (%r) and width (%r) should match when both are used." - % (size[0], width)) + "size[0] (%r) and width (%r) should match when both are used." + % (size[0], width) + ) if height is not None and height != size[1]: raise ValueError( - "size[1] (%r) and height (%r) should match when both are used." - % (size[1], height)) - width,height = size + "size[1] (%r) and height (%r) should match when both are used." + % (size[1], height) + ) + width, height = size del size if width <= 0 or height <= 0: @@ -489,28 +502,28 @@ class Writer: if not isinteger(width) or not isinteger(height): raise ValueError("width and height must be integers") # http://www.w3.org/TR/PNG/#7Integers-and-byte-order - if width > 2**32-1 or height > 2**32-1: + if width > 2**32 - 1 or height > 2**32 - 1: raise ValueError("width and height cannot exceed 2**32-1") if alpha and transparent is not None: - raise ValueError( - "transparent colour not allowed with alpha channel") + raise ValueError("transparent colour not allowed with alpha channel") if bytes_per_sample is not None: - warnings.warn('please use bitdepth instead of bytes_per_sample', - DeprecationWarning) + warnings.warn( + "please use bitdepth instead of bytes_per_sample", DeprecationWarning + ) if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2): - raise ValueError( - "bytes per sample must be .125, .25, .5, 1, or 2") - bitdepth = int(8*bytes_per_sample) + raise ValueError("bytes per sample must be .125, .25, .5, 1, or 2") + bitdepth = int(8 * bytes_per_sample) del bytes_per_sample if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth: - raise ValueError("bitdepth (%r) must be a postive integer <= 16" % - bitdepth) + raise ValueError( + f"bitdepth ({bitdepth!r}) must be a positive integer <= 16" + ) self.rescale = None if palette: - if bitdepth not in (1,2,4,8): + if bitdepth not in (1, 2, 4, 8): raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8") if transparent is not None: raise ValueError("transparent and palette not compatible") @@ -521,35 +534,33 @@ class Writer: else: # No palette, check for sBIT chunk generation. if alpha or not greyscale: - if bitdepth not in (8,16): - targetbitdepth = (8,16)[bitdepth > 8] + if bitdepth not in (8, 16): + targetbitdepth = (8, 16)[bitdepth > 8] self.rescale = (bitdepth, targetbitdepth) bitdepth = targetbitdepth del targetbitdepth else: assert greyscale assert not alpha - if bitdepth not in (1,2,4,8,16): + if bitdepth not in (1, 2, 4, 8, 16): if bitdepth > 8: targetbitdepth = 16 elif bitdepth == 3: targetbitdepth = 4 else: - assert bitdepth in (5,6,7) + assert bitdepth in (5, 6, 7) targetbitdepth = 8 self.rescale = (bitdepth, targetbitdepth) bitdepth = targetbitdepth del targetbitdepth if bitdepth < 8 and (alpha or not greyscale and not palette): - raise ValueError( - "bitdepth < 8 only permitted with greyscale or palette") + raise ValueError("bitdepth < 8 only permitted with greyscale or palette") if bitdepth > 8 and palette: - raise ValueError( - "bit depth must be 8 or less for images with palette") + raise ValueError("bit depth must be 8 or less for images with palette") - transparent = check_color(transparent, 'transparent') - background = check_color(background, 'background') + transparent = check_color(transparent, "transparent") + background = check_color(background, "background") # It's important that the true boolean values (greyscale, alpha, # colormap, interlace) are converted to bool because Iverson's @@ -568,13 +579,13 @@ class Writer: self.interlace = bool(interlace) self.palette = check_palette(palette) - self.color_type = 4*self.alpha + 2*(not greyscale) + 1*self.colormap - assert self.color_type in (0,2,3,4,6) + self.color_type = 4 * self.alpha + 2 * (not greyscale) + 1 * self.colormap + assert self.color_type in (0, 2, 3, 4, 6) - self.color_planes = (3,1)[self.greyscale or self.colormap] + self.color_planes = (3, 1)[self.greyscale or self.colormap] self.planes = self.color_planes + self.alpha # :todo: fix for bitdepth < 8 - self.psize = (self.bitdepth/8) * self.planes + self.psize = (self.bitdepth / 8) * self.planes def make_palette(self): """Create the byte sequences for a ``PLTE`` and if necessary a @@ -582,8 +593,8 @@ class Writer: ``None`` if no ``tRNS`` chunk is necessary. """ - p = array('B') - t = array('B') + p = array("B") + t = array("B") for x in self.palette: p.extend(x[0:3]) @@ -592,8 +603,8 @@ class Writer: p = tostring(p) t = tostring(t) if t: - return p,t - return p,None + return p, t + return p, None def write(self, outfile, rows): """Write a PNG image to the output file. `rows` should be @@ -603,22 +614,23 @@ class Writer: If `interlace` is specified (when creating the instance), then an interlaced PNG file will be written. Supply the rows in the normal image order; the interlacing is carried out internally. - + .. note :: Interlacing will require the entire image to be in working memory. """ if self.interlace: - fmt = 'BH'[self.bitdepth > 8] + fmt = "BH"[self.bitdepth > 8] a = array(fmt, itertools.chain(*rows)) return self.write_array(outfile, a) else: nrows = self.write_passes(outfile, rows) if nrows != self.height: raise ValueError( - "rows supplied (%d) does not match height (%d)" % - (nrows, self.height)) + "rows supplied (%d) does not match height (%d)" + % (nrows, self.height) + ) def write_passes(self, outfile, rows, packed=False): """ @@ -626,7 +638,7 @@ class Writer: Most users are expected to find the :meth:`write` or :meth:`write_array` method more convenient. - + The rows should be given to this method in the order that they appear in the output file. For straightlaced images, this is the usual top to bottom ordering, but for interlaced @@ -644,53 +656,62 @@ class Writer: outfile.write(_signature) # http://www.w3.org/TR/PNG/#11IHDR - write_chunk(outfile, 'IHDR', - struct.pack("!2I5B", self.width, self.height, - self.bitdepth, self.color_type, - 0, 0, self.interlace)) + write_chunk( + outfile, + "IHDR", + struct.pack( + "!2I5B", + self.width, + self.height, + self.bitdepth, + self.color_type, + 0, + 0, + self.interlace, + ), + ) # See :chunk:order # http://www.w3.org/TR/PNG/#11gAMA if self.gamma is not None: - write_chunk(outfile, 'gAMA', - struct.pack("!L", int(round(self.gamma*1e5)))) + write_chunk( + outfile, "gAMA", struct.pack("!L", int(round(self.gamma * 1e5))) + ) # See :chunk:order # http://www.w3.org/TR/PNG/#11sBIT if self.rescale: - write_chunk(outfile, 'sBIT', - struct.pack('%dB' % self.planes, - *[self.rescale[0]]*self.planes)) - + write_chunk( + outfile, + "sBIT", + struct.pack("%dB" % self.planes, *[self.rescale[0]] * self.planes), + ) + # :chunk:order: Without a palette (PLTE chunk), ordering is # relatively relaxed. With one, gAMA chunk must precede PLTE # chunk which must precede tRNS and bKGD. # See http://www.w3.org/TR/PNG/#5ChunkOrdering if self.palette: - p,t = self.make_palette() - write_chunk(outfile, 'PLTE', p) + p, t = self.make_palette() + write_chunk(outfile, "PLTE", p) if t: # tRNS chunk is optional. Only needed if palette entries # have alpha. - write_chunk(outfile, 'tRNS', t) + write_chunk(outfile, "tRNS", t) # http://www.w3.org/TR/PNG/#11tRNS if self.transparent is not None: if self.greyscale: - write_chunk(outfile, 'tRNS', - struct.pack("!1H", *self.transparent)) + write_chunk(outfile, "tRNS", struct.pack("!1H", *self.transparent)) else: - write_chunk(outfile, 'tRNS', - struct.pack("!3H", *self.transparent)) + write_chunk(outfile, "tRNS", struct.pack("!3H", *self.transparent)) # http://www.w3.org/TR/PNG/#11bKGD if self.background is not None: if self.greyscale: - write_chunk(outfile, 'bKGD', - struct.pack("!1H", *self.background)) + write_chunk(outfile, "bKGD", struct.pack("!1H", *self.background)) else: - write_chunk(outfile, 'bKGD', - struct.pack("!3H", *self.background)) + write_chunk(outfile, "bKGD", struct.pack("!3H", *self.background)) # http://www.w3.org/TR/PNG/#11IDAT if self.compression is not None: @@ -701,37 +722,39 @@ class Writer: # Choose an extend function based on the bitdepth. The extend # function packs/decomposes the pixel values into bytes and # stuffs them onto the data array. - data = array('B') + data = array("B") if self.bitdepth == 8 or packed: extend = data.extend elif self.bitdepth == 16: # Decompose into bytes def extend(sl): - fmt = '!%dH' % len(sl) - data.extend(array('B', struct.pack(fmt, *sl))) + fmt = f"!{len(sl)}H" + data.extend(array("B", struct.pack(fmt, *sl))) + else: # Pack into bytes assert self.bitdepth < 8 # samples per byte - spb = int(8/self.bitdepth) + spb = int(8 / self.bitdepth) + def extend(sl): - a = array('B', sl) + a = array("B", sl) # Adding padding bytes so we can group into a whole # number of spb-tuples. l = float(len(a)) - extra = math.ceil(l / float(spb))*spb - l - a.extend([0]*int(extra)) + extra = math.ceil(l / float(spb)) * spb - l + a.extend([0] * int(extra)) # Pack into bytes l = group(a, spb) - l = map(lambda e: reduce(lambda x,y: - (x << self.bitdepth) + y, e), l) + l = map(lambda e: reduce(lambda x, y: (x << self.bitdepth) + y, e), l) data.extend(l) + if self.rescale: oldextend = extend - factor = \ - float(2**self.rescale[1]-1) / float(2**self.rescale[0]-1) + factor = float(2 ** self.rescale[1] - 1) / float(2 ** self.rescale[0] - 1) + def extend(sl): - oldextend(map(lambda x: int(round(factor*x)), sl)) + oldextend(map(lambda x: int(round(factor * x)), sl)) # Build the first row, testing mostly to see if we need to # changed the extend function to cope with NumPy integer types @@ -757,11 +780,12 @@ class Writer: # int types it works for. def wrapmapint(f): return lambda sl: f(map(int, sl)) + extend = wrapmapint(extend) del wrapmapint extend(row) - for i,row in enumrows: + for i, row in enumrows: # Add "None" filter type. Currently, it's essential that # this filter type be used for every scanline as we do not # mark the first row of a reduced pass image; that means we @@ -772,8 +796,8 @@ class Writer: if len(data) > self.chunk_limit: compressed = compressor.compress(tostring(data)) if len(compressed): - # print >> sys.stderr, len(data), len(compressed) - write_chunk(outfile, 'IDAT', compressed) + # print(len(data), len(compressed), file= >> sys.stderr) + write_chunk(outfile, "IDAT", compressed) # Because of our very witty definition of ``extend``, # above, we must re-use the same ``data`` object. Hence # we use ``del`` to empty this one, rather than create a @@ -782,14 +806,14 @@ class Writer: if len(data): compressed = compressor.compress(tostring(data)) else: - compressed = '' + compressed = "" flushed = compressor.flush() if len(compressed) or len(flushed): - # print >> sys.stderr, len(data), len(compressed), len(flushed) - write_chunk(outfile, 'IDAT', compressed + flushed) + # print(len(data), len(compressed), len(flushed), file=sys.stderr) + write_chunk(outfile, "IDAT", compressed + flushed) # http://www.w3.org/TR/PNG/#11IEND - write_chunk(outfile, 'IEND') - return i+1 + write_chunk(outfile, "IEND") + return i + 1 def write_array(self, outfile, pixels): """ @@ -818,8 +842,9 @@ class Writer: """ if self.rescale: - raise Error("write_packed method not suitable for bit depth %d" % - self.rescale[0]) + raise Error( + "write_packed method not suitable for bit depth %d" % self.rescale[0] + ) return self.write_passes(outfile, rows, packed=True) def convert_pnm(self, infile, outfile): @@ -830,10 +855,11 @@ class Writer: """ if self.interlace: - pixels = array('B') - pixels.fromfile(infile, - (self.bitdepth/8) * self.color_planes * - self.width * self.height) + pixels = array("B") + pixels.fromfile( + infile, + (self.bitdepth / 8) * self.color_planes * self.width * self.height, + ) self.write_passes(outfile, self.array_scanlines_interlace(pixels)) else: self.write_passes(outfile, self.file_scanlines(infile)) @@ -843,17 +869,18 @@ class Writer: Convert a PPM and PGM file containing raw pixel data into a PNG outfile with the parameters set in the writer object. """ - pixels = array('B') - pixels.fromfile(ppmfile, - (self.bitdepth/8) * self.color_planes * - self.width * self.height) - apixels = array('B') - apixels.fromfile(pgmfile, - (self.bitdepth/8) * - self.width * self.height) - pixels = interleave_planes(pixels, apixels, - (self.bitdepth/8) * self.color_planes, - (self.bitdepth/8)) + pixels = array("B") + pixels.fromfile( + ppmfile, (self.bitdepth / 8) * self.color_planes * self.width * self.height + ) + apixels = array("B") + apixels.fromfile(pgmfile, (self.bitdepth / 8) * self.width * self.height) + pixels = interleave_planes( + pixels, + apixels, + (self.bitdepth / 8) * self.color_planes, + (self.bitdepth / 8), + ) if self.interlace: self.write_passes(outfile, self.array_scanlines_interlace(pixels)) else: @@ -875,13 +902,17 @@ class Writer: if self.bitdepth > 8: assert self.bitdepth == 16 row_bytes *= 2 - fmt = '>%dH' % vpr + fmt = ">%dH" % vpr + def line(): - return array('H', struct.unpack(fmt, infile.read(row_bytes))) + return array("H", struct.unpack(fmt, infile.read(row_bytes))) + else: + def line(): - scanline = array('B', infile.read(row_bytes)) + scanline = array("B", infile.read(row_bytes)) return scanline + for y in range(self.height): yield line() @@ -909,33 +940,33 @@ class Writer: # http://www.w3.org/TR/PNG/#8InterlaceMethods # Array type. - fmt = 'BH'[self.bitdepth > 8] + fmt = "BH"[self.bitdepth > 8] # Value per row vpr = self.width * self.planes for xstart, ystart, xstep, ystep in _adam7: if xstart >= self.width: continue # Pixels per row (of reduced image) - ppr = int(math.ceil((self.width-xstart)/float(xstep))) + ppr = int(math.ceil((self.width - xstart) / float(xstep))) # number of values in reduced image row. - row_len = ppr*self.planes + row_len = ppr * self.planes for y in range(ystart, self.height, ystep): if xstep == 1: offset = y * vpr - yield pixels[offset:offset+vpr] + yield pixels[offset : offset + vpr] else: row = array(fmt) # There's no easier way to set the length of an array row.extend(pixels[0:row_len]) offset = y * vpr + xstart * self.planes - end_offset = (y+1) * vpr + end_offset = (y + 1) * vpr skip = self.planes * xstep for i in range(self.planes): - row[i::self.planes] = \ - pixels[offset+i:end_offset:skip] + row[i :: self.planes] = pixels[offset + i : end_offset : skip] yield row -def write_chunk(outfile, tag, data=strtobytes('')): + +def write_chunk(outfile, tag, data=strtobytes("")): """ Write a PNG chunk to the output file, including length and checksum. @@ -948,9 +979,10 @@ def write_chunk(outfile, tag, data=strtobytes('')): outfile.write(data) checksum = zlib.crc32(tag) checksum = zlib.crc32(data, checksum) - checksum &= 2**32-1 + checksum &= 2**32 - 1 outfile.write(struct.pack("!I", checksum)) + def write_chunks(out, chunks): """Create a PNG file by writing out the chunks.""" @@ -958,6 +990,7 @@ def write_chunks(out, chunks): for chunk in chunks: write_chunk(out, *chunk) + def filter_scanline(type, line, fo, prev=None): """Apply a scanline filter to a scanline. `type` specifies the filter type (0 to 4); `line` specifies the current (unfiltered) @@ -972,32 +1005,35 @@ def filter_scanline(type, line, fo, prev=None): # The output array. Which, pathetically, we extend one-byte at a # time (fortunately this is linear). - out = array('B', [type]) + out = array("B", [type]) def sub(): ai = -fo for x in line: if ai >= 0: - x = (x - line[ai]) & 0xff + x = (x - line[ai]) & 0xFF out.append(x) ai += 1 + def up(): - for i,x in enumerate(line): - x = (x - prev[i]) & 0xff + for i, x in enumerate(line): + x = (x - prev[i]) & 0xFF out.append(x) + def average(): ai = -fo - for i,x in enumerate(line): + for i, x in enumerate(line): if ai >= 0: - x = (x - ((line[ai] + prev[i]) >> 1)) & 0xff + x = (x - ((line[ai] + prev[i]) >> 1)) & 0xFF else: - x = (x - (prev[i] >> 1)) & 0xff + x = (x - (prev[i] >> 1)) & 0xFF out.append(x) ai += 1 + def paeth(): # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth - ai = -fo # also used for ci - for i,x in enumerate(line): + ai = -fo # also used for ci + for i, x in enumerate(line): a = 0 b = prev[i] c = 0 @@ -1009,11 +1045,14 @@ def filter_scanline(type, line, fo, prev=None): pa = abs(p - a) pb = abs(p - b) pc = abs(p - c) - if pa <= pb and pa <= pc: Pr = a - elif pb <= pc: Pr = b - else: Pr = c + if pa <= pb and pa <= pc: + Pr = a + elif pb <= pc: + Pr = b + else: + Pr = c - x = (x - Pr) & 0xff + x = (x - Pr) & 0xFF out.append(x) ai += 1 @@ -1023,11 +1062,11 @@ def filter_scanline(type, line, fo, prev=None): # of the image simpler. "up" becomes "none"; "paeth" becomes # "left" (non-trivial, but true). "average" needs to be handled # specially. - if type == 2: # "up" - return line # type = 0 + if type == 2: # "up" + return line # type = 0 elif type == 3: - prev = [0]*len(line) - elif type == 4: # "paeth" + prev = [0] * len(line) + elif type == 4: # "paeth" type = 1 if type == 0: out.extend(line) @@ -1037,7 +1076,7 @@ def filter_scanline(type, line, fo, prev=None): up() elif type == 3: average() - else: # type == 4 + else: # type == 4 paeth() return out @@ -1053,7 +1092,7 @@ def from_array(a, mode=None, info={}): only. It doesn't actually work. Please bear with us. Meanwhile enjoy the complimentary snacks (on request) and please use a 2-dimensional array. - + Unless they are specified using the *info* parameter, the PNG's height and width are taken from the array size. For a 3 dimensional array the first axis is the height; the second axis is the width; @@ -1108,7 +1147,7 @@ def from_array(a, mode=None, info={}): metadata (in the same style as the arguments to the :class:``png.Writer`` class). For this function the keys that are useful are: - + height overrides the height derived from the array dimensions and allows *a* to be an iterable. @@ -1133,10 +1172,10 @@ def from_array(a, mode=None, info={}): # Syntax check mode string. bitdepth = None try: - mode = mode.split(';') - if len(mode) not in (1,2): + mode = mode.split(";") + if len(mode) not in (1, 2): raise Error() - if mode[0] not in ('L', 'LA', 'RGB', 'RGBA'): + if mode[0] not in ("L", "LA", "RGB", "RGBA"): raise Error() if len(mode) == 2: try: @@ -1149,48 +1188,49 @@ def from_array(a, mode=None, info={}): # Get bitdepth from *mode* if possible. if bitdepth: - if info.get('bitdepth') and bitdepth != info['bitdepth']: - raise Error("mode bitdepth (%d) should match info bitdepth (%d)." % - (bitdepth, info['bitdepth'])) - info['bitdepth'] = bitdepth + if info.get("bitdepth") and bitdepth != info["bitdepth"]: + raise Error( + "mode bitdepth (%d) should match info bitdepth (%d)." + % (bitdepth, info["bitdepth"]) + ) + info["bitdepth"] = bitdepth # Fill in and/or check entries in *info*. # Dimensions. - if 'size' in info: + if "size" in info: # Check width, height, size all match where used. - for dimension,axis in [('width', 0), ('height', 1)]: + for dimension, axis in [("width", 0), ("height", 1)]: if dimension in info: - if info[dimension] != info['size'][axis]: + if info[dimension] != info["size"][axis]: raise Error( - "info[%r] shhould match info['size'][%r]." % - (dimension, axis)) - info['width'],info['height'] = info['size'] - if 'height' not in info: + f"info[{dimension!r}] should match info['size'][{axis!r}]." + ) + info["width"], info["height"] = info["size"] + if "height" not in info: try: l = len(a) except: - raise Error( - "len(a) does not work, supply info['height'] instead.") - info['height'] = l + raise Error("len(a) does not work, supply info['height'] instead.") + info["height"] = l # Colour format. - if 'greyscale' in info: - if bool(info['greyscale']) != ('L' in mode): + if "greyscale" in info: + if bool(info["greyscale"]) != ("L" in mode): raise Error("info['greyscale'] should match mode.") - info['greyscale'] = 'L' in mode - if 'alpha' in info: - if bool(info['alpha']) != ('A' in mode): + info["greyscale"] = "L" in mode + if "alpha" in info: + if bool(info["alpha"]) != ("A" in mode): raise Error("info['alpha'] should match mode.") - info['alpha'] = 'A' in mode + info["alpha"] = "A" in mode planes = len(mode) - if 'planes' in info: - if info['planes'] != planes: + if "planes" in info: + if info["planes"] != planes: raise Error("info['planes'] should match mode.") # In order to work out whether we the array is 2D or 3D we need its # first row, which requires that we take a copy of its iterator. # We may also need the first row to derive width and bitdepth. - a,t = itertools.tee(a) + a, t = itertools.tee(a) row = next(t) del t try: @@ -1200,17 +1240,17 @@ def from_array(a, mode=None, info={}): except: threed = False testelement = row - if 'width' not in info: + if "width" not in info: if threed: width = len(row) else: width = len(row) // planes - info['width'] = width + info["width"] = width # Not implemented yet assert not threed - if 'bitdepth' not in info: + if "bitdepth" not in info: try: dtype = testelement.dtype # goto the "else:" clause. Sorry. @@ -1225,31 +1265,34 @@ def from_array(a, mode=None, info={}): else: # If we got here without exception, we now assume that # the array is a numpy array. - if dtype.kind == 'b': + if dtype.kind == "b": bitdepth = 1 else: bitdepth = 8 * dtype.itemsize - info['bitdepth'] = bitdepth + info["bitdepth"] = bitdepth - for thing in 'width height bitdepth greyscale alpha'.split(): + for thing in "width height bitdepth greyscale alpha".split(): assert thing in info return Image(a, info) + # So that refugee's from PIL feel more at home. Not documented. fromarray = from_array + class Image: """A PNG image. You can create an :class:`Image` object from an array of pixels by calling :meth:`png.from_array`. It can be saved to disk with the :meth:`save` method.""" + def __init__(self, rows, info): """ .. note :: - + The constructor is not public. Please do not call it. """ - + self.rows = rows self.info = info @@ -1268,16 +1311,22 @@ class Image: try: file.write - def close(): pass + + def close(): + pass + except: - file = open(file, 'wb') - def close(): file.close() + file = open(file, "wb") + + def close(): + file.close() try: w.write(file, self.rows) finally: close() + class _readable: """ A simple file-like interface for strings and arrays. @@ -1288,9 +1337,9 @@ class _readable: self.offset = 0 def read(self, n): - r = self.buf[self.offset:self.offset+n] + r = self.buf[self.offset : self.offset + n] if isarray(r): - r = r.tostring() + r = tostring(r) self.offset += n return r @@ -1316,8 +1365,7 @@ class Reader: ``array`` or ``string`` with PNG data. """ - if ((_guess is not None and len(kw) != 0) or - (_guess is None and len(kw) != 1)): + if (_guess is not None and len(kw) != 0) or (_guess is None and len(kw) != 1): raise TypeError("Reader() takes exactly 1 argument") # Will be the first 8 bytes, later on. See validate_signature. @@ -1334,7 +1382,7 @@ class Reader: kw["bytes"] = _guess elif isinstance(_guess, str): kw["filename"] = _guess - elif isinstance(_guess, file): + elif isinstance(_guess, io.IOBase): kw["file"] = _guess if "filename" in kw: @@ -1366,15 +1414,16 @@ class Reader: # http://www.w3.org/TR/PNG/#5Chunk-layout if not self.atchunk: self.atchunk = self.chunklentype() - length,type = self.atchunk + length, type = self.atchunk self.atchunk = None data = self.file.read(length) if len(data) != length: - raise ChunkError('Chunk %s too short for required %i octets.' - % (type, length)) + raise ChunkError( + "Chunk %s too short for required %i octets." % (type, length) + ) checksum = self.file.read(4) if len(checksum) != 4: - raise ValueError('Chunk %s too short for checksum.', tag) + raise ValueError("Chunk %s too short for checksum.", checksum) if seek and type != seek: continue verify = zlib.crc32(strtobytes(type)) @@ -1385,14 +1434,14 @@ class Reader: # We coerce it to be positive here (in a way which works on # Python 2.3 and older). verify &= 2**32 - 1 - verify = struct.pack('!I', verify) + verify = struct.pack("!I", verify) if checksum != verify: - # print repr(checksum) - (a, ) = struct.unpack('!I', checksum) - (b, ) = struct.unpack('!I', verify) + # print(repr(checksum)) + (a,) = struct.unpack("!I", checksum) + (b,) = struct.unpack("!I", verify) raise ChunkError( - "Checksum error in %s chunk: 0x%08X != 0x%08X." % - (type, a, b)) + f"Checksum error in {type} chunk: 0x{a:08X} != 0x{b:08X}." + ) return type, data def chunks(self): @@ -1401,9 +1450,9 @@ class Reader: """ while True: - t,v = self.chunk() - yield t,v - if t == 'IEND': + t, v = self.chunk() + yield t, v + if t == "IEND": break def undo_filter(self, filter_type, scanline, previous): @@ -1428,16 +1477,18 @@ class Reader: # existing sequence. *sigh* # If we fill the result with scanline, then this allows a # micro-optimisation in the "null" and "sub" cases. - result = array('B', scanline) + result = array("B", scanline) if filter_type == 0: # And here, we _rely_ on filling the result with scanline, # above. return result - if filter_type not in (1,2,3,4): - raise FormatError('Invalid PNG Filter Type.' - ' See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .') + if filter_type not in (1, 2, 3, 4): + raise FormatError( + "Invalid PNG Filter Type." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + ) # Filter unit. The stride from one pixel to the corresponding # byte from the previous previous. Normally this is the pixel @@ -1450,7 +1501,7 @@ class Reader: # first line 'up' is the same as 'null', 'paeth' is the same # as 'sub', with only 'average' requiring any special case. if not previous: - previous = array('B', [0]*len(scanline)) + previous = array("B", [0] * len(scanline)) def sub(): """Undo sub filter.""" @@ -1462,29 +1513,28 @@ class Reader: for i in range(fu, len(result)): x = scanline[i] a = result[ai] - result[i] = (x + a) & 0xff + result[i] = (x + a) & 0xFF ai += 1 def up(): """Undo up filter.""" - - for i in range(len(result)): + for i in range(len(result)): # pylint: disable=consider-using-enumerate x = scanline[i] b = previous[i] - result[i] = (x + b) & 0xff + result[i] = (x + b) & 0xFF def average(): """Undo average filter.""" ai = -fu - for i in range(len(result)): + for i in range(len(result)): # pylint: disable=consider-using-enumerate x = scanline[i] if ai < 0: a = 0 else: a = result[ai] b = previous[i] - result[i] = (x + ((a + b) >> 1)) & 0xff + result[i] = (x + ((a + b) >> 1)) & 0xFF ai += 1 def paeth(): @@ -1492,7 +1542,7 @@ class Reader: # Also used for ci. ai = -fu - for i in range(len(result)): + for i in range(len(result)): # pylint: disable=consider-using-enumerate x = scanline[i] if ai < 0: a = c = 0 @@ -1510,7 +1560,7 @@ class Reader: pr = b else: pr = c - result[i] = (x + pr) & 0xff + result[i] = (x + pr) & 0xFF ai += 1 # Call appropriate filter algorithm. Note that 0 has already @@ -1524,21 +1574,21 @@ class Reader: Return in flat row flat pixel format. """ - # print >> sys.stderr, ("Reading interlaced, w=%s, r=%s, planes=%s," + - # " bpp=%s") % (self.width, self.height, self.planes, self.bps) + # print("Reading interlaced, w=%s, r=%s, planes=%s, bpp=%s" + # % (self.width, self.height, self.planes, self.bps, file=sys.stderr)) # Values per row (of the target image) vpr = self.width * self.planes # Make a result array, and make it big enough. Interleaving # writes to the output array randomly (well, not quite), so the # entire output array must be in memory. - fmt = 'BH'[self.bitdepth > 8] - a = array(fmt, [0]*vpr*self.height) + fmt = "BH"[self.bitdepth > 8] + a = array(fmt, [0] * vpr * self.height) source_offset = 0 for xstart, ystart, xstep, ystep in _adam7: - # print >> sys.stderr, "Adam7: start=%s,%s step=%s,%s" % ( - # xstart, ystart, xstep, ystep) + # print("Adam7: start=%s,%s step=%s,%s" % ( + # xstart, ystart, xstep, ystep, file=sys.stderr)) if xstart >= self.width: continue # The previous (reconstructed) scanline. None at the @@ -1546,13 +1596,13 @@ class Reader: # line. recon = None # Pixels per row (reduced pass image) - ppr = int(math.ceil((self.width-xstart)/float(xstep))) + ppr = int(math.ceil((self.width - xstart) / float(xstep))) # Row size in bytes for this pass. row_size = int(math.ceil(self.psize * ppr)) for y in range(ystart, self.height, ystep): filter_type = raw[source_offset] source_offset += 1 - scanline = raw[source_offset:source_offset+row_size] + scanline = raw[source_offset : source_offset + row_size] source_offset += row_size recon = self.undo_filter(filter_type, scanline, recon) # Convert so that there is one element per pixel value @@ -1560,14 +1610,13 @@ class Reader: if xstep == 1: assert xstart == 0 offset = y * vpr - a[offset:offset+vpr] = flat + a[offset : offset + vpr] = flat else: offset = y * vpr + xstart * self.planes - end_offset = (y+1) * vpr + end_offset = (y + 1) * vpr skip = self.planes * xstep for i in range(self.planes): - a[offset+i:end_offset:skip] = \ - flat[i::self.planes] + a[offset + i : end_offset : skip] = flat[i :: self.planes] return a def iterboxed(self, rows): @@ -1584,19 +1633,19 @@ class Reader: return raw if self.bitdepth == 16: raw = tostring(raw) - return array('H', struct.unpack('!%dH' % (len(raw)//2), raw)) + return array("H", struct.unpack("!%dH" % (len(raw) // 2), raw)) assert self.bitdepth < 8 width = self.width # Samples per byte - spb = 8//self.bitdepth - out = array('B') + spb = 8 // self.bitdepth + out = array("B") mask = 2**self.bitdepth - 1 shifts = map(self.bitdepth.__mul__, reversed(range(spb))) for o in raw: - out.extend(map(lambda i: mask&(o>>i), shifts)) + out.extend(map(lambda i: mask & (o >> i), shifts)) return out[:width] - return imap_(asvalues, rows) + return map(asvalues, rows) def serialtoflat(self, bytes, width=None): """Convert serial format (byte stream) pixel data to flat row @@ -1607,19 +1656,18 @@ class Reader: return bytes if self.bitdepth == 16: bytes = tostring(bytes) - return array('H', - struct.unpack('!%dH' % (len(bytes)//2), bytes)) + return array("H", struct.unpack("!%dH" % (len(bytes) // 2), bytes)) assert self.bitdepth < 8 if width is None: width = self.width # Samples per byte - spb = 8//self.bitdepth - out = array('B') + spb = 8 // self.bitdepth + out = array("B") mask = 2**self.bitdepth - 1 shifts = map(self.bitdepth.__mul__, reversed(range(spb))) l = width for o in bytes: - out.extend([(mask&(o>>s)) for s in shifts][:l]) + out.extend([(mask & (o >> s)) for s in shifts][:l]) l -= spb if l <= 0: l = width @@ -1633,7 +1681,7 @@ class Reader: # length of row, in bytes rb = self.row_bytes - a = array('B') + a = array("B") # The previous (reconstructed) scanline. None indicates first # line of image. recon = None @@ -1641,16 +1689,15 @@ class Reader: a.extend(some) while len(a) >= rb + 1: filter_type = a[0] - scanline = a[1:rb+1] - del a[:rb+1] + scanline = a[1 : rb + 1] + del a[: rb + 1] recon = self.undo_filter(filter_type, scanline, recon) yield recon if len(a) != 0: # :file:format We get here with a file format error: when the # available bytes (after decompressing) do not pack into exact # rows. - raise FormatError( - 'Wrong size for decompressed IDAT chunk.') + raise FormatError("Wrong size for decompressed IDAT chunk.") assert len(a) == 0 def validate_signature(self): @@ -1678,9 +1725,8 @@ class Reader: if not self.atchunk: self.atchunk = self.chunklentype() if self.atchunk is None: - raise FormatError( - 'This PNG file has no IDAT chunks.') - if self.atchunk[1] == 'IDAT': + raise FormatError("This PNG file has no IDAT chunks.") + if self.atchunk[1] == "IDAT": return self.process_chunk() @@ -1695,13 +1741,12 @@ class Reader: if not x: return None if len(x) != 8: - raise FormatError( - 'End of file whilst reading chunk length and type.') - length,type = struct.unpack('!I4s', x) + raise FormatError("End of file whilst reading chunk length and type.") + length, type = struct.unpack("!I4s", x) type = bytestostr(type) - if length > 2**31-1: - raise FormatError('Chunk %s is too large: %d.' % (type,length)) - return length,type + if length > 2**31 - 1: + raise FormatError("Chunk %s is too large: %d." % (type, length)) + return length, type def process_chunk(self): """Process the next chunk and its data. This only processes the @@ -1710,45 +1755,58 @@ class Reader: """ type, data = self.chunk() - if type == 'IHDR': + if type == "IHDR": # http://www.w3.org/TR/PNG/#11IHDR if len(data) != 13: - raise FormatError('IHDR chunk has incorrect length.') - (self.width, self.height, self.bitdepth, self.color_type, - self.compression, self.filter, - self.interlace) = struct.unpack("!2I5B", data) + raise FormatError("IHDR chunk has incorrect length.") + ( + self.width, + self.height, + self.bitdepth, + self.color_type, + self.compression, + self.filter, + self.interlace, + ) = struct.unpack("!2I5B", data) # Check that the header specifies only valid combinations. - if self.bitdepth not in (1,2,4,8,16): + if self.bitdepth not in (1, 2, 4, 8, 16): raise Error("invalid bit depth %d" % self.bitdepth) - if self.color_type not in (0,2,3,4,6): + if self.color_type not in (0, 2, 3, 4, 6): raise Error("invalid colour type %d" % self.color_type) # Check indexed (palettized) images have 8 or fewer bits # per pixel; check only indexed or greyscale images have # fewer than 8 bits per pixel. - if ((self.color_type & 1 and self.bitdepth > 8) or - (self.bitdepth < 8 and self.color_type not in (0,3))): - raise FormatError("Illegal combination of bit depth (%d)" - " and colour type (%d)." - " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." - % (self.bitdepth, self.color_type)) + if (self.color_type & 1 and self.bitdepth > 8) or ( + self.bitdepth < 8 and self.color_type not in (0, 3) + ): + raise FormatError( + "Illegal combination of bit depth (%d)" + " and colour type (%d)." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." + % (self.bitdepth, self.color_type) + ) if self.compression != 0: raise Error("unknown compression method %d" % self.compression) if self.filter != 0: - raise FormatError("Unknown filter method %d," - " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." - % self.filter) - if self.interlace not in (0,1): - raise FormatError("Unknown interlace method %d," - " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." - % self.interlace) + raise FormatError( + "Unknown filter method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + % self.filter + ) + if self.interlace not in (0, 1): + raise FormatError( + "Unknown interlace method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." + % self.interlace + ) # Derived values # http://www.w3.org/TR/PNG/#6Colour-values - colormap = bool(self.color_type & 1) + colormap = bool(self.color_type & 1) greyscale = not (self.color_type & 2) alpha = bool(self.color_type & 4) - color_planes = (3,1)[greyscale or colormap] + color_planes = (3, 1)[greyscale or colormap] planes = color_planes + alpha self.colormap = colormap @@ -1756,7 +1814,7 @@ class Reader: self.alpha = alpha self.color_planes = color_planes self.planes = planes - self.psize = float(self.bitdepth)/float(8) * planes + self.psize = float(self.bitdepth) / float(8) * planes if int(self.psize) == self.psize: self.psize = int(self.psize) self.row_bytes = int(math.ceil(self.width * self.psize)) @@ -1768,60 +1826,60 @@ class Reader: self.trns = None # Stores sbit chunk if present. self.sbit = None - elif type == 'PLTE': + elif type == "PLTE": # http://www.w3.org/TR/PNG/#11PLTE if self.plte: warnings.warn("Multiple PLTE chunks present.") self.plte = data if len(data) % 3 != 0: - raise FormatError( - "PLTE chunk's length should be a multiple of 3.") - if len(data) > (2**self.bitdepth)*3: + raise FormatError("PLTE chunk's length should be a multiple of 3.") + if len(data) > (2**self.bitdepth) * 3: raise FormatError("PLTE chunk is too long.") if len(data) == 0: raise FormatError("Empty PLTE is not allowed.") - elif type == 'bKGD': + elif type == "bKGD": try: if self.colormap: if not self.plte: - warnings.warn( - "PLTE chunk is required before bKGD chunk.") - self.background = struct.unpack('B', data) + warnings.warn("PLTE chunk is required before bKGD chunk.") + self.background = struct.unpack("B", data) else: - self.background = struct.unpack("!%dH" % self.color_planes, - data) + self.background = struct.unpack("!%dH" % self.color_planes, data) except struct.error: raise FormatError("bKGD chunk has incorrect length.") - elif type == 'tRNS': + elif type == "tRNS": # http://www.w3.org/TR/PNG/#11tRNS self.trns = data if self.colormap: if not self.plte: warnings.warn("PLTE chunk is required before tRNS chunk.") else: - if len(data) > len(self.plte)/3: + if len(data) > len(self.plte) / 3: # Was warning, but promoted to Error as it # would otherwise cause pain later on. raise FormatError("tRNS chunk is too long.") else: if self.alpha: raise FormatError( - "tRNS chunk is not valid with colour type %d." % - self.color_type) + "tRNS chunk is not valid with colour type %d." % self.color_type + ) try: - self.transparent = \ - struct.unpack("!%dH" % self.color_planes, data) + self.transparent = struct.unpack("!%dH" % self.color_planes, data) except struct.error: raise FormatError("tRNS chunk has incorrect length.") - elif type == 'gAMA': + elif type == "gAMA": try: self.gamma = struct.unpack("!L", data)[0] / 100000.0 except struct.error: raise FormatError("gAMA chunk has incorrect length.") - elif type == 'sBIT': + elif type == "sBIT": self.sbit = data - if (self.colormap and len(data) != 3 or - not self.colormap and len(data) != self.planes): + if ( + self.colormap + and len(data) != 3 + or not self.colormap + and len(data) != self.planes + ): raise FormatError("sBIT chunk has incorrect length.") def read(self): @@ -1839,13 +1897,12 @@ class Reader: while True: try: type, data = self.chunk() - except ValueError: - e = geterror() + except ValueError as e: raise ChunkError(e.args[0]) - if type == 'IEND': + if type == "IEND": # http://www.w3.org/TR/PNG/#11IEND break - if type != 'IDAT': + if type != "IDAT": continue # type == 'IDAT' # http://www.w3.org/TR/PNG/#11IDAT @@ -1858,7 +1915,7 @@ class Reader: be an iterator that yields the ``IDAT`` chunk data. """ - # Currently, with no max_length paramter to decompress, this + # Currently, with no max_length parameter to decompress, this # routine will do one yield per IDAT chunk. So not very # incremental. d = zlib.decompressobj() @@ -1867,32 +1924,33 @@ class Reader: for data in idat: # :todo: add a max_length argument here to limit output # size. - yield array('B', d.decompress(data)) - yield array('B', d.flush()) + yield array("B", d.decompress(data)) + yield array("B", d.flush()) self.preamble() raw = iterdecomp(iteridat()) if self.interlace: - raw = array('B', itertools.chain(*raw)) - arraycode = 'BH'[self.bitdepth>8] + raw = array("B", itertools.chain(*raw)) + arraycode = "BH"[self.bitdepth > 8] # Like :meth:`group` but producing an array.array object for # each row. - pixels = imap_(lambda *row: array(arraycode, row), - *[iter(self.deinterlace(raw))]*self.width*self.planes) + pixels = map( + lambda *row: array(arraycode, row), + *[iter(self.deinterlace(raw))] * self.width * self.planes, + ) else: pixels = self.iterboxed(self.iterstraight(raw)) meta = dict() - for attr in 'greyscale alpha planes bitdepth interlace'.split(): + for attr in "greyscale alpha planes bitdepth interlace".split(): meta[attr] = getattr(self, attr) - meta['size'] = (self.width, self.height) - for attr in 'gamma transparent background'.split(): + meta["size"] = (self.width, self.height) + for attr in "gamma transparent background".split(): a = getattr(self, attr, None) if a is not None: meta[attr] = a return self.width, self.height, pixels, meta - def read_flat(self): """ Read a PNG file and decode it into flat row flat pixel format. @@ -1907,11 +1965,11 @@ class Reader: """ x, y, pixel, meta = self.read() - arraycode = 'BH'[meta['bitdepth']>8] + arraycode = "BH"[meta["bitdepth"] > 8] pixel = array(arraycode, itertools.chain(*pixel)) return x, y, pixel, meta - def palette(self, alpha='natural'): + def palette(self, alpha="natural"): """Returns a palette that is a sequence of 3-tuples or 4-tuples, synthesizing it from the ``PLTE`` and ``tRNS`` chunks. These chunks should have already been processed (for example, by @@ -1925,12 +1983,11 @@ class Reader: """ if not self.plte: - raise FormatError( - "Required PLTE chunk is missing in colour type 3 image.") - plte = group(array('B', self.plte), 3) - if self.trns or alpha == 'force': - trns = array('B', self.trns or '') - trns.extend([255]*(len(plte)-len(trns))) + raise FormatError("Required PLTE chunk is missing in colour type 3 image.") + plte = group(array("B", self.plte), 3) + if self.trns or alpha == "force": + trns = array("B", self.trns or "") + trns.extend([255] * (len(plte) - len(trns))) plte = map(operator.add, plte, group(trns, 1)) return plte @@ -1978,18 +2035,20 @@ class Reader: if not self.colormap and not self.trns and not self.sbit: return self.read() - x,y,pixels,meta = self.read() + x, y, pixels, meta = self.read() if self.colormap: - meta['colormap'] = False - meta['alpha'] = bool(self.trns) - meta['bitdepth'] = 8 - meta['planes'] = 3 + bool(self.trns) - plte = self.palette() + meta["colormap"] = False + meta["alpha"] = bool(self.trns) + meta["bitdepth"] = 8 + meta["planes"] = 3 + bool(self.trns) + plte = list(self.palette()) + def iterpal(pixels): for row in pixels: row = map(plte.__getitem__, row) - yield array('B', itertools.chain(*row)) + yield array("B", itertools.chain(*row)) + pixels = iterpal(pixels) elif self.trns: # It would be nice if there was some reasonable way of doing @@ -1999,11 +2058,12 @@ class Reader: # conversion could perhaps go faster (all those 1-tuples!), but # I still wonder whether the code proliferation is worth it) it = self.transparent - maxval = 2**meta['bitdepth']-1 - planes = meta['planes'] - meta['alpha'] = True - meta['planes'] += 1 - typecode = 'BH'[meta['bitdepth']>8] + maxval = 2 ** meta["bitdepth"] - 1 + planes = meta["planes"] + meta["alpha"] = True + meta["planes"] += 1 + typecode = "BH"[meta["bitdepth"] > 8] + def itertrns(pixels): for row in pixels: # For each row we group it into pixels, then form a @@ -2014,29 +2074,30 @@ class Reader: row = group(row, planes) opa = map(it.__ne__, row) opa = map(maxval.__mul__, opa) - opa = zip(opa) # convert to 1-tuples - yield array(typecode, - itertools.chain(*map(operator.add, row, opa))) + opa = zip(opa) # convert to 1-tuples + yield array(typecode, itertools.chain(*map(operator.add, row, opa))) + pixels = itertrns(pixels) targetbitdepth = None if self.sbit: - sbit = struct.unpack('%dB' % len(self.sbit), self.sbit) + sbit = struct.unpack(f"{len(self.sbit)}B", self.sbit) targetbitdepth = max(sbit) - if targetbitdepth > meta['bitdepth']: - raise Error('sBIT chunk %r exceeds bitdepth %d' % - (sbit,self.bitdepth)) + if targetbitdepth > meta["bitdepth"]: + raise Error("sBIT chunk %r exceeds bitdepth %d" % (sbit, self.bitdepth)) if min(sbit) <= 0: - raise Error('sBIT chunk %r has a 0-entry' % sbit) - if targetbitdepth == meta['bitdepth']: + raise Error(f"sBIT chunk {sbit!r} has a 0-entry") + if targetbitdepth == meta["bitdepth"]: targetbitdepth = None if targetbitdepth: - shift = meta['bitdepth'] - targetbitdepth - meta['bitdepth'] = targetbitdepth + shift = meta["bitdepth"] - targetbitdepth + meta["bitdepth"] = targetbitdepth + def itershift(pixels): for row in pixels: yield map(shift.__rrshift__, row) + pixels = itershift(pixels) - return x,y,pixels,meta + return x, y, pixels, meta def asFloat(self, maxval=1.0): """Return image pixels as per :meth:`asDirect` method, but scale @@ -2044,27 +2105,31 @@ class Reader: *maxval*. """ - x,y,pixels,info = self.asDirect() - sourcemaxval = 2**info['bitdepth']-1 - del info['bitdepth'] - info['maxval'] = float(maxval) - factor = float(maxval)/float(sourcemaxval) + x, y, pixels, info = self.asDirect() + sourcemaxval = 2 ** info["bitdepth"] - 1 + del info["bitdepth"] + info["maxval"] = float(maxval) + factor = float(maxval) / float(sourcemaxval) + def iterfloat(): for row in pixels: yield map(factor.__mul__, row) - return x,y,iterfloat(),info + + return x, y, iterfloat(), info def _as_rescale(self, get, targetbitdepth): """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`.""" - width,height,pixels,meta = get() - maxval = 2**meta['bitdepth'] - 1 + width, height, pixels, meta = get() + maxval = 2 ** meta["bitdepth"] - 1 targetmaxval = 2**targetbitdepth - 1 factor = float(targetmaxval) / float(maxval) - meta['bitdepth'] = targetbitdepth + meta["bitdepth"] = targetbitdepth + def iterscale(): for row in pixels: - yield map(lambda x: int(round(x*factor)), row) + yield map(lambda x: int(round(x * factor)), row) + return width, height, iterscale(), meta def asRGB8(self): @@ -2081,7 +2146,7 @@ class Reader: This function returns a 4-tuple: (*width*, *height*, *pixels*, *metadata*). *width*, *height*, *metadata* are as per the :meth:`read` method. - + *pixels* is the pixel data in boxed row flat pixel format. """ @@ -2111,20 +2176,22 @@ class Reader: ``metadata['greyscale']`` will be ``False``. """ - width,height,pixels,meta = self.asDirect() - if meta['alpha']: + width, height, pixels, meta = self.asDirect() + if meta["alpha"]: raise Error("will not convert image with alpha channel to RGB") - if not meta['greyscale']: - return width,height,pixels,meta - meta['greyscale'] = False - typecode = 'BH'[meta['bitdepth'] > 8] + if not meta["greyscale"]: + return width, height, pixels, meta + meta["greyscale"] = False + typecode = "BH"[meta["bitdepth"] > 8] + def iterrgb(): for row in pixels: a = array(typecode, [0]) * 3 * width for i in range(3): a[i::3] = row yield a - return width,height,iterrgb(),meta + + return width, height, iterrgb(), meta def asRGBA(self): """Return image as RGBA pixels. Greyscales are expanded into @@ -2136,14 +2203,16 @@ class Reader: ``metadata['alpha']`` will be ``True``. """ - width,height,pixels,meta = self.asDirect() - if meta['alpha'] and not meta['greyscale']: - return width,height,pixels,meta - typecode = 'BH'[meta['bitdepth'] > 8] - maxval = 2**meta['bitdepth'] - 1 + width, height, pixels, meta = self.asDirect() + if meta["alpha"] and not meta["greyscale"]: + return width, height, pixels, meta + typecode = "BH"[meta["bitdepth"] > 8] + maxval = 2 ** meta["bitdepth"] - 1 + def newarray(): return array(typecode, [0]) * 4 * width - if meta['alpha'] and meta['greyscale']: + + if meta["alpha"] and meta["greyscale"]: # LA to RGBA def convert(): for row in pixels: @@ -2155,7 +2224,8 @@ class Reader: a[i::4] = row[0::2] a[3::4] = row[1::2] yield a - elif meta['greyscale']: + + elif meta["greyscale"]: # L to RGBA def convert(): for row in pixels: @@ -2164,8 +2234,10 @@ class Reader: a[i::4] = row a[3::4] = array(typecode, [maxval]) * width yield a + else: - assert not meta['alpha'] and not meta['greyscale'] + assert not meta["alpha"] and not meta["greyscale"] + # RGB to RGBA def convert(): for row in pixels: @@ -2174,9 +2246,10 @@ class Reader: a[i::4] = row[i::3] a[3::4] = array(typecode, [maxval]) * width yield a - meta['alpha'] = True - meta['greyscale'] = False - return width,height,convert(),meta + + meta["alpha"] = True + meta["greyscale"] = False + return width, height, convert(), meta # === Internal Test Support === @@ -2206,6 +2279,7 @@ import unittest def test(): unittest.main(__name__) + def topngbytes(name, rows, x, y, **k): """Convenience function for creating a PNG file "in memory" as a string. Creates a :class:`Writer` instance using the keyword arguments, @@ -2216,16 +2290,17 @@ def topngbytes(name, rows, x, y, **k): import os - print (name) + print(name) f = BytesIO() w = Writer(x, y, **k) w.write(f, rows) - if os.environ.get('PYPNG_TEST_TMP'): - w = open(name, 'wb') + if os.environ.get("PYPNG_TEST_TMP"): + w = open(name, "wb") w.write(f.getvalue()) w.close() return f.getvalue() + def testWithIO(inp, out, f): """Calls the function `f` with ``sys.stdin`` changed to `inp` and ``sys.stdout`` changed to `out`. They are restored when `f` @@ -2235,20 +2310,21 @@ def testWithIO(inp, out, f): import os try: - oldin,sys.stdin = sys.stdin,inp - oldout,sys.stdout = sys.stdout,out + oldin, sys.stdin = sys.stdin, inp + oldout, sys.stdout = sys.stdout, out x = f() finally: sys.stdin = oldin sys.stdout = oldout - if os.environ.get('PYPNG_TEST_TMP') and hasattr(out,'getvalue'): + if os.environ.get("PYPNG_TEST_TMP") and hasattr(out, "getvalue"): name = mycallersname() if name: - w = open(name+'.png', 'wb') + w = open(name + ".png", "wb") w.write(out.getvalue()) w.close() return x + def mycallersname(): """Returns the name of the caller of the caller of this function (hence the name of the caller of the function in which @@ -2261,16 +2337,19 @@ def mycallersname(): frame = inspect.currentframe() if not frame: return None - frame_,filename_,lineno_,funname,linelist_,listi_ = ( - inspect.getouterframes(frame)[2]) + frame_, filename_, lineno_, funname, linelist_, listi_ = inspect.getouterframes( + frame + )[2] return funname + def seqtobytes(s): """Convert a sequence of integers to a *bytes* instance. Good for plastering over Python 2 / Python 3 cracks. """ - return strtobytes(''.join(chr(x) for x in s)) + return strtobytes("".join(chr(x) for x in s)) + class Test(unittest.TestCase): # This member is used by the superclass. If we don't define a new @@ -2287,100 +2366,108 @@ class Test(unittest.TestCase): # tested. Making it a test for Issue 20. w = Writer(15, 17, greyscale=True, bitdepth=n, chunk_limit=99) f = BytesIO() - w.write_array(f, array('B', map(mask.__and__, range(1, 256)))) + w.write_array(f, array("B", map(mask.__and__, range(1, 256)))) r = Reader(bytes=f.getvalue()) - x,y,pixels,meta = r.read() + x, y, pixels, meta = r.read() self.assertEqual(x, 15) self.assertEqual(y, 17) - self.assertEqual(list(itertools.chain(*pixels)), - map(mask.__and__, range(1,256))) + self.assertEqual( + list(itertools.chain(*pixels)), map(mask.__and__, range(1, 256)) + ) + def testL8(self): return self.helperLN(8) + def testL4(self): return self.helperLN(4) + def testL2(self): "Also tests asRGB8." w = Writer(1, 4, greyscale=True, bitdepth=2) f = BytesIO() - w.write_array(f, array('B', range(4))) + w.write_array(f, array("B", range(4))) r = Reader(bytes=f.getvalue()) - x,y,pixels,meta = r.asRGB8() + x, y, pixels, meta = r.asRGB8() self.assertEqual(x, 1) self.assertEqual(y, 4) - for i,row in enumerate(pixels): + for i, row in enumerate(pixels): self.assertEqual(len(row), 3) - self.assertEqual(list(row), [0x55*i]*3) + self.assertEqual(list(row), [0x55 * i] * 3) + def testP2(self): "2-bit palette." - a = (255,255,255) - b = (200,120,120) - c = (50,99,50) - w = Writer(1, 4, bitdepth=2, palette=[a,b,c]) + a = (255, 255, 255) + b = (200, 120, 120) + c = (50, 99, 50) + w = Writer(1, 4, bitdepth=2, palette=[a, b, c]) f = BytesIO() - w.write_array(f, array('B', (0,1,1,2))) + w.write_array(f, array("B", (0, 1, 1, 2))) r = Reader(bytes=f.getvalue()) - x,y,pixels,meta = r.asRGB8() + x, y, pixels, meta = r.asRGB8() self.assertEqual(x, 1) self.assertEqual(y, 4) self.assertEqual(list(pixels), map(list, [a, b, b, c])) + def testPtrns(self): "Test colour type 3 and tRNS chunk (and 4-bit palette)." - a = (50,99,50,50) - b = (200,120,120,80) - c = (255,255,255) - d = (200,120,120) - e = (50,99,50) - w = Writer(3, 3, bitdepth=4, palette=[a,b,c,d,e]) + a = (50, 99, 50, 50) + b = (200, 120, 120, 80) + c = (255, 255, 255) + d = (200, 120, 120) + e = (50, 99, 50) + w = Writer(3, 3, bitdepth=4, palette=[a, b, c, d, e]) f = BytesIO() - w.write_array(f, array('B', (4, 3, 2, 3, 2, 0, 2, 0, 1))) + w.write_array(f, array("B", (4, 3, 2, 3, 2, 0, 2, 0, 1))) r = Reader(bytes=f.getvalue()) - x,y,pixels,meta = r.asRGBA8() + x, y, pixels, meta = r.asRGBA8() self.assertEqual(x, 3) self.assertEqual(y, 3) - c = c+(255,) - d = d+(255,) - e = e+(255,) - boxed = [(e,d,c),(d,c,a),(c,a,b)] + c = c + (255,) + d = d + (255,) + e = e + (255,) + boxed = [(e, d, c), (d, c, a), (c, a, b)] flat = map(lambda row: itertools.chain(*row), boxed) self.assertEqual(map(list, pixels), map(list, flat)) + def testRGBtoRGBA(self): - "asRGBA8() on colour type 2 source.""" + "asRGBA8() on colour type 2 source." "" # Test for Issue 26 - r = Reader(bytes=_pngsuite['basn2c08']) - x,y,pixels,meta = r.asRGBA8() + r = Reader(bytes=_pngsuite["basn2c08"]) + x, y, pixels, meta = r.asRGBA8() # Test the pixels at row 9 columns 0 and 1. row9 = list(pixels)[9] - self.assertEqual(row9[0:8], - [0xff, 0xdf, 0xff, 0xff, 0xff, 0xde, 0xff, 0xff]) + self.assertEqual(row9[0:8], [0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDE, 0xFF, 0xFF]) + def testLtoRGBA(self): - "asRGBA() on grey source.""" + "asRGBA() on grey source." "" # Test for Issue 60 - r = Reader(bytes=_pngsuite['basi0g08']) - x,y,pixels,meta = r.asRGBA() + r = Reader(bytes=_pngsuite["basi0g08"]) + x, y, pixels, meta = r.asRGBA() row9 = list(list(pixels)[9]) - self.assertEqual(row9[0:8], - [222, 222, 222, 255, 221, 221, 221, 255]) + self.assertEqual(row9[0:8], [222, 222, 222, 255, 221, 221, 221, 255]) + def testCtrns(self): "Test colour type 2 and tRNS chunk." # Test for Issue 25 - r = Reader(bytes=_pngsuite['tbrn2c08']) - x,y,pixels,meta = r.asRGBA8() + r = Reader(bytes=_pngsuite["tbrn2c08"]) + x, y, pixels, meta = r.asRGBA8() # I just happen to know that the first pixel is transparent. # In particular it should be #7f7f7f00 row0 = list(pixels)[0] - self.assertEqual(tuple(row0[0:4]), (0x7f, 0x7f, 0x7f, 0x00)) + self.assertEqual(tuple(row0[0:4]), (0x7F, 0x7F, 0x7F, 0x00)) + def testAdam7read(self): """Adam7 interlace reading. Specifically, test that for images in the PngSuite that have both an interlaced and straightlaced pair that both images from the pair produce the same array of pixels.""" for candidate in _pngsuite: - if not candidate.startswith('basn'): + if not candidate.startswith("basn"): continue - candi = candidate.replace('n', 'i') + candi = candidate.replace("n", "i") if candi not in _pngsuite: continue - print ('adam7 read %s' % (candidate,)) + print(f"adam7 read {candidate}") straight = Reader(bytes=_pngsuite[candidate]) adam7 = Reader(bytes=_pngsuite[candi]) # Just compare the pixels. Ignore x,y (because they're @@ -2389,6 +2476,7 @@ class Test(unittest.TestCase): straight = straight.read()[2] adam7 = adam7.read()[2] self.assertEqual(map(list, straight), map(list, adam7)) + def testAdam7write(self): """Adam7 interlace writing. For each test image in the PngSuite, write an interlaced @@ -2397,51 +2485,73 @@ class Test(unittest.TestCase): # Not such a great test, because the only way we can check what # we have written is to read it back again. - for name,bytes in _pngsuite.items(): + for name, bytes in _pngsuite.items(): # Only certain colour types supported for this test. - if name[3:5] not in ['n0', 'n2', 'n4', 'n6']: + if name[3:5] not in ["n0", "n2", "n4", "n6"]: continue it = Reader(bytes=bytes) - x,y,pixels,meta = it.read() - pngi = topngbytes('adam7wn'+name+'.png', pixels, - x=x, y=y, bitdepth=it.bitdepth, - greyscale=it.greyscale, alpha=it.alpha, - transparent=it.transparent, - interlace=False) - x,y,ps,meta = Reader(bytes=pngi).read() + x, y, pixels, meta = it.read() + pngi = topngbytes( + f"adam7wn{name}.png", + pixels, + x=x, + y=y, + bitdepth=it.bitdepth, + greyscale=it.greyscale, + alpha=it.alpha, + transparent=it.transparent, + interlace=False, + ) + x, y, ps, meta = Reader(bytes=pngi).read() it = Reader(bytes=bytes) - x,y,pixels,meta = it.read() - pngs = topngbytes('adam7wi'+name+'.png', pixels, - x=x, y=y, bitdepth=it.bitdepth, - greyscale=it.greyscale, alpha=it.alpha, - transparent=it.transparent, - interlace=True) - x,y,pi,meta = Reader(bytes=pngs).read() + x, y, pixels, meta = it.read() + pngs = topngbytes( + f"adam7wi{name}.png", + pixels, + x=x, + y=y, + bitdepth=it.bitdepth, + greyscale=it.greyscale, + alpha=it.alpha, + transparent=it.transparent, + interlace=True, + ) + x, y, pi, meta = Reader(bytes=pngs).read() self.assertEqual(map(list, ps), map(list, pi)) + def testPGMin(self): """Test that the command line tool can read PGM files.""" + def do(): - return _main(['testPGMin']) + return _main(["testPGMin"]) + s = BytesIO() - s.write(strtobytes('P5 2 2 3\n')) - s.write(strtobytes('\x00\x01\x02\x03')) + s.write(strtobytes("P5 2 2 3\n")) + s.write(strtobytes("\x00\x01\x02\x03")) s.flush() s.seek(0) o = BytesIO() testWithIO(s, o, do) r = Reader(bytes=o.getvalue()) - x,y,pixels,meta = r.read() + x, y, pixels, meta = r.read() self.assertTrue(r.greyscale) self.assertEqual(r.bitdepth, 2) + def testPAMin(self): """Test that the command line tool can read PAM file.""" + def do(): - return _main(['testPAMin']) + return _main(["testPAMin"]) + s = BytesIO() - s.write(strtobytes('P7\nWIDTH 3\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\n' - 'TUPLTYPE RGB_ALPHA\nENDHDR\n')) + s.write( + strtobytes( + "P7\nWIDTH 3\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\n" + "TUPLTYPE RGB_ALPHA\nENDHDR\n" + ) + ) # The pixels in flat row flat pixel format - flat = [255,0,0,255, 0,255,0,120, 0,0,255,30] + flat = [255, 0, 0, 255, 0, 255, 0, 120, 0, 0, 255, 30] asbytes = seqtobytes(flat) s.write(asbytes) s.flush() @@ -2449,55 +2559,65 @@ class Test(unittest.TestCase): o = BytesIO() testWithIO(s, o, do) r = Reader(bytes=o.getvalue()) - x,y,pixels,meta = r.read() + x, y, pixels, meta = r.read() self.assertTrue(r.alpha) self.assertTrue(not r.greyscale) self.assertEqual(list(itertools.chain(*pixels)), flat) + def testLA4(self): """Create an LA image with bitdepth 4.""" - bytes = topngbytes('la4.png', [[5, 12]], 1, 1, - greyscale=True, alpha=True, bitdepth=4) - sbit = Reader(bytes=bytes).chunk('sBIT')[1] - self.assertEqual(sbit, strtobytes('\x04\x04')) + bytes = topngbytes( + "la4.png", [[5, 12]], 1, 1, greyscale=True, alpha=True, bitdepth=4 + ) + sbit = Reader(bytes=bytes).chunk("sBIT")[1] + self.assertEqual(sbit, strtobytes("\x04\x04")) + def testPNMsbit(self): """Test that PNM files can generates sBIT chunk.""" + def do(): - return _main(['testPNMsbit']) + return _main(["testPNMsbit"]) + s = BytesIO() - s.write(strtobytes('P6 8 1 1\n')) + s.write(strtobytes("P6 8 1 1\n")) for pixel in range(8): - s.write(struct.pack(' 255: - a = array('H') + a = array("H") else: - a = array('B') + a = array("B") fw = float(width) fh = float(height) pfun = test_patterns[pattern] for y in range(height): - fy = float(y)/fh + fy = float(y) / fh for x in range(width): - a.append(int(round(pfun(float(x)/fw, fy) * maxval))) + a.append(int(round(pfun(float(x) / fw, fy) * maxval))) return a - def test_rgba(size=256, bitdepth=8, - red="GTB", green="GLR", blue="RTL", alpha=None): + def test_rgba(size=256, bitdepth=8, red="GTB", green="GLR", blue="RTL", alpha=None): """ Create a test image. Each channel is generated from the specified pattern; any channel apart from red can be set to @@ -3282,20 +3531,24 @@ def test_suite(options, args): """ if name not in _pngsuite: - raise NotImplementedError("cannot find PngSuite file %s (use -L for a list)" % name) + raise NotImplementedError( + f"cannot find PngSuite file {name} (use -L for a list)" + ) r = Reader(bytes=_pngsuite[name]) - w,h,pixels,meta = r.asDirect() + w, h, pixels, meta = r.asDirect() assert w == h # LAn for n < 8 is a special case for which we need to rescale # the data. - if meta['greyscale'] and meta['alpha'] and meta['bitdepth'] < 8: - factor = 255 // (2**meta['bitdepth']-1) + if meta["greyscale"] and meta["alpha"] and meta["bitdepth"] < 8: + factor = 255 // (2 ** meta["bitdepth"] - 1) + def rescale(data): for row in data: yield map(factor.__mul__, row) + pixels = rescale(pixels) - meta['bitdepth'] = 8 - arraycode = 'BH'[meta['bitdepth']>8] + meta["bitdepth"] = 8 + arraycode = "BH"[meta["bitdepth"] > 8] return w, array(arraycode, itertools.chain(*pixels)), meta # The body of test_suite() @@ -3303,7 +3556,7 @@ def test_suite(options, args): if options.test_size: size = options.test_size options.bitdepth = options.test_depth - options.greyscale=bool(options.test_black) + options.greyscale = bool(options.test_black) kwargs = {} if options.test_red: @@ -3316,7 +3569,9 @@ def test_suite(options, args): kwargs["alpha"] = options.test_alpha if options.greyscale: if options.test_red or options.test_green or options.test_blue: - raise ValueError("cannot specify colours (R, G, B) when greyscale image (black channel, K) is specified") + raise ValueError( + "cannot specify colours (R, G, B) when greyscale image (black channel, K) is specified" + ) kwargs["red"] = options.test_black kwargs["green"] = None kwargs["blue"] = None @@ -3324,63 +3579,64 @@ def test_suite(options, args): if not args: pixels = test_rgba(size, options.bitdepth, **kwargs) else: - size,pixels,meta = pngsuite_image(args[0]) - for k in ['bitdepth', 'alpha', 'greyscale']: + size, pixels, meta = pngsuite_image(args[0]) + for k in ["bitdepth", "alpha", "greyscale"]: setattr(options, k, meta[k]) - writer = Writer(size, size, - bitdepth=options.bitdepth, - transparent=options.transparent, - background=options.background, - gamma=options.gamma, - greyscale=options.greyscale, - alpha=options.alpha, - compression=options.compression, - interlace=options.interlace) + writer = Writer( + size, + size, + bitdepth=options.bitdepth, + transparent=options.transparent, + background=options.background, + gamma=options.gamma, + greyscale=options.greyscale, + alpha=options.alpha, + compression=options.compression, + interlace=options.interlace, + ) writer.write_array(sys.stdout, pixels) + def read_pam_header(infile): """ Read (the rest of a) PAM header. `infile` should be positioned immediately after the initial 'P7' line (at the beginning of the second line). Returns are as for `read_pnm_header`. """ - + # Unlike PBM, PGM, and PPM, we can read the header a line at a time. header = dict() while True: l = infile.readline().strip() - if l == strtobytes('ENDHDR'): + if l == strtobytes("ENDHDR"): break if not l: - raise EOFError('PAM ended prematurely') - if l[0] == strtobytes('#'): + raise EOFError("PAM ended prematurely") + if l[0] == strtobytes("#"): continue l = l.split(None, 1) if l[0] not in header: header[l[0]] = l[1] else: - header[l[0]] += strtobytes(' ') + l[1] + header[l[0]] += strtobytes(" ") + l[1] - required = ['WIDTH', 'HEIGHT', 'DEPTH', 'MAXVAL'] + required = ["WIDTH", "HEIGHT", "DEPTH", "MAXVAL"] required = [strtobytes(x) for x in required] - WIDTH,HEIGHT,DEPTH,MAXVAL = required + WIDTH, HEIGHT, DEPTH, MAXVAL = required present = [x for x in required if x in header] if len(present) != len(required): - raise Error('PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL') + raise Error("PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL") width = int(header[WIDTH]) height = int(header[HEIGHT]) depth = int(header[DEPTH]) maxval = int(header[MAXVAL]) - if (width <= 0 or - height <= 0 or - depth <= 0 or - maxval <= 0): - raise Error( - 'WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers') - return 'P7', width, height, depth, maxval - -def read_pnm_header(infile, supported=('P5','P6')): + if width <= 0 or height <= 0 or depth <= 0 or maxval <= 0: + raise Error("WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers") + return "P7", width, height, depth, maxval + + +def read_pnm_header(infile, supported=("P5", "P6")): """ Read a PNM header, returning (format,width,height,depth,maxval). `width` and `height` are in pixels. `depth` is the number of @@ -3399,13 +3655,13 @@ def read_pnm_header(infile, supported=('P5','P6')): # is acceptable. type = infile.read(3).rstrip() if type not in supported: - raise NotImplementedError('file format %s not supported' % type) - if type == strtobytes('P7'): + raise NotImplementedError(f"file format {type} not supported") + if type == strtobytes("P7"): # PAM header parsing is completely different. return read_pam_header(infile) # Expected number of tokens in header (3 for P4, 4 for P6) expected = 4 - pbm = ('P1', 'P4') + pbm = ("P1", "P4") if type in pbm: expected = 3 header = [type] @@ -3418,7 +3674,7 @@ def read_pnm_header(infile, supported=('P5','P6')): def getc(): c = infile.read(1) if not c: - raise Error('premature EOF reading PNM header') + raise Error("premature EOF reading PNM header") return c c = getc() @@ -3427,17 +3683,17 @@ def read_pnm_header(infile, supported=('P5','P6')): while c.isspace(): c = getc() # Skip comments. - while c == '#': - while c not in '\n\r': + while c == "#": + while c not in "\n\r": c = getc() if not c.isdigit(): - raise Error('unexpected character %s found in header' % c) + raise Error(f"unexpected character {c} found in header") # According to the specification it is legal to have comments # that appear in the middle of a token. # This is bonkers; I've never seen it; and it's a bit awkward to # code good lexers in Python (no goto). So we break on such # cases. - token = strtobytes('') + token = strtobytes("") while c.isdigit(): token += c c = getc() @@ -3447,79 +3703,76 @@ def read_pnm_header(infile, supported=('P5','P6')): if len(header) == expected: break # Skip comments (again) - while c == '#': - while c not in '\n\r': + while c == "#": + while c not in "\n\r": c = getc() if not c.isspace(): - raise Error('expected header to end with whitespace, not %s' % c) + raise Error(f"expected header to end with whitespace, not {c}") if type in pbm: # synthesize a MAXVAL header.append(1) - depth = (1,3)[type == strtobytes('P6')] + depth = (1, 3)[type == strtobytes("P6")] return header[0], header[1], header[2], depth, header[3] + def write_pnm(file, width, height, pixels, meta): """Write a Netpbm PNM/PAM file.""" - bitdepth = meta['bitdepth'] + bitdepth = meta["bitdepth"] maxval = 2**bitdepth - 1 # Rudely, the number of image planes can be used to determine # whether we are L (PGM), LA (PAM), RGB (PPM), or RGBA (PAM). - planes = meta['planes'] + planes = meta["planes"] # Can be an assert as long as we assume that pixels and meta came # from a PNG file. - assert planes in (1,2,3,4) - if planes in (1,3): + assert planes in (1, 2, 3, 4) + if planes in (1, 3): if 1 == planes: # PGM # Could generate PBM if maxval is 1, but we don't (for one # thing, we'd have to convert the data, not just blat it # out). - fmt = 'P5' + fmt = "P5" else: # PPM - fmt = 'P6' - file.write('%s %d %d %d\n' % (fmt, width, height, maxval)) - if planes in (2,4): + fmt = "P6" + file.write("%s %d %d %d\n" % (fmt, width, height, maxval)) + if planes in (2, 4): # PAM # See http://netpbm.sourceforge.net/doc/pam.html if 2 == planes: - tupltype = 'GRAYSCALE_ALPHA' + tupltype = "GRAYSCALE_ALPHA" else: - tupltype = 'RGB_ALPHA' - file.write('P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n' - 'TUPLTYPE %s\nENDHDR\n' % - (width, height, planes, maxval, tupltype)) + tupltype = "RGB_ALPHA" + file.write( + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n" + "TUPLTYPE %s\nENDHDR\n" % (width, height, planes, maxval, tupltype) + ) # Values per row vpr = planes * width # struct format - fmt = '>%d' % vpr - if maxval > 0xff: - fmt = fmt + 'H' + fmt = ">%d" % vpr + if maxval > 0xFF: + fmt = fmt + "H" else: - fmt = fmt + 'B' + fmt = fmt + "B" for row in pixels: file.write(struct.pack(fmt, *row)) file.flush() + def color_triple(color): """ Convert a command line colour value to a RGB triple of integers. FIXME: Somewhere we need support for greyscale backgrounds etc. """ - if color.startswith('#') and len(color) == 4: - return (int(color[1], 16), - int(color[2], 16), - int(color[3], 16)) - if color.startswith('#') and len(color) == 7: - return (int(color[1:3], 16), - int(color[3:5], 16), - int(color[5:7], 16)) - elif color.startswith('#') and len(color) == 13: - return (int(color[1:5], 16), - int(color[5:9], 16), - int(color[9:13], 16)) + if color.startswith("#") and len(color) == 4: + return (int(color[1], 16), int(color[2], 16), int(color[3], 16)) + if color.startswith("#") and len(color) == 7: + return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)) + elif color.startswith("#") and len(color) == 13: + return (int(color[1:5], 16), int(color[5:9], 16), int(color[9:13], 16)) def _main(argv): @@ -3530,58 +3783,135 @@ def _main(argv): # Parse command line arguments from optparse import OptionParser import re - version = '%prog ' + re.sub(r'( ?\$|URL: |Rev:)', '', __version__) + + version = "%prog " + re.sub(r"( ?\$|URL: |Rev:)", "", __version__) parser = OptionParser(version=version) parser.set_usage("%prog [options] [imagefile]") - parser.add_option('-r', '--read-png', default=False, - action='store_true', - help='Read PNG, write PNM') - parser.add_option("-i", "--interlace", - default=False, action="store_true", - help="create an interlaced PNG file (Adam7)") - parser.add_option("-t", "--transparent", - action="store", type="string", metavar="color", - help="mark the specified colour (#RRGGBB) as transparent") - parser.add_option("-b", "--background", - action="store", type="string", metavar="color", - help="save the specified background colour") - parser.add_option("-a", "--alpha", - action="store", type="string", metavar="pgmfile", - help="alpha channel transparency (RGBA)") - parser.add_option("-g", "--gamma", - action="store", type="float", metavar="value", - help="save the specified gamma value") - parser.add_option("-c", "--compression", - action="store", type="int", metavar="level", - help="zlib compression level (0-9)") - parser.add_option("-T", "--test", - default=False, action="store_true", - help="create a test image (a named PngSuite image if an argument is supplied)") - parser.add_option('-L', '--list', - default=False, action='store_true', - help="print list of named test images") - parser.add_option("-R", "--test-red", - action="store", type="string", metavar="pattern", - help="test pattern for the red image layer") - parser.add_option("-G", "--test-green", - action="store", type="string", metavar="pattern", - help="test pattern for the green image layer") - parser.add_option("-B", "--test-blue", - action="store", type="string", metavar="pattern", - help="test pattern for the blue image layer") - parser.add_option("-A", "--test-alpha", - action="store", type="string", metavar="pattern", - help="test pattern for the alpha image layer") - parser.add_option("-K", "--test-black", - action="store", type="string", metavar="pattern", - help="test pattern for greyscale image") - parser.add_option("-d", "--test-depth", - default=8, action="store", type="int", - metavar='NBITS', - help="create test PNGs that are NBITS bits per channel") - parser.add_option("-S", "--test-size", - action="store", type="int", metavar="size", - help="width and height of the test image") + parser.add_option( + "-r", + "--read-png", + default=False, + action="store_true", + help="Read PNG, write PNM", + ) + parser.add_option( + "-i", + "--interlace", + default=False, + action="store_true", + help="create an interlaced PNG file (Adam7)", + ) + parser.add_option( + "-t", + "--transparent", + action="store", + type="string", + metavar="color", + help="mark the specified colour (#RRGGBB) as transparent", + ) + parser.add_option( + "-b", + "--background", + action="store", + type="string", + metavar="color", + help="save the specified background colour", + ) + parser.add_option( + "-a", + "--alpha", + action="store", + type="string", + metavar="pgmfile", + help="alpha channel transparency (RGBA)", + ) + parser.add_option( + "-g", + "--gamma", + action="store", + type="float", + metavar="value", + help="save the specified gamma value", + ) + parser.add_option( + "-c", + "--compression", + action="store", + type="int", + metavar="level", + help="zlib compression level (0-9)", + ) + parser.add_option( + "-T", + "--test", + default=False, + action="store_true", + help="create a test image (a named PngSuite image if an argument is supplied)", + ) + parser.add_option( + "-L", + "--list", + default=False, + action="store_true", + help="print list of named test images", + ) + parser.add_option( + "-R", + "--test-red", + action="store", + type="string", + metavar="pattern", + help="test pattern for the red image layer", + ) + parser.add_option( + "-G", + "--test-green", + action="store", + type="string", + metavar="pattern", + help="test pattern for the green image layer", + ) + parser.add_option( + "-B", + "--test-blue", + action="store", + type="string", + metavar="pattern", + help="test pattern for the blue image layer", + ) + parser.add_option( + "-A", + "--test-alpha", + action="store", + type="string", + metavar="pattern", + help="test pattern for the alpha image layer", + ) + parser.add_option( + "-K", + "--test-black", + action="store", + type="string", + metavar="pattern", + help="test pattern for greyscale image", + ) + parser.add_option( + "-d", + "--test-depth", + default=8, + action="store", + type="int", + metavar="NBITS", + help="create test PNGs that are NBITS bits per channel", + ) + parser.add_option( + "-S", + "--test-size", + action="store", + type="int", + metavar="size", + help="width and height of the test image", + ) (options, args) = parser.parse_args(args=argv[1:]) # Convert options @@ -3594,7 +3924,7 @@ def _main(argv): names = list(_pngsuite) names.sort() for name in names: - print (name) + print(name) return # Run regression tests @@ -3603,11 +3933,11 @@ def _main(argv): # Prepare input and output files if len(args) == 0: - infilename = '-' + infilename = "-" infile = sys.stdin elif len(args) == 1: infilename = args[0] - infile = open(infilename, 'rb') + infile = open(infilename, "rb") else: parser.error("more than one input file") outfile = sys.stdout @@ -3615,12 +3945,13 @@ def _main(argv): if options.read_png: # Encode PNG to PPM png = Reader(file=infile) - width,height,pixels,meta = png.asDirect() - write_pnm(outfile, width, height, pixels, meta) + width, height, pixels, meta = png.asDirect() + write_pnm(outfile, width, height, pixels, meta) else: # Encode PNM to PNG - format, width, height, depth, maxval = \ - read_pnm_header(infile, ('P5','P6','P7')) + format, width, height, depth, maxval = read_pnm_header( + infile, ("P5", "P6", "P7") + ) # When it comes to the variety of input formats, we do something # rather rude. Observe that L, LA, RGB, RGBA are the 4 colour # types supported by PNG and that they correspond to 1, 2, 3, 4 @@ -3628,44 +3959,47 @@ def _main(argv): # the source image to determine which one we have. We do not # care about TUPLTYPE. greyscale = depth <= 2 - pamalpha = depth in (2,4) - supported = map(lambda x: 2**x-1, range(1,17)) + pamalpha = depth in (2, 4) + supported = map(lambda x: 2**x - 1, range(1, 17)) try: mi = supported.index(maxval) except ValueError: raise NotImplementedError( - 'your maxval (%s) not in supported list %s' % - (maxval, str(supported))) - bitdepth = mi+1 - writer = Writer(width, height, - greyscale=greyscale, - bitdepth=bitdepth, - interlace=options.interlace, - transparent=options.transparent, - background=options.background, - alpha=bool(pamalpha or options.alpha), - gamma=options.gamma, - compression=options.compression) + f"your maxval ({maxval}) not in supported list {str(supported)}" + ) + bitdepth = mi + 1 + writer = Writer( + width, + height, + greyscale=greyscale, + bitdepth=bitdepth, + interlace=options.interlace, + transparent=options.transparent, + background=options.background, + alpha=bool(pamalpha or options.alpha), + gamma=options.gamma, + compression=options.compression, + ) if options.alpha: - pgmfile = open(options.alpha, 'rb') - format, awidth, aheight, adepth, amaxval = \ - read_pnm_header(pgmfile, 'P5') - if amaxval != '255': + pgmfile = open(options.alpha, "rb") + format, awidth, aheight, adepth, amaxval = read_pnm_header(pgmfile, "P5") + if amaxval != "255": raise NotImplementedError( - 'maxval %s not supported for alpha channel' % amaxval) + f"maxval {amaxval} not supported for alpha channel" + ) if (awidth, aheight) != (width, height): - raise ValueError("alpha channel image size mismatch" - " (%s has %sx%s but %s has %sx%s)" - % (infilename, width, height, - options.alpha, awidth, aheight)) + raise ValueError( + "alpha channel image size mismatch" + " (%s has %sx%s but %s has %sx%s)" + % (infilename, width, height, options.alpha, awidth, aheight) + ) writer.convert_ppm_and_pgm(infile, pgmfile, outfile) else: writer.convert_pnm(infile, outfile) -if __name__ == '__main__': +if __name__ == "__main__": try: _main(sys.argv) - except Error: - e = geterror() - sys.stderr.write("%s\n" % (e,)) + except Error as e: + sys.stderr.write(f"{e}\n") diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py b/venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py index a1261cff8f450ededb98fc7d50ee919708a7709b..60968d53c7366e39f9adf9318d9cef754baa5107 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/run_tests.py @@ -1,22 +1,29 @@ import sys -if __name__ == '__main__': - sys.exit("This module is for import only") - -test_pkg_name = '.'.join(__name__.split('.')[0:-2]) -is_pygame_pkg = test_pkg_name == 'pygame.tests' -test_runner_mod = test_pkg_name + '.test_utils.test_runner' +if __name__ == "__main__": + raise RuntimeError("This module is for import only") +test_pkg_name = ".".join(__name__.split(".")[0:-2]) +is_pygame_pkg = test_pkg_name == "pygame.tests" +test_runner_mod = test_pkg_name + ".test_utils.test_runner" if is_pygame_pkg: from pygame.tests.test_utils import import_submodule - from pygame.tests.test_utils.test_runner \ - import prepare_test_env, run_test, combine_results, \ - get_test_results, TEST_RESULTS_START + from pygame.tests.test_utils.test_runner import ( + prepare_test_env, + run_test, + combine_results, + get_test_results, + TEST_RESULTS_START, + ) else: from test.test_utils import import_submodule - from test.test_utils.test_runner \ - import prepare_test_env, run_test, combine_results, \ - get_test_results, TEST_RESULTS_START + from test.test_utils.test_runner import ( + prepare_test_env, + run_test, + combine_results, + get_test_results, + TEST_RESULTS_START, + ) import pygame import pygame.threads @@ -30,6 +37,7 @@ from pprint import pformat was_run = False + def run(*args, **kwds): """Run the Pygame unit test suite and return (total tests run, fails dict) @@ -60,7 +68,7 @@ def run(*args, **kwds): Pygame tests python - the path to a python executable to run subprocessed tests (default sys.executable) - interative - allow tests tagged 'interative'. + interactive - allow tests tagged 'interactive'. Return value: A tuple of total number of tests run, dictionary of error information. The @@ -101,67 +109,59 @@ def run(*args, **kwds): was_run = True options = kwds.copy() - option_usesubprocess = options.get('usesubprocess', False) - option_dump = options.pop('dump', False) - option_file = options.pop('file', None) - option_randomize = options.get('randomize', False) - option_seed = options.get('seed', None) - option_multi_thread = options.pop('multi_thread', 1) - option_time_out = options.pop('time_out', 120) - option_fake = options.pop('fake', None) - option_python = options.pop('python', sys.executable) - option_exclude = options.pop('exclude', ()) - option_interactive = options.pop('interactive', False) - - if not option_interactive and 'interactive' not in option_exclude: - option_exclude += ('interactive',) - if option_usesubprocess and 'subprocess_ignore' not in option_exclude: - option_exclude += ('subprocess_ignore',) - elif 'ignore' not in option_exclude: - option_exclude += ('ignore',) - if sys.version_info < (3, 0, 0): - option_exclude += ('python2_ignore',) - else: - option_exclude += ('python3_ignore',) + option_usesubprocess = options.get("usesubprocess", False) + option_dump = options.pop("dump", False) + option_file = options.pop("file", None) + option_randomize = options.get("randomize", False) + option_seed = options.get("seed", None) + option_multi_thread = options.pop("multi_thread", 1) + option_time_out = options.pop("time_out", 120) + option_fake = options.pop("fake", None) + option_python = options.pop("python", sys.executable) + option_exclude = options.pop("exclude", ()) + option_interactive = options.pop("interactive", False) + + if not option_interactive and "interactive" not in option_exclude: + option_exclude += ("interactive",) + if option_usesubprocess and "subprocess_ignore" not in option_exclude: + option_exclude += ("subprocess_ignore",) + elif "ignore" not in option_exclude: + option_exclude += ("ignore",) + + option_exclude += ("python3_ignore",) + option_exclude += ("SDL2_ignore",) - if pygame.get_sdl_version() < (2, 0, 0): - option_exclude += ('SDL1_ignore',) - else: - option_exclude += ('SDL2_ignore',) main_dir, test_subdir, fake_test_subdir = prepare_test_env() ########################################################################### # Compile a list of test modules. If fake, then compile list of fake # xxxx_test.py from run_tests__tests - TEST_MODULE_RE = re.compile('^(.+_test)\.py$') + TEST_MODULE_RE = re.compile(r"^(.+_test)\.py$") test_mods_pkg_name = test_pkg_name working_dir_temp = tempfile.mkdtemp() if option_fake is not None: - test_mods_pkg_name = '.'.join([test_mods_pkg_name, - 'run_tests__tests', - option_fake]) + test_mods_pkg_name = ".".join( + [test_mods_pkg_name, "run_tests__tests", option_fake] + ) test_subdir = os.path.join(fake_test_subdir, option_fake) working_dir = test_subdir else: working_dir = working_dir_temp - # Added in because some machines will need os.environ else there will be # false failures in subprocess mode. Same issue as python2.6. Needs some # env vars. test_env = os.environ - fmt1 = '%s.%%s' % test_mods_pkg_name - fmt2 = '%s.%%s_test' % test_mods_pkg_name + fmt1 = "%s.%%s" % test_mods_pkg_name + fmt2 = "%s.%%s_test" % test_mods_pkg_name if args: - test_modules = [ - m.endswith('_test') and (fmt1 % m) or (fmt2 % m) for m in args - ] + test_modules = [m.endswith("_test") and (fmt1 % m) or (fmt2 % m) for m in args] else: test_modules = [] for f in sorted(os.listdir(test_subdir)): @@ -174,7 +174,7 @@ def run(*args, **kwds): tmp = test_modules test_modules = [] for name in tmp: - tag_module_name = "%s_tags" % (name[0:-5],) + tag_module_name = f"{name[0:-5]}_tags" try: tag_module = import_submodule(tag_module_name) except ImportError: @@ -183,12 +183,12 @@ def run(*args, **kwds): try: tags = tag_module.__tags__ except AttributeError: - print ("%s has no tags: ignoring" % (tag_module_name,)) + print(f"{tag_module_name} has no tags: ignoring") test_modules.append(name) else: for tag in tags: if tag in option_exclude: - print ("skipping %s (tag '%s')" % (name, tag)) + print(f"skipping {name} (tag '{tag}')") break else: test_modules.append(name) @@ -198,8 +198,8 @@ def run(*args, **kwds): # Meta results results = {} - meta_results = {'__meta__' : {}} - meta = meta_results['__meta__'] + meta_results = {"__meta__": {}} + meta = meta_results["__meta__"] ########################################################################### # Randomization @@ -207,8 +207,8 @@ def run(*args, **kwds): if option_randomize or option_seed is not None: if option_seed is None: option_seed = time.time() - meta['random_seed'] = option_seed - print ("\nRANDOM SEED USED: %s\n" % option_seed) + meta["random_seed"] = option_seed + print(f"\nRANDOM SEED USED: {option_seed}\n") random.seed(option_seed) random.shuffle(test_modules) @@ -216,7 +216,7 @@ def run(*args, **kwds): # Single process mode if not option_usesubprocess: - options['exclude'] = option_exclude + options["exclude"] = option_exclude t = time.time() for module in test_modules: results.update(run_test(module, **options)) @@ -232,36 +232,38 @@ def run(*args, **kwds): else: from test.test_utils.async_sub import proc_in_time_or_kill - pass_on_args = ['--exclude', ','.join(option_exclude)] - for field in ['randomize', 'incomplete', 'unbuffered']: + pass_on_args = ["--exclude", ",".join(option_exclude)] + for field in ["randomize", "incomplete", "unbuffered", "verbosity"]: if kwds.get(field, False): - pass_on_args.append('--'+field) + pass_on_args.append("--" + field) def sub_test(module): - print ('loading %s' % module) + print(f"loading {module}") - cmd = [option_python, '-m', test_runner_mod, - module] + pass_on_args + cmd = [option_python, "-m", test_runner_mod, module] + pass_on_args - return (module, - (cmd, test_env, working_dir), - proc_in_time_or_kill(cmd, option_time_out, env=test_env, - wd=working_dir)) + return ( + module, + (cmd, test_env, working_dir), + proc_in_time_or_kill( + cmd, option_time_out, env=test_env, wd=working_dir + ), + ) if option_multi_thread > 1: + def tmap(f, args): - return pygame.threads.tmap ( - f, args, stop_on_error = False, - num_workers = option_multi_thread + return pygame.threads.tmap( + f, args, stop_on_error=False, num_workers=option_multi_thread ) + else: tmap = map t = time.time() - for module, cmd, (return_code, raw_return) in tmap(sub_test, - test_modules): - test_file = '%s.py' % os.path.join(test_subdir, module) + for module, cmd, (return_code, raw_return) in tmap(sub_test, test_modules): + test_file = f"{os.path.join(test_subdir, module)}.py" cmd, test_env, working_dir = cmd test_results = get_test_results(raw_return) @@ -270,15 +272,17 @@ def run(*args, **kwds): else: results[module] = {} - results[module].update(dict( - return_code=return_code, - raw_return=raw_return, - cmd=cmd, - test_file=test_file, - test_env=test_env, - working_dir=working_dir, - module=module, - )) + results[module].update( + dict( + return_code=return_code, + raw_return=raw_return, + cmd=cmd, + test_file=test_file, + test_env=test_env, + working_dir=working_dir, + module=module, + ) + ) t = time.time() - t @@ -289,24 +293,26 @@ def run(*args, **kwds): untrusty_total, combined = combine_results(results, t) total, n_errors, n_failures = count_results(results) - meta['total_tests'] = total - meta['combined'] = combined - meta['total_errors'] = n_errors - meta['total_failures'] = n_failures + meta["total_tests"] = total + meta["combined"] = combined + meta["total_errors"] = n_errors + meta["total_failures"] = n_failures results.update(meta_results) if not option_usesubprocess and total != untrusty_total: - raise AssertionError('Something went wrong in the Test Machinery:\n' - 'total: %d != untrusty_total: %d' % (total, untrusty_total)) + raise AssertionError( + "Something went wrong in the Test Machinery:\n" + "total: %d != untrusty_total: %d" % (total, untrusty_total) + ) if not option_dump: - print (combined) + print(combined) else: - print (TEST_RESULTS_START) - print (pformat(results)) + print(TEST_RESULTS_START) + print(pformat(results)) if option_file is not None: - results_file = open(option_file, 'w') + results_file = open(option_file, "w") try: results_file.write(pformat(results)) finally: @@ -320,13 +326,13 @@ def run(*args, **kwds): def count_results(results): total = errors = failures = 0 for result in results.values(): - if result.get('return_code', 0): + if result.get("return_code", 0): total += 1 errors += 1 else: - total += result['num_tests'] - errors += result['num_errors'] - failures += result['num_failures'] + total += result["num_tests"] + errors += result["num_errors"] + failures += result["num_failures"] return total, errors, failures @@ -341,4 +347,3 @@ def run_and_exit(*args, **kwargs): if fails: sys.exit(1) sys.exit(0) - diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py b/venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py index 2222b49c72f493b6b403062ba31872c74abe7636..0531cc2f71d61a87a29357dcc8edc66c9d6984d5 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/test_machinery.py @@ -10,10 +10,12 @@ except ImportError: from . import import_submodule + class PygameTestLoader(unittest.TestLoader): - def __init__(self, randomize_tests=False, include_incomplete=False, - exclude=('interactive',)): - super(PygameTestLoader, self).__init__() + def __init__( + self, randomize_tests=False, include_incomplete=False, exclude=("interactive",) + ): + super().__init__() self.randomize_tests = randomize_tests if exclude is None: @@ -22,11 +24,11 @@ class PygameTestLoader(unittest.TestLoader): self.exclude = set(exclude) if include_incomplete: - self.testMethodPrefix = ('test', 'todo_') + self.testMethodPrefix = ("test", "todo_") def getTestCaseNames(self, testCaseClass): res = [] - for name in super(PygameTestLoader, self).getTestCaseNames(testCaseClass): + for name in super().getTestCaseNames(testCaseClass): tags = get_tags(testCaseClass, getattr(testCaseClass, name)) if self.exclude.isdisjoint(tags): res.append(name) @@ -41,6 +43,7 @@ class PygameTestLoader(unittest.TestLoader): TAGS_RE = re.compile(r"\|[tT]ags:(-?[ a-zA-Z,0-9_\n]+)\|", re.M) + class TestTags: def __init__(self): self.memoized = {} @@ -56,25 +59,31 @@ class TestTags: if key not in self.memoized: parent_module = self.get_parent_module(parent_class) - module_tags = getattr(parent_module, '__tags__', []) - class_tags = getattr(parent_class, '__tags__', []) + module_tags = getattr(parent_module, "__tags__", []) + class_tags = getattr(parent_class, "__tags__", []) - tags = TAGS_RE.search(inspect.getdoc(meth) or '') - if tags: test_tags = [t.strip() for t in tags.group(1).split(',')] - else: test_tags = [] + tags = TAGS_RE.search(inspect.getdoc(meth) or "") + if tags: + test_tags = [t.strip() for t in tags.group(1).split(",")] + else: + test_tags = [] combined = set() for tags in (module_tags, class_tags, test_tags): - if not tags: continue + if not tags: + continue - add = set([t for t in tags if not t.startswith('-')]) - remove = set([t[1:] for t in tags if t not in add]) + add = {t for t in tags if not t.startswith("-")} + remove = {t[1:] for t in tags if t not in add} - if add: combined.update(add) - if remove: combined.difference_update(remove) + if add: + combined.update(add) + if remove: + combined.difference_update(remove) self.memoized[key] = combined return self.memoized[key] + get_tags = TestTags() diff --git a/venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py b/venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py index 8ee322fad36d8e8962ded6c07087e90aa307063c..a19d7b00aced26aab365f5d4c573f229b4e08908 100644 --- a/venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py +++ b/venv/Lib/site-packages/pygame/tests/test_utils/test_runner.py @@ -1,36 +1,32 @@ import sys import os -if __name__ == '__main__': +if __name__ == "__main__": pkg_dir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] parent_dir, pkg_name = os.path.split(pkg_dir) - is_pygame_pkg = (pkg_name == 'tests' and - os.path.split(parent_dir)[1] == 'pygame') + is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame" if not is_pygame_pkg: sys.path.insert(0, parent_dir) else: - is_pygame_pkg = __name__.startswith('pygame.tests.') - -import unittest -from .test_machinery import PygameTestLoader - -import re -try: - import StringIO -except ImportError: - import io as StringIO + is_pygame_pkg = __name__.startswith("pygame.tests.") +import io import optparse +import re +import unittest from pprint import pformat +from .test_machinery import PygameTestLoader + def prepare_test_env(): test_subdir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] main_dir = os.path.split(test_subdir)[0] sys.path.insert(0, test_subdir) - fake_test_subdir = os.path.join(test_subdir, 'run_tests__tests') + fake_test_subdir = os.path.join(test_subdir, "run_tests__tests") return main_dir, test_subdir, fake_test_subdir + main_dir, test_subdir, fake_test_subdir = prepare_test_env() ################################################################################ @@ -39,41 +35,66 @@ main_dir, test_subdir, fake_test_subdir = prepare_test_env() # options are shared with run_tests.py so make sure not to conflict # in time more will be added here -TAG_PAT = r'-?[a-zA-Z0-9_]+' +TAG_PAT = r"-?[a-zA-Z0-9_]+" TAG_RE = re.compile(TAG_PAT) -EXCLUDE_RE = re.compile("(%s,?\s*)+$" % (TAG_PAT,)) +EXCLUDE_RE = re.compile(rf"({TAG_PAT},?\s*)+$") + def exclude_callback(option, opt, value, parser): if EXCLUDE_RE.match(value) is None: - raise optparse.OptionValueError("%s argument has invalid value" % - (opt,)) + raise optparse.OptionValueError(f"{opt} argument has invalid value") parser.values.exclude = TAG_RE.findall(value) -opt_parser = optparse.OptionParser() - -opt_parser.add_option ( - "-i", "--incomplete", action = 'store_true', - help = "fail incomplete tests" ) -opt_parser.add_option ( - "-s", "--usesubprocess", action = "store_true", - help = "run everything in a single process " - " (default: use no subprocesses)" ) - -opt_parser.add_option ( - "-e", "--exclude", - action = 'callback', - type = 'string', - help = "exclude tests containing any of TAGS", - callback = exclude_callback) - -opt_parser.add_option ( - "-v", "--unbuffered", action = 'store_true', - help = "Show stdout/stderr as tests run, rather than storing it and showing on failures" ) +opt_parser = optparse.OptionParser() -opt_parser.add_option ( - "-r", "--randomize", action = 'store_true', - help = "randomize order of tests" ) +opt_parser.add_option( + "-i", "--incomplete", action="store_true", help="fail incomplete tests" +) + +opt_parser.add_option( + "-s", + "--usesubprocess", + action="store_true", + help="run everything in a single process " " (default: use no subprocesses)", +) + +opt_parser.add_option( + "-e", + "--exclude", + action="callback", + type="string", + help="exclude tests containing any of TAGS", + callback=exclude_callback, +) + +opt_parser.add_option( + "-u", + "--unbuffered", + action="store_true", + help="Show stdout/stderr as tests run, rather than storing it and showing on failures", +) + +opt_parser.add_option( + "-v", + "--verbose", + dest="verbosity", + action="store_const", + const=2, + help="Verbose output", +) +opt_parser.add_option( + "-q", + "--quiet", + dest="verbosity", + action="store_const", + const=0, + help="Quiet output", +) + +opt_parser.add_option( + "-r", "--randomize", action="store_true", help="randomize order of tests" +) ################################################################################ # If an xxxx_test.py takes longer than TIME_OUT seconds it will be killed @@ -101,13 +122,61 @@ return (first 10 and last 10 lines): %(raw_return)s """ # Leave that last empty line else build page regex won't match - # Text also needs to be vertically compressed +# Text also needs to be vertically compressed RAN_TESTS_DIV = (70 * "-") + "\nRan" DOTS = re.compile("^([FE.sux]*)$", re.MULTILINE) + +def extract_tracebacks(output): + """from test runner output return the tracebacks.""" + verbose_mode = " ..." in output + + if verbose_mode: + if "ERROR" in output or "FAILURE" in output: + return "\n\n==".join(output.split("\n\n==")[1:]) + else: + dots = DOTS.search(output).group(1) + if "E" in dots or "F" in dots: + return output[len(dots) + 1 :].split(RAN_TESTS_DIV)[0] + return "" + + +def output_into_dots(output): + """convert the test runner output into dots.""" + # verbose_mode = ") ..." in output + verbose_mode = " ..." in output + + if verbose_mode: + # a map from the verbose output to the dots output. + reasons = { + "... ERROR": "E", + "... unexpected success": "u", + "... skipped": "s", + "... expected failure": "x", + "... ok": ".", + "... FAIL": "F", + } + results = output.split("\n\n==")[0] + lines = [l for l in results.split("\n") if l and "..." in l] + dotlist = [] + for l in lines: + found = False + for reason in reasons: + if reason in l: + dotlist.append(reasons[reason]) + found = True + break + if not found: + raise ValueError(f"Not sure what this is. Add to reasons. :{l}") + + return "".join(dotlist) + dots = DOTS.search(output).group(1) + return dots + + def combine_results(all_results, t): """ @@ -118,117 +187,138 @@ def combine_results(all_results, t): """ - all_dots = '' + all_dots = "" failures = [] for module, results in sorted(all_results.items()): - output, return_code, raw_return = map ( - results.get, ('output','return_code', 'raw_return') + output, return_code, raw_return = map( + results.get, ("output", "return_code", "raw_return") ) if not output or (return_code and RAN_TESTS_DIV not in output): # would this effect the original dict? TODO output_lines = raw_return.splitlines() if len(output_lines) > 20: - results['raw_return'] = '\n'.join(output_lines[:10] + - ['...'] + - output_lines[-10:] - ) - failures.append( COMPLETE_FAILURE_TEMPLATE % results ) - all_dots += 'E' + results["raw_return"] = "\n".join( + output_lines[:10] + ["..."] + output_lines[-10:] + ) + failures.append(COMPLETE_FAILURE_TEMPLATE % results) + all_dots += "E" continue - dots = DOTS.search(output).group(1) + dots = output_into_dots(output) all_dots += dots + tracebacks = extract_tracebacks(output) + if tracebacks: + failures.append(tracebacks) - if 'E' in dots or 'F' in dots: - failures.append( output[len(dots)+1:].split(RAN_TESTS_DIV)[0] ) - - total_fails, total_errors = map(all_dots.count, 'FE') + total_fails, total_errors = map(all_dots.count, "FE") total_tests = len(all_dots) combined = [all_dots] if failures: - combined += [''.join(failures).lstrip('\n')[:-1]] - combined += ["%s %s tests in %.3fs\n" % (RAN_TESTS_DIV, total_tests, t)] + combined += ["".join(failures).lstrip("\n")[:-1]] + combined += [f"{RAN_TESTS_DIV} {total_tests} tests in {t:.3f}s\n"] if failures: - infos = ((["failures=%s" % total_fails] if total_fails else []) + - (["errors=%s" % total_errors] if total_errors else [])) - combined += ['FAILED (%s)\n' % ', '.join(infos)] + infos = ([f"failures={total_fails}"] if total_fails else []) + ( + [f"errors={total_errors}"] if total_errors else [] + ) + combined += [f"FAILED ({', '.join(infos)})\n"] else: - combined += ['OK\n'] + combined += ["OK\n"] + + return total_tests, "\n".join(combined) - return total_tests, '\n'.join(combined) ################################################################################ TEST_RESULTS_START = "<--!! TEST RESULTS START HERE !!-->" TEST_RESULTS_END = "<--!! TEST RESULTS END HERE !!-->" -_test_re_str = '%s\n(.*)%s' % (TEST_RESULTS_START, TEST_RESULTS_END) +_test_re_str = f"{TEST_RESULTS_START}\n(.*){TEST_RESULTS_END}" TEST_RESULTS_RE = re.compile(_test_re_str, re.DOTALL | re.M) + def get_test_results(raw_return): test_results = TEST_RESULTS_RE.search(raw_return) if test_results: try: return eval(test_results.group(1)) except: - print ("BUGGY TEST RESULTS EVAL:\n %s" % test_results.group(1)) + print(f"BUGGY TEST RESULTS EVAL:\n {test_results.group(1)}") raise ################################################################################ -def run_test(module, incomplete=False, usesubprocess=True, randomize=False, - exclude=('interactive',), buffer=True): - """Run a unit test module - """ + +def run_test( + module, + incomplete=False, + usesubprocess=True, + randomize=False, + exclude=("interactive",), + buffer=True, + unbuffered=None, + verbosity=1, +): + """Run a unit test module""" suite = unittest.TestSuite() - print ('loading %s' % module) + if verbosity is None: + verbosity = 1 + + if verbosity: + print(f"loading {module}") - loader = PygameTestLoader(randomize_tests=randomize, - include_incomplete=incomplete, - exclude=exclude) + loader = PygameTestLoader( + randomize_tests=randomize, include_incomplete=incomplete, exclude=exclude + ) suite.addTest(loader.loadTestsFromName(module)) - output = StringIO.StringIO() - runner = unittest.TextTestRunner(stream=output, buffer=buffer) + output = io.StringIO() + runner = unittest.TextTestRunner(stream=output, buffer=buffer, verbosity=verbosity) results = runner.run(suite) - results = {module: { - 'output': output.getvalue(), - 'num_tests': results.testsRun, - 'num_errors': len(results.errors), - 'num_failures': len(results.failures), - }} + if verbosity == 2: + output.seek(0) + print(output.read()) + output.seek(0) + + results = { + module: { + "output": output.getvalue(), + "num_tests": results.testsRun, + "num_errors": len(results.errors), + "num_failures": len(results.failures), + } + } if usesubprocess: - print (TEST_RESULTS_START) - print (pformat(results)) - print (TEST_RESULTS_END) + print(TEST_RESULTS_START) + print(pformat(results)) + print(TEST_RESULTS_END) else: return results + ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": options, args = opt_parser.parse_args() if not args: - if is_pygame_pkg: - run_from = 'pygame.tests.go' + run_from = "pygame.tests.go" else: - run_from = os.path.join(main_dir, 'run_tests.py') - sys.exit('No test module provided; consider using %s instead' % run_from) - run_test(args[0], - incomplete=options.incomplete, - usesubprocess=options.usesubprocess, - randomize=options.randomize, - exclude=options.exclude, - buffer=(not options.unbuffered), - ) + run_from = os.path.join(main_dir, "run_tests.py") + sys.exit(f"No test module provided; consider using {run_from} instead") + run_test( + args[0], + incomplete=options.incomplete, + usesubprocess=options.usesubprocess, + randomize=options.randomize, + exclude=options.exclude, + buffer=(not options.unbuffered), + ) ################################################################################ - diff --git a/venv/Lib/site-packages/pygame/tests/threads_test.py b/venv/Lib/site-packages/pygame/tests/threads_test.py index ce492bc99078781f5662d87f059f075b27257f71..07829841cff87fdf3bacf9585c75c24613f47938 100644 --- a/venv/Lib/site-packages/pygame/tests/threads_test.py +++ b/venv/Lib/site-packages/pygame/tests/threads_test.py @@ -1,7 +1,7 @@ import unittest from pygame.threads import FuncResult, tmap, WorkerQueue, Empty, STOP -from pygame import threads -from pygame.compat import xrange_ +from pygame import threads, Surface, transform + import time @@ -9,10 +9,10 @@ import time class WorkerQueueTypeTest(unittest.TestCase): def test_usage_with_different_functions(self): def f(x): - return x+1 + return x + 1 def f2(x): - return x+2 + return x + 2 wq = WorkerQueue() fr = FuncResult(f) @@ -25,14 +25,34 @@ class WorkerQueueTypeTest(unittest.TestCase): self.assertEqual(fr.result, 2) self.assertEqual(fr2.result, 3) - def todo_test_do(self): - + def test_do(self): + """Tests function placement on queue and execution after blocking function completion.""" # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.do: - # puts a function on a queue for running later. - # + # puts a function on a queue for running _later_. - self.fail() + # TODO: This tests needs refactoring to avoid sleep. + # sleep is slow and unreliable (especially on VMs). + + # def sleep_test(): + # time.sleep(0.5) + + # def calc_test(x): + # return x + 1 + + # worker_queue = WorkerQueue(num_workers=1) + # sleep_return = FuncResult(sleep_test) + # calc_return = FuncResult(calc_test) + # init_time = time.time() + # worker_queue.do(sleep_return) + # worker_queue.do(calc_return, 1) + # worker_queue.wait() + # worker_queue.stop() + # time_diff = time.time() - init_time + + # self.assertEqual(sleep_return.result, None) + # self.assertEqual(calc_return.result, 2) + # self.assertGreaterEqual(time_diff, 0.5) def test_stop(self): """Ensure stop() stops the worker queue""" @@ -41,35 +61,54 @@ class WorkerQueueTypeTest(unittest.TestCase): self.assertGreater(len(wq.pool), 0) for t in wq.pool: - self.assertTrue(t.isAlive()) + self.assertTrue(t.is_alive()) - for i in xrange_(200): - wq.do(lambda x: x+1, i) + for i in range(200): + wq.do(lambda x: x + 1, i) wq.stop() for t in wq.pool: - self.assertFalse(t.isAlive()) + self.assertFalse(t.is_alive()) self.assertIs(wq.queue.get(), STOP) - def todo_test_threadloop(self): - + def test_threadloop(self): # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.threadloop: - # Loops until all of the tasks are finished. + # Loops until all of the tasks are finished. - self.fail() + # Make a worker queue with only one thread + wq = WorkerQueue(1) - def test_wait(self): + # Ocuppy the one worker with the threadloop + # wq threads are just threadloop, so this makes an embedded threadloop + wq.do(wq.threadloop) + # Make sure wq can still do work + # If wq can still do work, threadloop works + l = [] + wq.do(l.append, 1) + # Wait won't work because the primary thread is in an infinite loop + time.sleep(0.5) + self.assertEqual(l[0], 1) + + # Kill the embedded threadloop by sending stop onto the stack + # Threadloop puts STOP back onto the queue when it STOPs so this kills both loops + wq.stop() + + # Make sure wq has stopped + self.assertFalse(wq.pool[0].is_alive()) + + def test_wait(self): # __doc__ (as of 2008-06-28) for pygame.threads.WorkerQueue.wait: - # waits until all tasks are complete. + # waits until all tasks are complete. wq = WorkerQueue() - for i in xrange_(2000): wq.do(lambda x: x+1, i) + for i in range(2000): + wq.do(lambda x: x + 1, i) wq.wait() self.assertRaises(Empty, wq.queue.get_nowait) @@ -78,20 +117,30 @@ class WorkerQueueTypeTest(unittest.TestCase): class ThreadsModuleTest(unittest.TestCase): - def todo_test_benchmark_workers(self): + def test_benchmark_workers(self): + """Ensure benchmark_workers performance measure functions properly with both default and specified inputs""" "tags:long_running" # __doc__ (as of 2008-06-28) for pygame.threads.benchmark_workers: - # does a little test to see if workers are at all faster. - # Returns the number of workers which works best. - # Takes a little bit of time to run, so you should only really call - # it once. - # You can pass in benchmark data, and functions if you want. - # a_bench_func - f(data) - # the_data - data to work on. - - self.fail() + # does a little test to see if workers are at all faster. + # Returns the number of workers which works best. + # Takes a little bit of time to run, so you should only really call + # it once. + # You can pass in benchmark data, and functions if you want. + # a_bench_func - f(data) + # the_data - data to work on. + optimal_workers = threads.benchmark_workers() + self.assertIsInstance(optimal_workers, int) + self.assertTrue(0 <= optimal_workers < 64) + + # Test passing benchmark data and function explicitly + def smooth_scale_bench(data): + transform.smoothscale(data, (128, 128)) + + surf_data = [Surface((x, x), 0, 32) for x in range(12, 64, 12)] + best_num_workers = threads.benchmark_workers(smooth_scale_bench, surf_data) + self.assertIsInstance(best_num_workers, int) def test_init(self): """Ensure init() sets up the worker queue""" @@ -111,47 +160,60 @@ class ThreadsModuleTest(unittest.TestCase): def test_tmap(self): # __doc__ (as of 2008-06-28) for pygame.threads.tmap: - # like map, but uses a thread pool to execute. - # num_workers - the number of worker threads that will be used. If pool - # is passed in, then the num_workers arg is ignored. - # worker_queue - you can optionally pass in an existing WorkerQueue. - # wait - True means that the results are returned when everything is finished. - # False means that we return the [worker_queue, results] right away instead. - # results, is returned as a list of FuncResult instances. - # stop_on_error - + # like map, but uses a thread pool to execute. + # num_workers - the number of worker threads that will be used. If pool + # is passed in, then the num_workers arg is ignored. + # worker_queue - you can optionally pass in an existing WorkerQueue. + # wait - True means that the results are returned when everything is finished. + # False means that we return the [worker_queue, results] right away instead. + # results, is returned as a list of FuncResult instances. + # stop_on_error - - func, data = lambda x:x+1, xrange_(100) + ## test that the outcomes of map and tmap are the same + func, data = lambda x: x + 1, range(100) tmapped = list(tmap(func, data)) mapped = list(map(func, data)) self.assertEqual(tmapped, mapped) + ## Test that setting tmap to not stop on errors produces the expected result + data2 = range(100) + always_excepts = lambda x: 1 / 0 + + tmapped2 = list(tmap(always_excepts, data2, stop_on_error=False)) + + # Use list comprehension to check all entries are None as all function + # calls made by tmap will have thrown an exception (ZeroDivisionError) + # Condense to single bool with `all`, which will return true if all + # entries are true + self.assertTrue(all([x is None for x in tmapped2])) + def todo_test_tmap__None_func_and_multiple_sequences(self): """Using a None as func and multiple sequences""" self.fail() - res = tmap(None, [1,2,3,4]) - res2 = tmap(None, [1,2,3,4], [22, 33, 44, 55]) - res3 = tmap(None, [1,2,3,4], [22, 33, 44, 55, 66]) - res4 = tmap(None, [1,2,3,4,5], [22, 33, 44, 55]) + res = tmap(None, [1, 2, 3, 4]) + res2 = tmap(None, [1, 2, 3, 4], [22, 33, 44, 55]) + res3 = tmap(None, [1, 2, 3, 4], [22, 33, 44, 55, 66]) + res4 = tmap(None, [1, 2, 3, 4, 5], [22, 33, 44, 55]) self.assertEqual([1, 2, 3, 4], res) self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55)], res2) self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (None, 66)], res3) - self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (5,None)], res4) + self.assertEqual([(1, 22), (2, 33), (3, 44), (4, 55), (5, None)], res4) def test_tmap__wait(self): r = range(1000) - wq, results = tmap(lambda x:x, r, num_workers = 5, wait=False) + wq, results = tmap(lambda x: x, r, num_workers=5, wait=False) wq.wait() - r2 = map(lambda x:x.result, results) + r2 = map(lambda x: x.result, results) self.assertEqual(list(r), list(r2)) def test_FuncResult(self): """Ensure FuncResult sets its result and exception attributes""" # Results are stored in result attribute - fr = FuncResult(lambda x:x+1) + fr = FuncResult(lambda x: x + 1) fr(2) self.assertEqual(fr.result, 3) @@ -159,7 +221,7 @@ class ThreadsModuleTest(unittest.TestCase): # Exceptions are store in exception attribute self.assertIsNone(fr.exception, "no exception should be raised") - exception = ValueError('rast') + exception = ValueError("rast") def x(sdf): raise exception @@ -172,5 +234,5 @@ class ThreadsModuleTest(unittest.TestCase): ################################################################################ -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/time_test.py b/venv/Lib/site-packages/pygame/tests/time_test.py index 7a84458b8db866ecc9948d2d29fd8edfac43ad4a..95e04f048d2f8b53b5ee2477ec20bc0361632651 100644 --- a/venv/Lib/site-packages/pygame/tests/time_test.py +++ b/venv/Lib/site-packages/pygame/tests/time_test.py @@ -1,204 +1,410 @@ +import os +import platform import unittest import pygame +import time Clock = pygame.time.Clock class ClockTypeTest(unittest.TestCase): + __tags__ = ["timing"] + def test_construction(self): """Ensure a Clock object can be created""" c = Clock() self.assertTrue(c, "Clock cannot be constructed") - def todo_test_get_fps(self): - - # __doc__ (as of 2008-08-02) for pygame.time.Clock.get_fps: - - # Clock.get_fps(): return float - # compute the clock framerate - # - # Compute your game's framerate (in frames per second). It is computed - # by averaging the last few calls to Clock.tick(). - # - - self.fail() - - # delay_per_frame = 1 / 100.0 - # - # c = Clock() - # - # for f in range(100): - # c.tick() - # time.sleep(delay_per_frame) - # - # self.assertTrue(99.0 < c.get_fps() < 101.0) - - def todo_test_get_rawtime(self): - - # __doc__ (as of 2008-08-02) for pygame.time.Clock.get_rawtime: - - # Clock.get_rawtime(): return milliseconds - # actual time used in the previous tick - # - # Similar to Clock.get_time(), but this does not include any time used - # while Clock.tick() was delaying to limit the framerate. - # - - self.fail() - - def todo_test_get_time(self): - - # __doc__ (as of 2008-08-02) for pygame.time.Clock.get_time: - - # Clock.get_time(): return milliseconds - # time used in the previous tick - # - # Returns the parameter passed to the last call to Clock.tick(). It is - # the number of milliseconds passed between the previous two calls to - # Pygame.tick(). - # - - self.fail() - - # c = Clock() - # c.tick() #between here - # time.sleep(0.02) - # #get_time() - # c.tick() # here - # - # time.sleep(0.02) - # - # self.assertTrue(20 <= c.get_time() <= 30) - - - def todo_test_tick(self): - - # __doc__ (as of 2008-08-02) for pygame.time.Clock.tick: - - # Clock.tick(framerate=0): return milliseconds - # control timer events - # update the clock - # - # This method should be called once per frame. It will compute how - # many milliseconds have passed since the previous call. - # - # If you pass the optional framerate argument the function will delay - # to keep the game running slower than the given ticks per second. - # This can be used to help limit the runtime speed of a game. By - # calling Clock.tick(40) once per frame, the program will never run at - # more than 40 frames per second. - # - # Note that this function uses SDL_Delay function which is not - # accurate on every platform, but does not use much cpu. Use - # tick_busy_loop if you want an accurate timer, and don't mind chewing - # cpu. - # - - self.fail() - - # collection = [] - # c = Clock() - # - # c.tick() - # for i in range(100): - # time.sleep(0.005) - # collection.append(c.tick()) - # - # for outlier in [min(collection), max(collection)]: - # if outlier != 5: collection.remove(outlier) - # - # self.assertEqual(sum(collection) / len(collection), 5) - - def todo_test_tick_busy_loop(self): - - # __doc__ (as of 2008-08-02) for pygame.time.Clock.tick_busy_loop: - - # Clock.tick_busy_loop(framerate=0): return milliseconds - # control timer events - # update the clock - # - # This method should be called once per frame. It will compute how - # many milliseconds have passed since the previous call. - # - # If you pass the optional framerate argument the function will delay - # to keep the game running slower than the given ticks per second. - # This can be used to help limit the runtime speed of a game. By - # calling Clock.tick(40) once per frame, the program will never run at - # more than 40 frames per second. - # - # Note that this function uses pygame.time.delay, which uses lots of - # cpu in a busy loop to make sure that timing is more acurate. - # - # New in pygame 1.8.0. - - self.fail() - -class TimeModuleTest(unittest.TestCase): - def todo_test_delay(self): + def test_get_fps(self): + """test_get_fps tests pygame.time.get_fps()""" + # Initialization check, first call should return 0 fps + c = Clock() + self.assertEqual(c.get_fps(), 0) + # Type check get_fps should return float + self.assertTrue(type(c.get_fps()) == float) + # Allowable margin of error in percentage + delta = 0.30 + # Test fps correctness for 100, 60 and 30 fps + self._fps_test(c, 100, delta) + self._fps_test(c, 60, delta) + self._fps_test(c, 30, delta) + + def _fps_test(self, clock, fps, delta): + """ticks fps times each second, hence get_fps() should return fps""" + delay_per_frame = 1.0 / fps + for f in range(fps): # For one second tick and sleep + clock.tick() + time.sleep(delay_per_frame) + # We should get around fps (+- fps*delta -- delta % of fps) + self.assertAlmostEqual(clock.get_fps(), fps, delta=fps * delta) + + def test_get_rawtime(self): + iterations = 10 + delay = 0.1 + delay_miliseconds = delay * (10**3) # actual time difference between ticks + framerate_limit = 5 + delta = 50 # allowable error in milliseconds + + # Testing Clock Initialization + c = Clock() + self.assertEqual(c.get_rawtime(), 0) + + # Testing Raw Time with Frame Delay + for f in range(iterations): + time.sleep(delay) + c.tick(framerate_limit) + c1 = c.get_rawtime() + self.assertAlmostEqual(delay_miliseconds, c1, delta=delta) + + # Testing get_rawtime() = get_time() + for f in range(iterations): + time.sleep(delay) + c.tick() + c1 = c.get_rawtime() + c2 = c.get_time() + self.assertAlmostEqual(c1, c2, delta=delta) + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_get_time(self): + # Testing parameters + delay = 0.1 # seconds + delay_miliseconds = delay * (10**3) + iterations = 10 + delta = 50 # milliseconds + + # Testing Clock Initialization + c = Clock() + self.assertEqual(c.get_time(), 0) + + # Testing within delay parameter range + for i in range(iterations): + time.sleep(delay) + c.tick() + c1 = c.get_time() + self.assertAlmostEqual(delay_miliseconds, c1, delta=delta) + + # Comparing get_time() results with the 'time' module + for i in range(iterations): + t0 = time.time() + time.sleep(delay) + c.tick() + t1 = time.time() + c1 = c.get_time() # elapsed time in milliseconds + d0 = (t1 - t0) * ( + 10**3 + ) #'time' module elapsed time converted to milliseconds + self.assertAlmostEqual(d0, c1, delta=delta) + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_tick(self): + """Tests time.Clock.tick()""" + """ + Loops with a set delay a few times then checks what tick reports to + verify its accuracy. Then calls tick with a desired frame-rate and + verifies it is not faster than the desired frame-rate nor is it taking + a dramatically long time to complete + """ + + # Adjust this value to increase the acceptable sleep jitter + epsilon = 5 # 1.5 + + # Adjust this value to increase the acceptable locked frame-rate jitter + epsilon2 = 0.3 + # adjust this value to increase the acceptable frame-rate margin + epsilon3 = 20 + testing_framerate = 60 + milliseconds = 5.0 + + collection = [] + c = Clock() - # __doc__ (as of 2008-08-02) for pygame.time.delay: + # verify time.Clock.tick() will measure the time correctly + c.tick() + for i in range(100): + time.sleep(milliseconds / 1000) # convert to seconds + collection.append(c.tick()) - # pygame.time.delay(milliseconds): return time - # pause the program for an amount of time - # - # Will pause for a given number of milliseconds. This function will - # use the processor (rather than sleeping) in order to make the delay - # more accurate than pygame.time.wait(). - # - # This returns the actual number of milliseconds used. + # removes the first highest and lowest value + for outlier in [min(collection), max(collection)]: + if outlier != milliseconds: + collection.remove(outlier) - self.fail() + average_time = float(sum(collection)) / len(collection) - def todo_test_get_ticks(self): + # assert the deviation from the intended frame-rate is within the + # acceptable amount (the delay is not taking a dramatically long time) + self.assertAlmostEqual(average_time, milliseconds, delta=epsilon) - # __doc__ (as of 2008-08-02) for pygame.time.get_ticks: + # verify tick will control the frame-rate - # pygame.time.get_ticks(): return milliseconds - # get the time in milliseconds - # - # Return the number of millisconds since pygame.init() was called. - # Before pygame is initialized this will always be 0. - # + c = Clock() + collection = [] - self.fail() + start = time.time() - def todo_test_set_timer(self): + for i in range(testing_framerate): + collection.append(c.tick(testing_framerate)) - # __doc__ (as of 2008-08-02) for pygame.time.set_timer: + # remove the highest and lowest outliers + for outlier in [min(collection), max(collection)]: + if outlier != round(1000 / testing_framerate): + collection.remove(outlier) - # pygame.time.set_timer(eventid, milliseconds): return None - # repeatedly create an event on the event queue - # - # Set an event type to appear on the event queue every given number of - # milliseconds. The first event will not appear until the amount of - # time has passed. - # - # Every event type can have a separate timer attached to it. It is - # best to use the value between pygame.USEREVENT and pygame.NUMEVENTS. - # - # To disable the timer for an event, set the milliseconds argument to 0. + end = time.time() - self.fail() + # Since calling tick with a desired fps will prevent the program from + # running at greater than the given fps, 100 iterations at 100 fps + # should last no less than 1 second + self.assertAlmostEqual(end - start, 1, delta=epsilon2) - def todo_test_wait(self): + average_tick_time = float(sum(collection)) / len(collection) + self.assertAlmostEqual( + 1000 / average_tick_time, testing_framerate, delta=epsilon3 + ) - # __doc__ (as of 2008-08-02) for pygame.time.wait: + def test_tick_busy_loop(self): + """Test tick_busy_loop""" - # pygame.time.wait(milliseconds): return time - # pause the program for an amount of time - # - # Will pause for a given number of milliseconds. This function sleeps - # the process to share the processor with other programs. A program - # that waits for even a few milliseconds will consume very little - # processor time. It is slightly less accurate than the - # pygame.time.delay() function. - # - # This returns the actual number of milliseconds used. + c = Clock() - self.fail() + # Test whether the return value of tick_busy_loop is equal to + # (FPS is accurate) or greater than (slower than the set FPS) + # with a small margin for error based on differences in how this + # test runs in practise - it either sometimes runs slightly fast + # or seems to based on a rounding error. + second_length = 1000 + shortfall_tolerance = 1 # (ms) The amount of time a tick is allowed to run short of, to account for underlying rounding errors + sample_fps = 40 + + self.assertGreaterEqual( + c.tick_busy_loop(sample_fps), + (second_length / sample_fps) - shortfall_tolerance, + ) + pygame.time.wait(10) # incur delay between ticks that's faster than sample_fps + self.assertGreaterEqual( + c.tick_busy_loop(sample_fps), + (second_length / sample_fps) - shortfall_tolerance, + ) + pygame.time.wait(200) # incur delay between ticks that's slower than sample_fps + self.assertGreaterEqual( + c.tick_busy_loop(sample_fps), + (second_length / sample_fps) - shortfall_tolerance, + ) + + high_fps = 500 + self.assertGreaterEqual( + c.tick_busy_loop(high_fps), (second_length / high_fps) - shortfall_tolerance + ) + + low_fps = 1 + self.assertGreaterEqual( + c.tick_busy_loop(low_fps), (second_length / low_fps) - shortfall_tolerance + ) + + low_non_factor_fps = 35 # 1000/35 makes 28.5714285714 + frame_length_without_decimal_places = int( + second_length / low_non_factor_fps + ) # Same result as math.floor + self.assertGreaterEqual( + c.tick_busy_loop(low_non_factor_fps), + frame_length_without_decimal_places - shortfall_tolerance, + ) + + high_non_factor_fps = 750 # 1000/750 makes 1.3333... + frame_length_without_decimal_places_2 = int( + second_length / high_non_factor_fps + ) # Same result as math.floor + self.assertGreaterEqual( + c.tick_busy_loop(high_non_factor_fps), + frame_length_without_decimal_places_2 - shortfall_tolerance, + ) + + zero_fps = 0 + self.assertEqual(c.tick_busy_loop(zero_fps), 0) + + # Check behaviour of unexpected values + + negative_fps = -1 + self.assertEqual(c.tick_busy_loop(negative_fps), 0) + + fractional_fps = 32.75 + frame_length_without_decimal_places_3 = int(second_length / fractional_fps) + self.assertGreaterEqual( + c.tick_busy_loop(fractional_fps), + frame_length_without_decimal_places_3 - shortfall_tolerance, + ) + + bool_fps = True + self.assertGreaterEqual( + c.tick_busy_loop(bool_fps), (second_length / bool_fps) - shortfall_tolerance + ) -################################################################################ -if __name__ == '__main__': +class TimeModuleTest(unittest.TestCase): + __tags__ = ["timing"] + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_delay(self): + """Tests time.delay() function.""" + millis = 50 # millisecond to wait on each iteration + iterations = 20 # number of iterations + delta = 150 # Represents acceptable margin of error for wait in ms + # Call checking function + self._wait_delay_check(pygame.time.delay, millis, iterations, delta) + # After timing behaviour, check argument type exceptions + self._type_error_checks(pygame.time.delay) + + def test_get_ticks(self): + """Tests time.get_ticks()""" + """ + Iterates and delays for arbitrary amount of time for each iteration, + check get_ticks to equal correct gap time + """ + iterations = 20 + millis = 50 + delta = 15 # Acceptable margin of error in ms + # Assert return type to be int + self.assertTrue(type(pygame.time.get_ticks()) == int) + for i in range(iterations): + curr_ticks = pygame.time.get_ticks() # Save current tick count + curr_time = time.time() # Save current time + pygame.time.delay(millis) # Delay for millis + # Time and Ticks difference from start of the iteration + time_diff = round((time.time() - curr_time) * 1000) + ticks_diff = pygame.time.get_ticks() - curr_ticks + # Assert almost equality of the ticking time and time difference + self.assertAlmostEqual(ticks_diff, time_diff, delta=delta) + + @unittest.skipIf(platform.machine() == "s390x", "Fails on s390x") + @unittest.skipIf( + os.environ.get("CI", None), "CI can have variable time slices, slow." + ) + def test_set_timer(self): + """Tests time.set_timer()""" + """ + Tests if a timer will post the correct amount of eventid events in + the specified delay. Test is posting event objects work. + Also tests if setting milliseconds to 0 stops the timer and if + the once argument and repeat arguments work. + """ + pygame.init() + TIMER_EVENT_TYPE = pygame.event.custom_type() + timer_event = pygame.event.Event(TIMER_EVENT_TYPE) + delta = 50 + timer_delay = 100 + test_number = 8 # Number of events to read for the test + events = 0 # Events read + + pygame.event.clear() + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay) + + # Test that 'test_number' events are posted in the right amount of time + t1 = pygame.time.get_ticks() + max_test_time = t1 + timer_delay * test_number + delta + while events < test_number: + for event in pygame.event.get(): + if event == timer_event: + events += 1 + + # The test takes too much time + if pygame.time.get_ticks() > max_test_time: + break + + pygame.time.set_timer(TIMER_EVENT_TYPE, 0) + t2 = pygame.time.get_ticks() + # Is the number ef events and the timing right? + self.assertEqual(events, test_number) + self.assertAlmostEqual(timer_delay * test_number, t2 - t1, delta=delta) + + # Test that the timer stopped when set with 0ms delay. + pygame.time.delay(200) + self.assertNotIn(timer_event, pygame.event.get()) + + # Test that the old timer for an event is deleted when a new timer is set + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay) + pygame.time.delay(int(timer_delay * 3.5)) + self.assertEqual(pygame.event.get().count(timer_event), 3) + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay * 10) # long wait time + pygame.time.delay(timer_delay * 5) + self.assertNotIn(timer_event, pygame.event.get()) + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay * 3) + pygame.time.delay(timer_delay * 7) + self.assertEqual(pygame.event.get().count(timer_event), 2) + pygame.time.set_timer(TIMER_EVENT_TYPE, timer_delay) + pygame.time.delay(int(timer_delay * 5.5)) + self.assertEqual(pygame.event.get().count(timer_event), 5) + + # Test that the loops=True works + pygame.time.set_timer(TIMER_EVENT_TYPE, 10, True) + pygame.time.delay(40) + self.assertEqual(pygame.event.get().count(timer_event), 1) + + # Test a variety of event objects, test loops argument + events_to_test = [ + pygame.event.Event(TIMER_EVENT_TYPE), + pygame.event.Event( + TIMER_EVENT_TYPE, foo="9gwz5", baz=12, lol=[124, (34, "")] + ), + pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a, unicode="a"), + ] + repeat = 3 + millis = 50 + for e in events_to_test: + pygame.time.set_timer(e, millis, loops=repeat) + pygame.time.delay(2 * millis * repeat) + self.assertEqual(pygame.event.get().count(e), repeat) + pygame.quit() + + def test_wait(self): + """Tests time.wait() function.""" + millis = 100 # millisecond to wait on each iteration + iterations = 10 # number of iterations + delta = 50 # Represents acceptable margin of error for wait in ms + # Call checking function + self._wait_delay_check(pygame.time.wait, millis, iterations, delta) + # After timing behaviour, check argument type exceptions + self._type_error_checks(pygame.time.wait) + + def _wait_delay_check(self, func_to_check, millis, iterations, delta): + """ " + call func_to_check(millis) "iterations" times and check each time if + function "waited" for given millisecond (+- delta). At the end, take + average time for each call (whole_duration/iterations), which should + be equal to millis (+- delta - acceptable margin of error). + *Created to avoid code duplication during delay and wait tests + """ + # take starting time for duration calculation + start_time = time.time() + for i in range(iterations): + wait_time = func_to_check(millis) + # Check equality of wait_time and millis with margin of error delta + self.assertAlmostEqual(wait_time, millis, delta=delta) + stop_time = time.time() + # Cycle duration in millisecond + duration = round((stop_time - start_time) * 1000) + # Duration/Iterations should be (almost) equal to predefined millis + self.assertAlmostEqual(duration / iterations, millis, delta=delta) + + def _type_error_checks(self, func_to_check): + """Checks 3 TypeError (float, tuple, string) for the func_to_check""" + """Intended for time.delay and time.wait functions""" + # Those methods throw no exceptions on negative integers + self.assertRaises(TypeError, func_to_check, 0.1) # check float + self.assertRaises(TypeError, pygame.time.delay, (0, 1)) # check tuple + self.assertRaises(TypeError, pygame.time.delay, "10") # check string + + +############################################################################### + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/touch_tags.py b/venv/Lib/site-packages/pygame/tests/touch_tags.py deleted file mode 100644 index 9f648579868c79e706a790d0a933e27fd5fbdadd..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/tests/touch_tags.py +++ /dev/null @@ -1,2 +0,0 @@ -__tags__ = ['SDL1_ignore'] - diff --git a/venv/Lib/site-packages/pygame/tests/touch_test.py b/venv/Lib/site-packages/pygame/tests/touch_test.py index 7cb4652b325a0fbbb12037af642d7b0a8e46b4d5..259a2c70f601b8283cb5c508750195f7e5d60caf 100644 --- a/venv/Lib/site-packages/pygame/tests/touch_test.py +++ b/venv/Lib/site-packages/pygame/tests/touch_test.py @@ -1,13 +1,14 @@ import unittest +import os import pygame from pygame._sdl2 import touch +from pygame.tests.test_utils import question has_touchdevice = touch.get_num_devices() > 0 class TouchTest(unittest.TestCase): - @classmethod def setUpClass(cls): pygame.display.init() @@ -19,26 +20,78 @@ class TouchTest(unittest.TestCase): def test_num_devices(self): touch.get_num_devices() - @unittest.skipIf(not has_touchdevice, 'no touch devices found') + @unittest.skipIf(not has_touchdevice, "no touch devices found") def test_get_device(self): touch.get_device(0) - def test_num_fingers__invalid(self): + def test_get_device__invalid(self): self.assertRaises(pygame.error, touch.get_device, -1234) - self.assertRaises(TypeError, touch.get_device, 'test') + self.assertRaises(TypeError, touch.get_device, "test") - @unittest.skipIf(not has_touchdevice, 'no touch devices found') + @unittest.skipIf(not has_touchdevice, "no touch devices found") def test_num_fingers(self): touch.get_num_fingers(touch.get_device(0)) def test_num_fingers__invalid(self): - self.assertRaises(TypeError, touch.get_num_fingers, 'test') + self.assertRaises(TypeError, touch.get_num_fingers, "test") self.assertRaises(pygame.error, touch.get_num_fingers, -1234) - @unittest.skipIf(not has_touchdevice, 'no touch devices found') - def todo_test_get_finger(self): + +class TouchInteractiveTest(unittest.TestCase): + __tags__ = ["interactive"] + + @unittest.skipIf(not has_touchdevice, "no touch devices found") + def test_get_finger(self): """ask for touch input and check the dict""" + pygame.display.init() + pygame.font.init() + + os.environ["SDL_VIDEO_WINDOW_POS"] = "50,50" + screen = pygame.display.set_mode((800, 600)) + screen.fill((255, 255, 255)) + + font = pygame.font.Font(None, 32) + instructions_str_1 = "Please place some fingers on your touch device" + instructions_str_2 = ( + "Close the window when finished, " "and answer the question" + ) + inst_1_render = font.render(instructions_str_1, True, pygame.Color("#000000")) + inst_2_render = font.render(instructions_str_2, True, pygame.Color("#000000")) + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + finger_data_renders = [] + num_devices = pygame._sdl2.touch.get_num_devices() + if num_devices > 0: + first_device = pygame._sdl2.touch.get_device(0) + num_fingers = pygame._sdl2.touch.get_num_fingers(first_device) + if num_fingers > 0: + for finger_index in range(0, num_fingers): + data = pygame._sdl2.touch.get_finger(first_device, finger_index) + render = font.render( + f"finger - {data}", True, pygame.Color("#000000") + ) + + finger_data_renders.append(render) + + screen.fill((255, 255, 255)) + screen.blit(inst_1_render, (5, 5)) + screen.blit(inst_2_render, (5, 40)) + for index, finger in enumerate(finger_data_renders): + screen.blit(finger, (5, 80 + (index * 40))) + + pygame.display.update() + + response = question("Does the finger data seem correct?") + self.assertTrue(response) + + pygame.display.quit() + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/transform_test.py b/venv/Lib/site-packages/pygame/tests/transform_test.py index 5fa116cc19d2511c9f14c0feb8e3551e29ede131..b7c64e919860584b91ffb8258305b001aa23dfe4 100644 --- a/venv/Lib/site-packages/pygame/tests/transform_test.py +++ b/venv/Lib/site-packages/pygame/tests/transform_test.py @@ -1,17 +1,20 @@ import unittest +import os import platform from pygame.tests import test_utils +from pygame.tests.test_utils import example_path + import pygame import pygame.transform from pygame.locals import * -def show_image(s, images = []): - #pygame.display.init() +def show_image(s, images=[]): + # pygame.display.init() size = s.get_rect()[2:] screen = pygame.display.set_mode(size) - screen.blit(s, (0,0)) + screen.blit(s, (0, 0)) pygame.display.flip() pygame.event.pump() going = True @@ -23,10 +26,12 @@ def show_image(s, images = []): going = False if e.type == KEYDOWN: if e.key in [K_s, K_a]: - if e.key == K_s: idx += 1 - if e.key == K_a: idx -= 1 + if e.key == K_s: + idx += 1 + if e.key == K_a: + idx -= 1 s = images[idx] - screen.blit(s, (0,0)) + screen.blit(s, (0, 0)) pygame.display.flip() pygame.event.pump() elif e.key in [K_ESCAPE]: @@ -34,11 +39,18 @@ def show_image(s, images = []): pygame.display.quit() pygame.display.init() -def threshold(return_surf, surf, color, threshold = (0,0,0), diff_color = (0,0,0), change_return = True ): - """ given the color it makes return_surf only have areas with the given colour. - """ - width, height =surf.get_width(), surf.get_height() +def threshold( + return_surf, + surf, + color, + threshold=(0, 0, 0), + diff_color=(0, 0, 0), + change_return=True, +): + """given the color it makes return_surf only have areas with the given colour.""" + + width, height = surf.get_width(), surf.get_height() if change_return: return_surf.fill(diff_color) @@ -48,73 +60,73 @@ def threshold(return_surf, surf, color, threshold = (0,0,0), diff_color = (0,0,0 except ValueError: r, g, b, a = color - try: tr, tg, tb = color except ValueError: tr, tg, tb, ta = color - - similar = 0 - for y in xrange(height): - for x in xrange(width): - c1 = surf.get_at((x,y)) + for y in range(height): + for x in range(width): + c1 = surf.get_at((x, y)) - if ( (abs(c1[0] - r) < tr) & - (abs(c1[1] - g) < tg) & - (abs(c1[2] - b) < tb) ): + if (abs(c1[0] - r) < tr) & (abs(c1[1] - g) < tg) & (abs(c1[2] - b) < tb): # this pixel is within the threshold. if change_return: - return_surf.set_at((x,y), c1) + return_surf.set_at((x, y), c1) similar += 1 - #else: - # print c1, c2 - + # else: + # print(c1, c2) return similar -class TransformModuleTest( unittest.TestCase ): +class TransformModuleTest(unittest.TestCase): + def test_scale__alpha(self): + """see if set_alpha information is kept.""" - def test_scale__alpha( self ): - """ see if set_alpha information is kept. - """ - - s = pygame.Surface((32,32)) + s = pygame.Surface((32, 32)) s.set_alpha(55) - self.assertEqual(s.get_alpha(),55) + self.assertEqual(s.get_alpha(), 55) - s = pygame.Surface((32,32)) + s = pygame.Surface((32, 32)) s.set_alpha(55) - s2 = pygame.transform.scale(s, (64,64)) + s2 = pygame.transform.scale(s, (64, 64)) s3 = s.copy() - self.assertEqual(s.get_alpha(),s3.get_alpha()) - self.assertEqual(s.get_alpha(),s2.get_alpha()) + self.assertEqual(s.get_alpha(), s3.get_alpha()) + self.assertEqual(s.get_alpha(), s2.get_alpha()) - def test_scale__destination( self ): - """ see if the destination surface can be passed in to use. - """ + def test_scale__destination(self): + """see if the destination surface can be passed in to use.""" - s = pygame.Surface((32,32)) - s2 = pygame.transform.scale(s, (64,64)) + s = pygame.Surface((32, 32)) + s2 = pygame.transform.scale(s, (64, 64)) s3 = s2.copy() - s3 = pygame.transform.scale(s, (64,64), s3) - pygame.transform.scale(s, (64,64), s2) + # Also validate keyword arguments + s3 = pygame.transform.scale(surface=s, size=(64, 64), dest_surface=s3) + pygame.transform.scale(s, (64, 64), s2) # the wrong size surface is past in. Should raise an error. - self.assertRaises(ValueError, pygame.transform.scale, s, (33,64), s3) + self.assertRaises(ValueError, pygame.transform.scale, s, (33, 64), s3) - s = pygame.Surface((32,32)) - s2 = pygame.transform.smoothscale(s, (64,64)) + s = pygame.Surface((32, 32)) + s2 = pygame.transform.smoothscale(s, (64, 64)) s3 = s2.copy() - s3 = pygame.transform.smoothscale(s, (64,64), s3) - pygame.transform.smoothscale(s, (64,64), s2) + # Also validate keyword arguments + s3 = pygame.transform.smoothscale(surface=s, size=(64, 64), dest_surface=s3) # the wrong size surface is past in. Should raise an error. - self.assertRaises(ValueError, pygame.transform.smoothscale, s, (33,64), s3) + self.assertRaises(ValueError, pygame.transform.smoothscale, s, (33, 64), s3) + + def test_scale__vector2(self): + s = pygame.Surface((32, 32)) + s2 = pygame.transform.scale(s, pygame.Vector2(64, 64)) + s3 = pygame.transform.smoothscale(s, pygame.Vector2(64, 64)) + + self.assertEqual((64, 64), s2.get_size()) + self.assertEqual((64, 64), s3.get_size()) def test_scale__zero_surface_transform(self): tmp_surface = pygame.transform.scale(pygame.Surface((128, 128)), (0, 0)) @@ -122,6 +134,75 @@ class TransformModuleTest( unittest.TestCase ): tmp_surface = pygame.transform.scale(tmp_surface, (128, 128)) self.assertEqual(tmp_surface.get_size(), (128, 128)) + def test_scale_by(self): + s = pygame.Surface((32, 32)) + + s2 = pygame.transform.scale_by(s, 2) + self.assertEqual((64, 64), s2.get_size()) + + s2 = pygame.transform.scale_by(s, factor=(2.0, 1.5)) + self.assertEqual((64, 48), s2.get_size()) + + dest = pygame.Surface((64, 48)) + pygame.transform.scale_by(s, (2.0, 1.5), dest_surface=dest) + + def test_smoothscale_by(self): + s = pygame.Surface((32, 32)) + + s2 = pygame.transform.smoothscale_by(s, 2) + self.assertEqual((64, 64), s2.get_size()) + + s2 = pygame.transform.smoothscale_by(s, factor=(2.0, 1.5)) + self.assertEqual((64, 48), s2.get_size()) + + dest = pygame.Surface((64, 48)) + pygame.transform.smoothscale_by(s, (2.0, 1.5), dest_surface=dest) + + def test_grayscale(self): + s = pygame.Surface((32, 32)) + s.fill((255, 0, 0)) + + s2 = pygame.transform.grayscale(s) + self.assertEqual(pygame.transform.average_color(s2)[0], 76) + self.assertEqual(pygame.transform.average_color(s2)[1], 76) + self.assertEqual(pygame.transform.average_color(s2)[2], 76) + + dest = pygame.Surface((32, 32), depth=32) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 76) + self.assertEqual(pygame.transform.average_color(dest)[1], 76) + self.assertEqual(pygame.transform.average_color(dest)[2], 76) + + dest = pygame.Surface((32, 32), depth=32) + s.fill((34, 12, 65)) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 24) + self.assertEqual(pygame.transform.average_color(dest)[1], 24) + self.assertEqual(pygame.transform.average_color(dest)[2], 24) + + dest = pygame.Surface((32, 32), depth=32) + s.fill((123, 123, 123)) + pygame.transform.grayscale(s, dest) + self.assertIn(pygame.transform.average_color(dest)[0], [123, 122]) + self.assertIn(pygame.transform.average_color(dest)[1], [123, 122]) + self.assertIn(pygame.transform.average_color(dest)[2], [123, 122]) + + s = pygame.Surface((32, 32), depth=24) + s.fill((255, 0, 0)) + dest = pygame.Surface((32, 32), depth=24) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 76) + self.assertEqual(pygame.transform.average_color(dest)[1], 76) + self.assertEqual(pygame.transform.average_color(dest)[2], 76) + + s = pygame.Surface((32, 32), depth=16) + s.fill((255, 0, 0)) + dest = pygame.Surface((32, 32), depth=16) + pygame.transform.grayscale(s, dest) + self.assertEqual(pygame.transform.average_color(dest)[0], 72) + self.assertEqual(pygame.transform.average_color(dest)[1], 76) + self.assertEqual(pygame.transform.average_color(dest)[2], 72) + def test_threshold__honors_third_surface(self): # __doc__ for threshold as of Tue 07/15/2008 @@ -142,15 +223,15 @@ class TransformModuleTest( unittest.TestCase ): # the original_color is within the threshold of the threshold_color threshold = (20, 20, 20, 20) - original_color = (25,25,25,25) + original_color = (25, 25, 25, 25) threshold_color = (10, 10, 10, 10) # Surfaces original_surface = pygame.Surface(size, pygame.SRCALPHA, 32) - dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) # Third surface is used in lieu of 3rd position arg color - third_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + third_surface = pygame.Surface(size, pygame.SRCALPHA, 32) # Color filling original_surface.fill(original_color) @@ -159,35 +240,35 @@ class TransformModuleTest( unittest.TestCase ): ################################################################ # All pixels for color should be within threshold # - pixels_within_threshold = pygame.transform.threshold ( - dest_surf=None, - surf=original_surface, + pixels_within_threshold = pygame.transform.threshold( + dest_surface=None, + surface=original_surface, search_color=threshold_color, threshold=threshold, set_color=None, - set_behavior=0 + set_behavior=0, ) - self.assertEqual(w*h, pixels_within_threshold) + self.assertEqual(w * h, pixels_within_threshold) ################################################################ # This should respect third_surface colors in place of 3rd arg # color Should be the same as: surface.fill(threshold_color) # all within threshold - pixels_within_threshold = pygame.transform.threshold ( - dest_surf=None, - surf=original_surface, + pixels_within_threshold = pygame.transform.threshold( + dest_surface=None, + surface=original_surface, search_color=None, threshold=threshold, set_color=None, set_behavior=0, search_surf=third_surface, ) - self.assertEqual(w*h, pixels_within_threshold) + self.assertEqual(w * h, pixels_within_threshold) def test_threshold_dest_surf_not_change(self): - """ the pixels within the threshold. + """the pixels within the threshold. All pixels not within threshold are changed to set_color. So there should be none changed in this test. @@ -212,8 +293,8 @@ class TransformModuleTest( unittest.TestCase ): THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 pixels_within_threshold = pygame.transform.threshold( - dest_surf=dest_surf, - surf=surf, + dest_surface=dest_surf, + surface=surf, search_color=None, threshold=threshold, set_color=set_color, @@ -222,7 +303,7 @@ class TransformModuleTest( unittest.TestCase ): ) # # Return, of pixels within threshold is correct - self.assertEqual(w*h, pixels_within_threshold) + self.assertEqual(w * h, pixels_within_threshold) # # Size of dest surface is correct dest_rect = dest_surf.get_rect() @@ -237,8 +318,7 @@ class TransformModuleTest( unittest.TestCase ): self.assertEqual(dest_surf.get_at(pt), original_dest_color) def test_threshold_dest_surf_all_changed(self): - """ Lowering the threshold, expecting changed surface - """ + """Lowering the threshold, expecting changed surface""" (w, h) = size = (32, 32) threshold = (20, 20, 20, 20) @@ -256,7 +336,7 @@ class TransformModuleTest( unittest.TestCase ): dest_surf.fill(original_dest_color) THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 - pixels_within_threshold = pygame.transform.threshold ( + pixels_within_threshold = pygame.transform.threshold( dest_surf, surf, search_color=None, @@ -277,8 +357,7 @@ class TransformModuleTest( unittest.TestCase ): self.assertEqual(dest_surf.get_at(pt), set_color) def test_threshold_count(self): - """ counts the colors, and not changes them. - """ + """counts the colors, and not changes them.""" surf_size = (32, 32) surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) search_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) @@ -292,24 +371,27 @@ class TransformModuleTest( unittest.TestCase ): # There is no destination surface, but we ask to change it. # This should be an error. - self.assertRaises(TypeError, pygame.transform.threshold, - None, - surf, - search_color) + self.assertRaises( + TypeError, pygame.transform.threshold, None, surf, search_color + ) # from pygame.transform import THRESHOLD_BEHAVIOR_COUNT THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 - self.assertRaises(TypeError, pygame.transform.threshold, + self.assertRaises( + TypeError, + pygame.transform.threshold, None, surf, search_color, - set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF) + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + ) THRESHOLD_BEHAVIOR_COUNT = 0 num_threshold_pixels = pygame.transform.threshold( - dest_surf=None, - surf=surf, + dest_surface=None, + surface=surf, search_color=search_color, - set_behavior=THRESHOLD_BEHAVIOR_COUNT) + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) self.assertEqual(num_threshold_pixels, 2) def test_threshold_search_surf(self): @@ -334,40 +416,50 @@ class TransformModuleTest( unittest.TestCase ): THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 # TypeError: if search_surf is used, search_color should be None - self.assertRaises(TypeError, pygame.transform.threshold, + self.assertRaises( + TypeError, + pygame.transform.threshold, dest_surf, surf, search_color, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, - search_surf=search_surf) + search_surf=search_surf, + ) # surf, dest_surf, and search_surf should all be the same size. # Check surface sizes are the same size. different_sized_surf = pygame.Surface((22, 33), pygame.SRCALPHA, 32) - self.assertRaises(TypeError, pygame.transform.threshold, + self.assertRaises( + TypeError, + pygame.transform.threshold, different_sized_surf, surf, search_color=None, set_color=None, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, - search_surf=search_surf) + search_surf=search_surf, + ) - self.assertRaises(TypeError, pygame.transform.threshold, + self.assertRaises( + TypeError, + pygame.transform.threshold, dest_surf, surf, search_color=None, set_color=None, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, - search_surf=different_sized_surf) + search_surf=different_sized_surf, + ) # We look to see if colors in search_surf are in surf. num_threshold_pixels = pygame.transform.threshold( - dest_surf=dest_surf, - surf=surf, + dest_surface=dest_surf, + surface=surf, search_color=None, set_color=None, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, - search_surf=search_surf) + search_surf=search_surf, + ) num_pixels_within = 2 self.assertEqual(num_threshold_pixels, num_pixels_within) @@ -380,24 +472,23 @@ class TransformModuleTest( unittest.TestCase ): set_color=None, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, search_surf=search_surf, - inverse_set=True) + inverse_set=True, + ) self.assertEqual(num_threshold_pixels, 2) def test_threshold_inverse_set(self): - """ changes the pixels within the threshold, and not outside. - """ + """changes the pixels within the threshold, and not outside.""" surf_size = (32, 32) _dest_surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) _surf = pygame.Surface(surf_size, pygame.SRCALPHA, 32) - dest_surf = _dest_surf # surface we are changing. - surf = _surf # surface we are looking at - search_color = (55, 55, 55, 255) # color we are searching for. - threshold = (0, 0, 0, 0) # within this distance from search_color. - set_color = (245, 245, 245, 255) # color we set. - inverse_set = 1 # pixels within threshold are changed to 'set_color' - + dest_surf = _dest_surf # surface we are changing. + surf = _surf # surface we are looking at + search_color = (55, 55, 55, 255) # color we are searching for. + threshold = (0, 0, 0, 0) # within this distance from search_color. + set_color = (245, 245, 245, 255) # color we set. + inverse_set = 1 # pixels within threshold are changed to 'set_color' original_color = (10, 10, 10, 255) surf.fill(original_color) @@ -410,7 +501,6 @@ class TransformModuleTest( unittest.TestCase ): dest_surf.set_at((0, 0), search_color) dest_surf.set_at((12, 5), search_color) - THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 num_threshold_pixels = pygame.transform.threshold( dest_surf, @@ -419,162 +509,168 @@ class TransformModuleTest( unittest.TestCase ): threshold=threshold, set_color=set_color, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, - inverse_set=1) + inverse_set=1, + ) self.assertEqual(num_threshold_pixels, 2) # only two pixels changed to diff_color. self.assertEqual(dest_surf.get_at((0, 0)), set_color) self.assertEqual(dest_surf.get_at((12, 5)), set_color) - # other pixels should be the same as they were before. # We just check one other pixel, not all of them. self.assertEqual(dest_surf.get_at((2, 2)), original_color) -#XXX + # XXX def test_threshold_non_src_alpha(self): - - result = pygame.Surface((10,10)) - s1 = pygame.Surface((10,10)) - s2 = pygame.Surface((10,10)) - s3 = pygame.Surface((10,10)) - s4 = pygame.Surface((10,10)) + result = pygame.Surface((10, 10)) + s1 = pygame.Surface((10, 10)) + s2 = pygame.Surface((10, 10)) + s3 = pygame.Surface((10, 10)) + s4 = pygame.Surface((10, 10)) x = s1.fill((0, 0, 0)) - s1.set_at((0,0), (32, 20, 0 )) - - x = s2.fill((0,20,0)) - x = s3.fill((0,0,0)) - x = s4.fill((0,0,0)) - s2.set_at((0,0), (33, 21, 0 )) - s2.set_at((3,0), (63, 61, 0 )) - s3.set_at((0,0), (112, 31, 0 )) - s4.set_at((0,0), (11, 31, 0 )) - s4.set_at((1,1), (12, 31, 0 )) - - self.assertEqual(s1.get_at((0,0)), (32, 20, 0, 255)) - self.assertEqual(s2.get_at((0,0)), (33, 21, 0, 255)) + s1.set_at((0, 0), (32, 20, 0)) + + x = s2.fill((0, 20, 0)) + x = s3.fill((0, 0, 0)) + x = s4.fill((0, 0, 0)) + s2.set_at((0, 0), (33, 21, 0)) + s2.set_at((3, 0), (63, 61, 0)) + s3.set_at((0, 0), (112, 31, 0)) + s4.set_at((0, 0), (11, 31, 0)) + s4.set_at((1, 1), (12, 31, 0)) + + self.assertEqual(s1.get_at((0, 0)), (32, 20, 0, 255)) + self.assertEqual(s2.get_at((0, 0)), (33, 21, 0, 255)) self.assertEqual((0, 0), (s1.get_flags(), s2.get_flags())) - - similar_color = (255, 255, 255, 255) diff_color = (222, 0, 0, 255) threshold_color = (20, 20, 20, 255) THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 num_threshold_pixels = pygame.transform.threshold( - dest_surf=result, - surf=s1, + dest_surface=result, + surface=s1, search_color=similar_color, threshold=threshold_color, set_color=diff_color, - set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR) + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + ) self.assertEqual(num_threshold_pixels, 0) num_threshold_pixels = pygame.transform.threshold( - dest_surf=result, - surf=s1, + dest_surface=result, + surface=s1, search_color=(40, 40, 0), threshold=threshold_color, set_color=diff_color, - set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR) + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, + ) self.assertEqual(num_threshold_pixels, 1) - - self.assertEqual(result.get_at((0,0)), diff_color) - + self.assertEqual(result.get_at((0, 0)), diff_color) def test_threshold__uneven_colors(self): - (w,h) = size = (16, 16) + (w, h) = size = (16, 16) original_surface = pygame.Surface(size, pygame.SRCALPHA, 32) - dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) + dest_surface = pygame.Surface(size, pygame.SRCALPHA, 32) original_surface.fill(0) threshold_color_template = [5, 5, 5, 5] - threshold_template = [6, 6, 6, 6] + threshold_template = [6, 6, 6, 6] ################################################################ - for pos in range(len('rgb')): + for pos in range(len("rgb")): threshold_color = threshold_color_template[:] - threshold = threshold_template[:] + threshold = threshold_template[:] threshold_color[pos] = 45 threshold[pos] = 50 - pixels_within_threshold = pygame.transform.threshold ( + pixels_within_threshold = pygame.transform.threshold( None, original_surface, threshold_color, threshold, set_color=None, - set_behavior=0 + set_behavior=0, ) - self.assertEqual(w*h, pixels_within_threshold) + self.assertEqual(w * h, pixels_within_threshold) ################################################################ def test_threshold_set_behavior2(self): - """ raises an error when set_behavior=2 and set_color is not None. - """ + """raises an error when set_behavior=2 and set_color is not None.""" from pygame.transform import threshold - s1 = pygame.Surface((32,32), SRCALPHA, 32) - s2 = pygame.Surface((32,32), SRCALPHA, 32) + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 - self.assertRaises(TypeError, threshold, - dest_surf=s2, - surf=s1, + self.assertRaises( + TypeError, + threshold, + dest_surface=s2, + surface=s1, search_color=(30, 30, 30), threshold=(11, 11, 11), set_color=(255, 0, 0), - set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF) + set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, + ) def test_threshold_set_behavior0(self): - """ raises an error when set_behavior=1 - and set_color is not None, - and dest_surf is not None. + """raises an error when set_behavior=1 + and set_color is not None, + and dest_surf is not None. """ from pygame.transform import threshold - s1 = pygame.Surface((32,32), SRCALPHA, 32) - s2 = pygame.Surface((32,32), SRCALPHA, 32) + + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) THRESHOLD_BEHAVIOR_COUNT = 0 - self.assertRaises(TypeError, threshold, - dest_surf=None, - surf=s2, + self.assertRaises( + TypeError, + threshold, + dest_surface=None, + surface=s2, search_color=(30, 30, 30), threshold=(11, 11, 11), set_color=(0, 0, 0), - set_behavior=THRESHOLD_BEHAVIOR_COUNT) + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) - self.assertRaises(TypeError, threshold, - dest_surf=s1, - surf=s2, + self.assertRaises( + TypeError, + threshold, + dest_surface=s1, + surface=s2, search_color=(30, 30, 30), threshold=(11, 11, 11), set_color=None, - set_behavior=THRESHOLD_BEHAVIOR_COUNT) + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) threshold( - dest_surf=None, - surf=s2, + dest_surface=None, + surface=s2, search_color=(30, 30, 30), threshold=(11, 11, 11), set_color=None, - set_behavior=THRESHOLD_BEHAVIOR_COUNT) - + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) def test_threshold_from_surface(self): - """ Set similar pixels in 'dest_surf' to color in the 'surf'. - """ + """Set similar pixels in 'dest_surf' to color in the 'surf'.""" from pygame.transform import threshold - surf = pygame.Surface((32,32), SRCALPHA, 32) - dest_surf = pygame.Surface((32,32), SRCALPHA, 32) + surf = pygame.Surface((32, 32), SRCALPHA, 32) + dest_surf = pygame.Surface((32, 32), SRCALPHA, 32) surf_color = (40, 40, 40, 255) dest_color = (255, 255, 255) surf.fill(surf_color) @@ -582,26 +678,27 @@ class TransformModuleTest( unittest.TestCase ): THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 num_threshold_pixels = threshold( - dest_surf=dest_surf, - surf=surf, + dest_surface=dest_surf, + surface=surf, search_color=(30, 30, 30), threshold=(11, 11, 11), set_color=None, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF, - inverse_set=1) + inverse_set=1, + ) - self.assertEqual(num_threshold_pixels, dest_surf.get_height() * dest_surf.get_width()) + self.assertEqual( + num_threshold_pixels, dest_surf.get_height() * dest_surf.get_width() + ) self.assertEqual(dest_surf.get_at((0, 0)), surf_color) - def test_threshold__surface(self): - """ - """ + """ """ from pygame.transform import threshold - s1 = pygame.Surface((32,32), SRCALPHA, 32) - s2 = pygame.Surface((32,32), SRCALPHA, 32) - s3 = pygame.Surface((1,1), SRCALPHA, 32) + s1 = pygame.Surface((32, 32), SRCALPHA, 32) + s2 = pygame.Surface((32, 32), SRCALPHA, 32) + s3 = pygame.Surface((1, 1), SRCALPHA, 32) THRESHOLD_BEHAVIOR_FROM_SEARCH_SURF = 2 # # only one pixel should not be changed. @@ -611,8 +708,8 @@ class TransformModuleTest( unittest.TestCase ): # # set the similar pixels in destination surface to the color # # in the first surface. # num_threshold_pixels = threshold( - # dest_surf=s2, - # surf=s1, + # dest_surface=s2, + # surface=s1, # search_color=(30,30,30), # threshold=(11,11,11), # set_color=None, @@ -624,7 +721,6 @@ class TransformModuleTest( unittest.TestCase ): # self.assertEqual(s2.get_at((0,1)), (40, 40, 40, 255)) # self.assertEqual(s2.get_at((17,1)), (40, 40, 40, 255)) - # # abs(40 - 255) < 100 # #(abs(c1[0] - r) < tr) @@ -634,51 +730,51 @@ class TransformModuleTest( unittest.TestCase ): # self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width())) - # only one pixel should not be changed. s1.fill((40, 40, 40)) s1.set_at((0, 0), (170, 170, 170)) THRESHOLD_BEHAVIOR_COUNT = 0 num_threshold_pixels = threshold( - dest_surf=None, - surf=s1, + dest_surface=None, + surface=s1, search_color=(30, 30, 30), threshold=(11, 11, 11), set_color=None, - set_behavior=THRESHOLD_BEHAVIOR_COUNT) - - #num_threshold_pixels = threshold(s2, s1, (30,30,30)) - self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) -1) + set_behavior=THRESHOLD_BEHAVIOR_COUNT, + ) + # num_threshold_pixels = threshold(s2, s1, (30,30,30)) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) # test end markers. 0, and 255 # the pixels are different by 1. - s1.fill((254,254,254)) - s2.fill((255,255,255)) - s3.fill((255,255,255)) - s1.set_at( (0,0), (170, 170, 170) ) - num_threshold_pixels = threshold(None, s1, (254,254,254), (1,1,1), - None, THRESHOLD_BEHAVIOR_COUNT) - self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) -1) - + s1.fill((254, 254, 254)) + s2.fill((255, 255, 255)) + s3.fill((255, 255, 255)) + s1.set_at((0, 0), (170, 170, 170)) + num_threshold_pixels = threshold( + None, s1, (254, 254, 254), (1, 1, 1), None, THRESHOLD_BEHAVIOR_COUNT + ) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) # compare the two surfaces. Should be all but one matching. - num_threshold_pixels = threshold(None, s1, None, (1,1,1), - None, THRESHOLD_BEHAVIOR_COUNT, s2) - self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) -1) - + num_threshold_pixels = threshold( + None, s1, None, (1, 1, 1), None, THRESHOLD_BEHAVIOR_COUNT, s2 + ) + self.assertEqual(num_threshold_pixels, (s1.get_height() * s1.get_width()) - 1) # within (0,0,0) threshold? Should match no pixels. - num_threshold_pixels = threshold(None, s1, (253,253,253), (0,0,0), - None, THRESHOLD_BEHAVIOR_COUNT) + num_threshold_pixels = threshold( + None, s1, (253, 253, 253), (0, 0, 0), None, THRESHOLD_BEHAVIOR_COUNT + ) self.assertEqual(num_threshold_pixels, 0) - # other surface within (0,0,0) threshold? Should match no pixels. - num_threshold_pixels = threshold(None, s1, None, (0,0,0), - None, THRESHOLD_BEHAVIOR_COUNT, s2) + num_threshold_pixels = threshold( + None, s1, None, (0, 0, 0), None, THRESHOLD_BEHAVIOR_COUNT, s2 + ) self.assertEqual(num_threshold_pixels, 0) def test_threshold__subclassed_surface(self): @@ -688,82 +784,118 @@ class TransformModuleTest( unittest.TestCase ): expected_depth = 32 expected_color = (90, 80, 70, 255) expected_count = 0 - surface = test_utils.SurfaceSubclass(expected_size, expected_flags, - expected_depth) - dest_surface = test_utils.SurfaceSubclass(expected_size, - expected_flags, - expected_depth) - search_surface = test_utils.SurfaceSubclass(expected_size, - expected_flags, - expected_depth) + surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + dest_surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) + search_surface = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) surface.fill((10, 10, 10)) dest_surface.fill((255, 255, 255)) search_surface.fill((20, 20, 20)) count = pygame.transform.threshold( - dest_surf=dest_surface, surf=surface, threshold=(1, 1, 1), - set_color=expected_color, search_color=None, - search_surf=search_surface) + dest_surface=dest_surface, + surface=surface, + threshold=(1, 1, 1), + set_color=expected_color, + search_color=None, + search_surf=search_surface, + ) self.assertIsInstance(dest_surface, pygame.Surface) self.assertIsInstance(dest_surface, test_utils.SurfaceSubclass) self.assertEqual(count, expected_count) - self.assertEqual(dest_surface.get_at((0,0)), expected_color) + self.assertEqual(dest_surface.get_at((0, 0)), expected_color) self.assertEqual(dest_surface.get_bitsize(), expected_depth) self.assertEqual(dest_surface.get_size(), expected_size) self.assertEqual(dest_surface.get_flags(), expected_flags) def test_laplacian(self): - """ - """ + """ """ SIZE = 32 s1 = pygame.Surface((SIZE, SIZE)) s2 = pygame.Surface((SIZE, SIZE)) - s1.fill((10,10,70)) - pygame.draw.line(s1, (255,0,0), (3,10), (20,20)) + s1.fill((10, 10, 70)) + pygame.draw.line(s1, (255, 0, 0), (3, 10), (20, 20)) # a line at the last row of the image. - pygame.draw.line(s1, (255,0,0), (0,31), (31,31)) - + pygame.draw.line(s1, (255, 0, 0), (0, 31), (31, 31)) - pygame.transform.laplacian(s1,s2) + pygame.transform.laplacian(s1, s2) - #show_image(s1) - #show_image(s2) - - self.assertEqual(s2.get_at((0,0)), (0, 0, 0, 255)) - self.assertEqual(s2.get_at((3,10)), (255,0,0,255)) - self.assertEqual(s2.get_at((0,31)), (255,0,0,255)) - self.assertEqual(s2.get_at((31,31)), (255,0,0,255)) + # show_image(s1) + # show_image(s2) + self.assertEqual(s2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((31, 31)), (255, 0, 0, 255)) # here we create the return surface. s2 = pygame.transform.laplacian(s1) - self.assertEqual(s2.get_at((0,0)), (0, 0, 0, 255)) - self.assertEqual(s2.get_at((3,10)), (255,0,0,255)) - self.assertEqual(s2.get_at((0,31)), (255,0,0,255)) - self.assertEqual(s2.get_at((31,31)), (255,0,0,255)) + self.assertEqual(s2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(s2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(s2.get_at((31, 31)), (255, 0, 0, 255)) + + def test_laplacian__24_big_endian(self): + """ """ + pygame.display.init() + try: + surf_1 = pygame.image.load( + example_path(os.path.join("data", "laplacian.png")) + ) + SIZE = 32 + surf_2 = pygame.Surface((SIZE, SIZE), 0, 24) + # s1.fill((10, 10, 70)) + # pygame.draw.line(s1, (255, 0, 0), (3, 10), (20, 20)) + + # a line at the last row of the image. + # pygame.draw.line(s1, (255, 0, 0), (0, 31), (31, 31)) + + # Also validate keyword arguments + pygame.transform.laplacian(surface=surf_1, dest_surface=surf_2) + + # show_image(s1) + # show_image(s2) + + self.assertEqual(surf_2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(surf_2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((31, 31)), (255, 0, 0, 255)) + + # here we create the return surface. + surf_2 = pygame.transform.laplacian(surf_1) + + self.assertEqual(surf_2.get_at((0, 0)), (0, 0, 0, 255)) + self.assertEqual(surf_2.get_at((3, 10)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((0, 31)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((31, 31)), (255, 0, 0, 255)) + finally: + pygame.display.quit() def test_average_surfaces(self): - """ - """ + """ """ SIZE = 32 s1 = pygame.Surface((SIZE, SIZE)) s2 = pygame.Surface((SIZE, SIZE)) s3 = pygame.Surface((SIZE, SIZE)) - s1.fill((10,10,70)) - s2.fill((10,20,70)) - s3.fill((10,130,10)) + s1.fill((10, 10, 70)) + s2.fill((10, 20, 70)) + s3.fill((10, 130, 10)) surfaces = [s1, s2, s3] surfaces = [s1, s2] sr = pygame.transform.average_surfaces(surfaces) - self.assertEqual(sr.get_at((0,0)), (10,15,70,255)) - + self.assertEqual(sr.get_at((0, 0)), (10, 15, 70, 255)) self.assertRaises(TypeError, pygame.transform.average_surfaces, 1) self.assertRaises(TypeError, pygame.transform.average_surfaces, []) @@ -773,35 +905,54 @@ class TransformModuleTest( unittest.TestCase ): self.assertRaises(TypeError, pygame.transform.average_surfaces, [1, s1]) self.assertRaises(TypeError, pygame.transform.average_surfaces, [s1, s2, 1]) - self.assertRaises(TypeError, pygame.transform.average_surfaces, (s for s in [s1, s2,s3] )) - - + self.assertRaises( + TypeError, pygame.transform.average_surfaces, (s for s in [s1, s2, s3]) + ) def test_average_surfaces__24(self): - SIZE = 32 depth = 24 s1 = pygame.Surface((SIZE, SIZE), 0, depth) s2 = pygame.Surface((SIZE, SIZE), 0, depth) s3 = pygame.Surface((SIZE, SIZE), 0, depth) - s1.fill((10,10,70, 255)) - s2.fill((10,20,70, 255)) - s3.fill((10,130,10, 255)) + s1.fill((10, 10, 70, 255)) + s2.fill((10, 20, 70, 255)) + s3.fill((10, 130, 10, 255)) surfaces = [s1, s2, s3] sr = pygame.transform.average_surfaces(surfaces) - self.assertEqual( sr.get_masks(), s1.get_masks() ) - self.assertEqual( sr.get_flags(), s1.get_flags() ) - self.assertEqual( sr.get_losses(), s1.get_losses() ) + self.assertEqual(sr.get_masks(), s1.get_masks()) + self.assertEqual(sr.get_flags(), s1.get_flags()) + self.assertEqual(sr.get_losses(), s1.get_losses()) if 0: - print ( sr, s1 ) - print ( sr.get_masks(), s1.get_masks() ) - print ( sr.get_flags(), s1.get_flags() ) - print ( sr.get_losses(), s1.get_losses() ) - print ( sr.get_shifts(), s1.get_shifts() ) + print(sr, s1) + print(sr.get_masks(), s1.get_masks()) + print(sr.get_flags(), s1.get_flags()) + print(sr.get_losses(), s1.get_losses()) + print(sr.get_shifts(), s1.get_shifts()) + + self.assertEqual(sr.get_at((0, 0)), (10, 53, 50, 255)) - self.assertEqual(sr.get_at((0,0)), (10,53,50,255)) + def test_average_surfaces__24_big_endian(self): + pygame.display.init() + try: + surf_1 = pygame.image.load(example_path(os.path.join("data", "BGR.png"))) + + surf_2 = surf_1.copy() + + surfaces = [surf_1, surf_2] + self.assertEqual(surf_1.get_at((0, 0)), (255, 0, 0, 255)) + self.assertEqual(surf_2.get_at((0, 0)), (255, 0, 0, 255)) + + surf_av = pygame.transform.average_surfaces(surfaces) + self.assertEqual(surf_av.get_masks(), surf_1.get_masks()) + self.assertEqual(surf_av.get_flags(), surf_1.get_flags()) + self.assertEqual(surf_av.get_losses(), surf_1.get_losses()) + + self.assertEqual(surf_av.get_at((0, 0)), (255, 0, 0, 255)) + finally: + pygame.display.quit() def test_average_surfaces__subclassed_surfaces(self): """Ensure average_surfaces accepts subclassed surfaces.""" @@ -812,8 +963,9 @@ class TransformModuleTest( unittest.TestCase ): surfaces = [] for color in ((40, 60, 40), (60, 40, 60)): - s = test_utils.SurfaceSubclass(expected_size, expected_flags, - expected_depth) + s = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) s.fill(color) surfaces.append(s) @@ -821,7 +973,7 @@ class TransformModuleTest( unittest.TestCase ): self.assertIsInstance(surface, pygame.Surface) self.assertNotIsInstance(surface, test_utils.SurfaceSubclass) - self.assertEqual(surface.get_at((0,0)), expected_color) + self.assertEqual(surface.get_at((0, 0)), expected_color) self.assertEqual(surface.get_bitsize(), expected_depth) self.assertEqual(surface.get_size(), expected_size) self.assertEqual(surface.get_flags(), expected_flags) @@ -835,53 +987,113 @@ class TransformModuleTest( unittest.TestCase ): surfaces = [] for color in ((10, 10, 20), (20, 20, 10), (30, 30, 30)): - s = test_utils.SurfaceSubclass(expected_size, expected_flags, - expected_depth) + s = test_utils.SurfaceSubclass( + expected_size, expected_flags, expected_depth + ) s.fill(color) surfaces.append(s) expected_dest_surface = surfaces.pop() - dest_surface = pygame.transform.average_surfaces(surfaces, - expected_dest_surface) + # Also validate keyword arguments + dest_surface = pygame.transform.average_surfaces( + surfaces=surfaces, dest_surface=expected_dest_surface + ) self.assertIsInstance(dest_surface, pygame.Surface) self.assertIsInstance(dest_surface, test_utils.SurfaceSubclass) self.assertIs(dest_surface, expected_dest_surface) - self.assertEqual(dest_surface.get_at((0,0)), expected_color) + self.assertEqual(dest_surface.get_at((0, 0)), expected_color) self.assertEqual(dest_surface.get_bitsize(), expected_depth) self.assertEqual(dest_surface.get_size(), expected_size) self.assertEqual(dest_surface.get_flags(), expected_flags) def test_average_color(self): - """ - """ - - a = [24, 32] - for i in a: - s = pygame.Surface((32,32), 0, i) - s.fill((0,100,200)) - s.fill((10,50,100), (0,0,16,32)) - - self.assertEqual(pygame.transform.average_color(s),(5,75,150,0)) - self.assertEqual(pygame.transform.average_color(s, (16,0,16,32)), (0,100,200,0)) - - def todo_test_rotate(self): - - # __doc__ (as of 2008-06-25) for pygame.transform.rotate: - - # pygame.transform.rotate(Surface, angle): return Surface - # rotate an image - - # color = (128, 128, 128, 255) - # s = pygame.Surface((3, 3)) - - # s.set_at((2, 0), color) + """ """ + for i in (24, 32): + with self.subTest(f"Testing {i}-bit surface"): + s = pygame.Surface((32, 32), 0, i) + s.fill((0, 100, 200)) + s.fill((10, 50, 100), (0, 0, 16, 32)) + + self.assertEqual(pygame.transform.average_color(s), (5, 75, 150, 0)) + + # Also validate keyword arguments + avg_color = pygame.transform.average_color( + surface=s, rect=(16, 0, 16, 32) + ) + self.assertEqual(avg_color, (0, 100, 200, 0)) + + def test_average_color_considering_alpha_all_pixels_opaque(self): + """ """ + s = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s.fill((0, 100, 200, 255)) + s.fill((10, 50, 100, 255), (0, 0, 16, 32)) + + self.assertEqual( + pygame.transform.average_color(s, consider_alpha=True), (5, 75, 150, 255) + ) - # self.assertNotEqual(s.get_at((0, 0)), color) - # s = pygame.transform.rotate(s, 90) - # self.assertEqual(s.get_at((0, 0)), color) + # Also validate keyword arguments + avg_color = pygame.transform.average_color( + surface=s, rect=(16, 0, 16, 32), consider_alpha=True + ) + self.assertEqual(avg_color, (0, 100, 200, 255)) + + def test_average_color_considering_alpha(self): + """ """ + s = pygame.Surface((32, 32), pygame.SRCALPHA, 32) + s.fill((0, 100, 200, 255)) + s.fill((10, 50, 100, 128), (0, 0, 16, 32)) + + # formula for this example of half filled square + # n = number of pixels, e.g. 32 * 32 + # rgb = (n/2 * ( a_left * rgb_left) + n/2 (a_right * rgb_right) ) / (n/2 * a_left + n/2 * a_right) + # a = (n/2 * a_left + n/2 * a_right) / n + self.assertEqual( + pygame.transform.average_color(s, consider_alpha=True), (3, 83, 166, 191) + ) - self.fail() + # Also validate keyword arguments + avg_color = pygame.transform.average_color( + surface=s, rect=(0, 0, 16, 32), consider_alpha=True + ) + self.assertEqual(avg_color, (10, 50, 100, 128)) + + def test_rotate(self): + # setting colors and canvas + blue = (0, 0, 255, 255) + red = (255, 0, 0, 255) + black = (0, 0, 0) + canvas = pygame.Surface((3, 3)) + rotation = 0 + + canvas.set_at((2, 0), blue) + canvas.set_at((0, 2), red) + + self.assertEqual(canvas.get_at((0, 0)), black) + self.assertEqual(canvas.get_at((2, 0)), blue) + self.assertEqual(canvas.get_at((0, 2)), red) + + for i in range(0, 4): + if i % 2 == 0: + self.assertEqual(canvas.get_at((0, 0)), black) + elif i == 1: + self.assertEqual(canvas.get_at((0, 0)), blue) + elif i == 3: + self.assertEqual(canvas.get_at((0, 0)), red) + + rotation += 90 + # Also validate keyword arguments + canvas = pygame.transform.rotate(surface=canvas, angle=90) + + self.assertEqual(canvas.get_at((0, 0)), black) + + def test_rotate_of_0_sized_surface(self): + # This function just tests possible Segmentation Fault + canvas1 = pygame.Surface((0, 1)) + canvas2 = pygame.Surface((1, 0)) + pygame.transform.rotate(canvas1, 42) + pygame.transform.rotate(canvas2, 42) def test_rotate__lossless_at_90_degrees(self): w, h = 32, 32 @@ -889,32 +1101,50 @@ class TransformModuleTest( unittest.TestCase ): gradient = list(test_utils.gradient(w, h)) - for pt, color in gradient: s.set_at(pt, color) + for pt, color in gradient: + s.set_at(pt, color) for rotation in (90, -90): - s = pygame.transform.rotate(s,rotation) + s = pygame.transform.rotate(s, rotation) for pt, color in gradient: self.assertTrue(s.get_at(pt) == color) def test_scale2x(self): - # __doc__ (as of 2008-06-25) for pygame.transform.scale2x: - # pygame.transform.scale2x(Surface, DestSurface = None): Surface - # specialized image doubler + # pygame.transform.scale2x(Surface, DestSurface = None): Surface + # specialized image doubler w, h = 32, 32 s = pygame.Surface((w, h), pygame.SRCALPHA, 32) # s.set_at((0,0), (20, 20, 20, 255)) - s2 = pygame.transform.scale2x(s) + s1 = pygame.transform.scale2x(s) + # Also validate keyword arguments + s2 = pygame.transform.scale2x(surface=s) + self.assertEqual(s1.get_rect().size, (64, 64)) self.assertEqual(s2.get_rect().size, (64, 64)) + def test_scale2xraw(self): + w, h = 32, 32 + s = pygame.Surface((w, h), pygame.SRCALPHA, 32) + s.fill((0, 0, 0)) + pygame.draw.circle(s, (255, 0, 0), (w // 2, h // 2), (w // 3)) + + s2 = pygame.transform.scale(s, (w * 2, h * 2)) + s2_2 = pygame.transform.scale(s2, (w * 4, h * 4)) + s4 = pygame.transform.scale(s, (w * 4, h * 4)) + + self.assertEqual(s2_2.get_rect().size, (128, 128)) + + for pt in test_utils.rect_area_pts(s2_2.get_rect()): + self.assertEqual(s2_2.get_at(pt), s4.get_at(pt)) + def test_get_smoothscale_backend(self): filter_type = pygame.transform.get_smoothscale_backend() - self.assertTrue(filter_type in ['GENERIC', 'MMX', 'SSE']) + self.assertTrue(filter_type in ["GENERIC", "MMX", "SSE"]) # It would be nice to test if a non-generic type corresponds to an x86 # processor. But there is no simple test for this. platform.machine() # returns process version specific information, like 'i686'. @@ -922,106 +1152,203 @@ class TransformModuleTest( unittest.TestCase ): def test_set_smoothscale_backend(self): # All machines should allow 'GENERIC'. original_type = pygame.transform.get_smoothscale_backend() - pygame.transform.set_smoothscale_backend('GENERIC') + pygame.transform.set_smoothscale_backend("GENERIC") filter_type = pygame.transform.get_smoothscale_backend() - self.assertEqual(filter_type, 'GENERIC') + self.assertEqual(filter_type, "GENERIC") # All machines should allow returning to original value. # Also check that keyword argument works. - pygame.transform.set_smoothscale_backend(type=original_type) + pygame.transform.set_smoothscale_backend(backend=original_type) + # Something invalid. def change(): - pygame.transform.set_smoothscale_backend('mmx') + pygame.transform.set_smoothscale_backend("mmx") + self.assertRaises(ValueError, change) + # Invalid argument keyword. def change(): - pygame.transform.set_smoothscale_backend(t='GENERIC') + pygame.transform.set_smoothscale_backend(t="GENERIC") + self.assertRaises(TypeError, change) + # Invalid argument type. def change(): pygame.transform.set_smoothscale_backend(1) + self.assertRaises(TypeError, change) # Unsupported type, if possible. - if original_type != 'SSE': + if original_type != "SSE": + def change(): - pygame.transform.set_smoothscale_backend('SSE') + pygame.transform.set_smoothscale_backend("SSE") + self.assertRaises(ValueError, change) # Should be back where we started. filter_type = pygame.transform.get_smoothscale_backend() self.assertEqual(filter_type, original_type) - def todo_test_chop(self): + def test_chop(self): + original_surface = pygame.Surface((20, 20)) + pygame.draw.rect(original_surface, (255, 0, 0), (0, 0, 10, 10)) + pygame.draw.rect(original_surface, (0, 255, 0), (0, 10, 10, 10)) + pygame.draw.rect(original_surface, (0, 0, 255), (10, 0, 10, 10)) + pygame.draw.rect(original_surface, (255, 255, 0), (10, 10, 10, 10)) + # Test chopping the corner of image + rect = pygame.Rect(0, 0, 5, 15) + test_surface = pygame.transform.chop(original_surface, rect) + # Check the size of chopped image + self.assertEqual(test_surface.get_size(), (15, 5)) + # Check if the colors of the chopped image are correct + for x in range(15): + for y in range(5): + if x < 5: + self.assertEqual(test_surface.get_at((x, y)), (0, 255, 0)) + else: + self.assertEqual(test_surface.get_at((x, y)), (255, 255, 0)) + # Check if the original image stayed the same + self.assertEqual(original_surface.get_size(), (20, 20)) + for x in range(20): + for y in range(20): + if x < 10 and y < 10: + self.assertEqual(original_surface.get_at((x, y)), (255, 0, 0)) + if x < 10 < y: + self.assertEqual(original_surface.get_at((x, y)), (0, 255, 0)) + if x > 10 > y: + self.assertEqual(original_surface.get_at((x, y)), (0, 0, 255)) + if x > 10 and y > 10: + self.assertEqual(original_surface.get_at((x, y)), (255, 255, 0)) + # Test chopping the center of the surface: + rect = pygame.Rect(0, 0, 10, 10) + rect.center = original_surface.get_rect().center + # Also validate keyword arguments + test_surface = pygame.transform.chop(surface=original_surface, rect=rect) + self.assertEqual(test_surface.get_size(), (10, 10)) + for x in range(10): + for y in range(10): + if x < 5 and y < 5: + self.assertEqual(test_surface.get_at((x, y)), (255, 0, 0)) + if x < 5 < y: + self.assertEqual(test_surface.get_at((x, y)), (0, 255, 0)) + if x > 5 > y: + self.assertEqual(test_surface.get_at((x, y)), (0, 0, 255)) + if x > 5 and y > 5: + self.assertEqual(test_surface.get_at((x, y)), (255, 255, 0)) + # Test chopping with the empty rect + rect = pygame.Rect(10, 10, 0, 0) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (20, 20)) + # Test chopping the entire surface + rect = pygame.Rect(0, 0, 20, 20) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (0, 0)) + # Test chopping outside of surface + rect = pygame.Rect(5, 15, 20, 20) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (5, 15)) + rect = pygame.Rect(400, 400, 10, 10) + test_surface = pygame.transform.chop(original_surface, rect) + self.assertEqual(test_surface.get_size(), (20, 20)) + + def test_rotozoom(self): + # __doc__ (as of 2008-08-02) for pygame.transform.rotozoom: - # __doc__ (as of 2008-08-02) for pygame.transform.chop: + # pygame.transform.rotozoom(Surface, angle, scale): return Surface + # filtered scale and rotation + # + # This is a combined scale and rotation transform. The resulting + # Surface will be a filtered 32-bit Surface. The scale argument is a + # floating point value that will be multiplied by the current + # resolution. The angle argument is a floating point value that + # represents the counterclockwise degrees to rotate. A negative + # rotation angle will rotate clockwise. + + s = pygame.Surface((10, 0)) + pygame.transform.scale(s, (10, 2)) + s1 = pygame.transform.rotozoom(s, 30, 1) + # Also validate keyword arguments + s2 = pygame.transform.rotozoom(surface=s, angle=30, scale=1) + + self.assertEqual(s1.get_rect(), pygame.Rect(0, 0, 0, 0)) + self.assertEqual(s2.get_rect(), pygame.Rect(0, 0, 0, 0)) + + def test_smoothscale(self): + """Tests the stated boundaries, sizing, and color blending of smoothscale function""" + # __doc__ (as of 2008-08-02) for pygame.transform.smoothscale: - # pygame.transform.chop(Surface, rect): return Surface - # gets a copy of an image with an interior area removed - # - # Extracts a portion of an image. All vertical and horizontal pixels - # surrounding the given rectangle area are removed. The corner areas - # (diagonal to the rect) are then brought together. (The original - # image is not altered by this operation.) - # - # NOTE: If you want a "crop" that returns the part of an image within - # a rect, you can blit with a rect to a new surface or copy a - # subsurface. + # pygame.transform.smoothscale(Surface, (width, height), DestSurface = + # None): return Surface + # + # scale a surface to an arbitrary size smoothly + # + # Uses one of two different algorithms for scaling each dimension of + # the input surface as required. For shrinkage, the output pixels are + # area averages of the colors they cover. For expansion, a bilinear + # filter is used. For the amd64 and i686 architectures, optimized MMX + # routines are included and will run much faster than other machine + # types. The size is a 2 number sequence for (width, height). This + # function only works for 24-bit or 32-bit surfaces. An exception + # will be thrown if the input surface bit depth is less than 24. + # + # New in pygame 1.8 - self.fail() + # check stated exceptions + def smoothscale_low_bpp(): + starting_surface = pygame.Surface((20, 20), depth=12) + smoothscaled_surface = pygame.transform.smoothscale( + starting_surface, (10, 10) + ) - def todo_test_rotozoom(self): + self.assertRaises(ValueError, smoothscale_low_bpp) - # __doc__ (as of 2008-08-02) for pygame.transform.rotozoom: + def smoothscale_high_bpp(): + starting_surface = pygame.Surface((20, 20), depth=48) + smoothscaled_surface = pygame.transform.smoothscale( + starting_surface, (10, 10) + ) - # pygame.transform.rotozoom(Surface, angle, scale): return Surface - # filtered scale and rotation - # - # This is a combined scale and rotation transform. The resulting - # Surface will be a filtered 32-bit Surface. The scale argument is a - # floating point value that will be multiplied by the current - # resolution. The angle argument is a floating point value that - # represents the counterclockwise degrees to rotate. A negative - # rotation angle will rotate clockwise. + self.assertRaises(ValueError, smoothscale_high_bpp) - self.fail() + def smoothscale_invalid_scale(): + starting_surface = pygame.Surface((20, 20), depth=32) + smoothscaled_surface = pygame.transform.smoothscale( + starting_surface, (-1, -1) + ) - def todo_test_smoothscale(self): - # __doc__ (as of 2008-08-02) for pygame.transform.smoothscale: + self.assertRaises(ValueError, smoothscale_invalid_scale) - # pygame.transform.smoothscale(Surface, (width, height), DestSurface = - # None): return Surface - # - # scale a surface to an arbitrary size smoothly - # - # Uses one of two different algorithms for scaling each dimension of - # the input surface as required. For shrinkage, the output pixels are - # area averages of the colors they cover. For expansion, a bilinear - # filter is used. For the amd64 and i686 architectures, optimized MMX - # routines are included and will run much faster than other machine - # types. The size is a 2 number sequence for (width, height). This - # function only works for 24-bit or 32-bit surfaces. An exception - # will be thrown if the input surface bit depth is less than 24. - # - # New in pygame 1.8 - - self.fail() + # Test Color Blending Scaling-Up + two_pixel_surface = pygame.Surface((2, 1), depth=32) + two_pixel_surface.fill(pygame.Color(0, 0, 0), pygame.Rect(0, 0, 1, 1)) + two_pixel_surface.fill(pygame.Color(255, 255, 255), pygame.Rect(1, 0, 1, 1)) + for k in [2**x for x in range(5, 8)]: # Enlarge to targets 32, 64...256 + bigger_surface = pygame.transform.smoothscale(two_pixel_surface, (k, 1)) + self.assertEqual( + bigger_surface.get_at((k // 2, 0)), pygame.Color(127, 127, 127) + ) + self.assertEqual(bigger_surface.get_size(), (k, 1)) + # Test Color Blending Scaling-Down + two_five_six_surf = pygame.Surface((256, 1), depth=32) + two_five_six_surf.fill(pygame.Color(0, 0, 0), pygame.Rect(0, 0, 128, 1)) + two_five_six_surf.fill(pygame.Color(255, 255, 255), pygame.Rect(128, 0, 128, 1)) + for k in range(3, 11, 2): # Shrink to targets 3, 5...11 pixels wide + smaller_surface = pygame.transform.smoothscale(two_five_six_surf, (k, 1)) + self.assertEqual( + smaller_surface.get_at(((k // 2), 0)), pygame.Color(127, 127, 127) + ) + self.assertEqual(smaller_surface.get_size(), (k, 1)) class TransformDisplayModuleTest(unittest.TestCase): - def setUp(self): pygame.display.init() + pygame.display.set_mode((320, 200)) def tearDown(self): pygame.display.quit() def test_flip(self): - """ honors the set_color key on the returned surface from flip. - """ - from pygame.tests.test_utils import example_path - - pygame.display.set_mode((320, 200)) - - fullname = example_path('data/chimp.bmp') - image_loaded = pygame.image.load(fullname) + """honors the set_color key on the returned surface from flip.""" + image_loaded = pygame.image.load(example_path("data/chimp.png")) image = pygame.Surface(image_loaded.get_size(), 0, 32) image.blit(image_loaded, (0, 0)) @@ -1037,13 +1364,16 @@ class TransformDisplayModuleTest(unittest.TestCase): surf.fill((255, 255, 255)) surf2.fill((255, 255, 255)) - colorkey = image.get_at((0,0)) + colorkey = image.get_at((0, 0)) image.set_colorkey(colorkey, RLEACCEL) timage = pygame.transform.flip(image, 1, 0) - colorkey = image_converted.get_at((0,0)) + colorkey = image_converted.get_at((0, 0)) image_converted.set_colorkey(colorkey, RLEACCEL) - timage_converted = pygame.transform.flip(image_converted, 1, 0) + # Also validate keyword arguments + timage_converted = pygame.transform.flip( + surface=image_converted, flip_x=1, flip_y=0 + ) # blit the flipped surface, and non flipped surface. surf.blit(timage, (0, 0)) @@ -1061,14 +1391,8 @@ class TransformDisplayModuleTest(unittest.TestCase): self.assertEqual(surf.get_at((0, 0)), surf2.get_at((0, 0))) def test_flip_alpha(self): - """ returns a surface with the same properties as the input. - """ - from pygame.tests.test_utils import example_path - - pygame.display.set_mode((320, 200)) - - fullname = example_path('data/chimp.bmp') - image_loaded = pygame.image.load(fullname) + """returns a surface with the same properties as the input.""" + image_loaded = pygame.image.load(example_path("data/chimp.png")) image_alpha = pygame.Surface(image_loaded.get_size(), pygame.SRCALPHA, 32) image_alpha.blit(image_loaded, (0, 0)) @@ -1076,7 +1400,7 @@ class TransformDisplayModuleTest(unittest.TestCase): surf = pygame.Surface(image_loaded.get_size(), 0, 32) surf2 = pygame.Surface(image_loaded.get_size(), 0, 32) - colorkey = image_alpha.get_at((0,0)) + colorkey = image_alpha.get_at((0, 0)) image_alpha.set_colorkey(colorkey, RLEACCEL) timage_alpha = pygame.transform.flip(image_alpha, 1, 0) @@ -1092,5 +1416,5 @@ class TransformDisplayModuleTest(unittest.TestCase): self.assertEqual(surf2.get_at((0, 0)), (255, 0, 0, 255)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/tests/version_test.py b/venv/Lib/site-packages/pygame/tests/version_test.py index 93051c7fa36beaffdb066f59e6b4f0d9e3e65312..ba0bb3d02400f0d2342095b86be6cfc99d4c1457 100644 --- a/venv/Lib/site-packages/pygame/tests/version_test.py +++ b/venv/Lib/site-packages/pygame/tests/version_test.py @@ -2,35 +2,47 @@ import os import unittest -pg_header = os.path.join('src_c', '_pygame.h') +pg_header = os.path.join("src_c", "include", "_pygame.h") class VersionTest(unittest.TestCase): - @unittest.skipIf(not os.path.isfile(pg_header), - "Skipping because we cannot find _pygame.h") + @unittest.skipIf( + not os.path.isfile(pg_header), "Skipping because we cannot find _pygame.h" + ) def test_pg_version_consistency(self): from pygame import version + pgh_major = -1 pgh_minor = -1 pgh_patch = -1 import re - major_exp_search = re.compile('define\s+PG_MAJOR_VERSION\s+([0-9]+)').search - minor_exp_search = re.compile('define\s+PG_MINOR_VERSION\s+([0-9]+)').search - patch_exp_search = re.compile('define\s+PG_PATCH_VERSION\s+([0-9]+)').search + + major_exp_search = re.compile(r"define\s+PG_MAJOR_VERSION\s+([0-9]+)").search + minor_exp_search = re.compile(r"define\s+PG_MINOR_VERSION\s+([0-9]+)").search + patch_exp_search = re.compile(r"define\s+PG_PATCH_VERSION\s+([0-9]+)").search with open(pg_header) as f: for line in f: if pgh_major == -1: m = major_exp_search(line) - if m: pgh_major = int(m.group(1)) + if m: + pgh_major = int(m.group(1)) if pgh_minor == -1: m = minor_exp_search(line) - if m: pgh_minor = int(m.group(1)) + if m: + pgh_minor = int(m.group(1)) if pgh_patch == -1: m = patch_exp_search(line) - if m: pgh_patch = int(m.group(1)) + if m: + pgh_patch = int(m.group(1)) self.assertEqual(pgh_major, version.vernum[0]) self.assertEqual(pgh_minor, version.vernum[1]) self.assertEqual(pgh_patch, version.vernum[2]) -if __name__ == '__main__': + def test_sdl_version(self): + from pygame import version + + self.assertEqual(len(version.SDL), 3) + + +if __name__ == "__main__": unittest.main() diff --git a/venv/Lib/site-packages/pygame/threads/Py25Queue.py b/venv/Lib/site-packages/pygame/threads/Py25Queue.py deleted file mode 100644 index 603c1bd2028ff14aa1e8f00efc490f9cb277d685..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/pygame/threads/Py25Queue.py +++ /dev/null @@ -1,216 +0,0 @@ -"""A multi-producer, multi-consumer queue.""" - -from time import time as _time - -from collections import deque - -__all__ = ['Empty', 'Full', 'Queue'] - -class Empty(Exception): - "Exception raised by Queue.get(block=0)/get_nowait()." - pass - -class Full(Exception): - "Exception raised by Queue.put(block=0)/put_nowait()." - pass - -class Queue: - """Create a queue object with a given maximum size. - - If maxsize is <= 0, the queue size is infinite. - """ - def __init__(self, maxsize=0): - try: - import threading - except ImportError: - import dummy_threading as threading - self._init(maxsize) - # mutex must be held whenever the queue is mutating. All methods - # that acquire mutex must release it before returning. mutex - # is shared between the three conditions, so acquiring and - # releasing the conditions also acquires and releases mutex. - self.mutex = threading.Lock() - # Notify not_empty whenever an item is added to the queue; a - # thread waiting to get is notified then. - self.not_empty = threading.Condition(self.mutex) - # Notify not_full whenever an item is removed from the queue; - # a thread waiting to put is notified then. - self.not_full = threading.Condition(self.mutex) - # Notify all_tasks_done whenever the number of unfinished tasks - # drops to zero; thread waiting to join() is notified to resume - self.all_tasks_done = threading.Condition(self.mutex) - self.unfinished_tasks = 0 - - def task_done(self): - """Indicate that a formerly enqueued task is complete. - - Used by Queue consumer threads. For each get() used to fetch a task, - a subsequent call to task_done() tells the queue that the processing - on the task is complete. - - If a join() is currently blocking, it will resume when all items - have been processed (meaning that a task_done() call was received - for every item that had been put() into the queue). - - Raises a ValueError if called more times than there were items - placed in the queue. - """ - self.all_tasks_done.acquire() - try: - unfinished = self.unfinished_tasks - 1 - if unfinished <= 0: - if unfinished < 0: - raise ValueError('task_done() called too many times') - self.all_tasks_done.notifyAll() - self.unfinished_tasks = unfinished - finally: - self.all_tasks_done.release() - - def join(self): - """Blocks until all items in the Queue have been gotten and processed. - - The count of unfinished tasks goes up whenever an item is added to the - queue. The count goes down whenever a consumer thread calls task_done() - to indicate the item was retrieved and all work on it is complete. - - When the count of unfinished tasks drops to zero, join() unblocks. - """ - self.all_tasks_done.acquire() - try: - while self.unfinished_tasks: - self.all_tasks_done.wait() - finally: - self.all_tasks_done.release() - - def qsize(self): - """Return the approximate size of the queue (not reliable!).""" - self.mutex.acquire() - n = self._qsize() - self.mutex.release() - return n - - def empty(self): - """Return True if the queue is empty, False otherwise (not reliable!).""" - self.mutex.acquire() - n = self._empty() - self.mutex.release() - return n - - def full(self): - """Return True if the queue is full, False otherwise (not reliable!).""" - self.mutex.acquire() - n = self._full() - self.mutex.release() - return n - - def put(self, item, block=True, timeout=None): - """Put an item into the queue. - - If optional args 'block' is true and 'timeout' is None (the default), - block if necessary until a free slot is available. If 'timeout' is - a positive number, it blocks at most 'timeout' seconds and raises - the Full exception if no free slot was available within that time. - Otherwise ('block' is false), put an item on the queue if a free slot - is immediately available, else raise the Full exception ('timeout' - is ignored in that case). - """ - self.not_full.acquire() - try: - if not block: - if self._full(): - raise Full - elif timeout is None: - while self._full(): - self.not_full.wait() - else: - if timeout < 0: - raise ValueError("'timeout' must be a positive number") - endtime = _time() + timeout - while self._full(): - remaining = endtime - _time() - if remaining <= 0.0: - raise Full - self.not_full.wait(remaining) - self._put(item) - self.unfinished_tasks += 1 - self.not_empty.notify() - finally: - self.not_full.release() - - def put_nowait(self, item): - """Put an item into the queue without blocking. - - Only enqueue the item if a free slot is immediately available. - Otherwise raise the Full exception. - """ - return self.put(item, False) - - def get(self, block=True, timeout=None): - """Remove and return an item from the queue. - - If optional args 'block' is true and 'timeout' is None (the default), - block if necessary until an item is available. If 'timeout' is - a positive number, it blocks at most 'timeout' seconds and raises - the Empty exception if no item was available within that time. - Otherwise ('block' is false), return an item if one is immediately - available, else raise the Empty exception ('timeout' is ignored - in that case). - """ - self.not_empty.acquire() - try: - if not block: - if self._empty(): - raise Empty - elif timeout is None: - while self._empty(): - self.not_empty.wait() - else: - if timeout < 0: - raise ValueError("'timeout' must be a positive number") - endtime = _time() + timeout - while self._empty(): - remaining = endtime - _time() - if remaining <= 0.0: - raise Empty - self.not_empty.wait(remaining) - item = self._get() - self.not_full.notify() - return item - finally: - self.not_empty.release() - - def get_nowait(self): - """Remove and return an item from the queue without blocking. - - Only get an item if one is immediately available. Otherwise - raise the Empty exception. - """ - return self.get(False) - - # Override these methods to implement other queue organizations - # (e.g. stack or priority queue). - # These will only be called with appropriate locks held - - # Initialize the queue representation - def _init(self, maxsize): - self.maxsize = maxsize - self.queue = deque() - - def _qsize(self): - return len(self.queue) - - # Check whether the queue is empty - def _empty(self): - return not self.queue - - # Check whether the queue is full - def _full(self): - return self.maxsize > 0 and len(self.queue) == self.maxsize - - # Put a new item in the queue - def _put(self, item): - self.queue.append(item) - - # Get an item from the queue - def _get(self): - return self.queue.popleft() diff --git a/venv/Lib/site-packages/pygame/threads/__init__.py b/venv/Lib/site-packages/pygame/threads/__init__.py index cc4f9cfd189bc5fb332a4f242c0c4e3563e66c52..6337349ba5ec95ce8de8be41ba260cabb4c38ecc 100644 --- a/venv/Lib/site-packages/pygame/threads/__init__.py +++ b/venv/Lib/site-packages/pygame/threads/__init__.py @@ -10,24 +10,12 @@ If you know how to use the map function, you can use threads. __author__ = "Rene Dudfield" __version__ = "0.3.0" -__license__ = 'Python license' - -import traceback, sys - -from pygame.compat import geterror - -if sys.version_info[0] == 3: - from queue import Queue - from queue import Empty -elif (sys.version_info[0] == 2 and sys.version_info[1] < 5): - from Py25Queue import Queue - from Py25Queue import Empty -else: - # use up to date version - from Queue import Queue - from Queue import Empty - +__license__ = "Python license" + +from queue import Queue, Empty import threading + + Thread = threading.Thread STOP = object() @@ -43,17 +31,16 @@ _wq = None _use_workers = 0 # Set this to the maximum for the amount of Cores/CPUs -# Note, that the tests early out. +# Note, that the tests early out. # So it should only test the best number of workers +2 MAX_WORKERS_TO_TEST = 64 +def init(number_of_workers=0): + """Does a little test to see if threading is worth it. + Sets up a global worker queue if it's worth it. -def init(number_of_workers = 0): - """ Does a little test to see if threading is worth it. - Sets up a global worker queue if it's worth it. - - Calling init() is not required, but is generally better to do. + Calling init() is not required, but is generally better to do. """ global _wq, _use_workers @@ -66,76 +53,67 @@ def init(number_of_workers = 0): _wq = WorkerQueue(_use_workers) - - def quit(): - """ cleans up everything. - """ + """cleans up everything.""" global _wq, _use_workers _wq.stop() _wq = None _use_workers = False -def benchmark_workers(a_bench_func = None, the_data = None): - """ does a little test to see if workers are at all faster. - Returns the number of workers which works best. - Takes a little bit of time to run, so you should only really call - it once. - You can pass in benchmark data, and functions if you want. - a_bench_func - f(data) - the_data - data to work on. +def benchmark_workers(a_bench_func=None, the_data=None): + """does a little test to see if workers are at all faster. + Returns the number of workers which works best. + Takes a little bit of time to run, so you should only really call + it once. + You can pass in benchmark data, and functions if you want. + a_bench_func - f(data) + the_data - data to work on. """ - global _use_workers - - #TODO: try and make this scale better with slower/faster cpus. + # TODO: try and make this scale better with slower/faster cpus. # first find some variables so that using 0 workers takes about 1.0 seconds. # then go from there. - # note, this will only work with pygame 1.8rc3+ # replace the doit() and the_data with something that releases the GIL - import pygame import pygame.transform import time if not a_bench_func: + def doit(x): return pygame.transform.scale(x, (544, 576)) + else: doit = a_bench_func if not the_data: - thedata = [] - for x in range(10): - thedata.append(pygame.Surface((155,155), 0, 32)) + thedata = [pygame.Surface((155, 155), 0, 32) for x in range(10)] else: thedata = the_data best = time.time() + 100000000 best_number = 0 - last_best = -1 + # last_best = -1 for num_workers in range(0, MAX_WORKERS_TO_TEST): - wq = WorkerQueue(num_workers) t1 = time.time() - for xx in range(20): - print ("active count:%s" % threading.activeCount()) - results = tmap(doit, thedata, worker_queue = wq) + for _ in range(20): + print(f"active count:{threading.active_count()}") + tmap(doit, thedata, worker_queue=wq) t2 = time.time() wq.stop() - total_time = t2 - t1 - print ("total time num_workers:%s: time:%s:" % (num_workers, total_time)) + print(f"total time num_workers:{num_workers}: time:{total_time}:") if total_time < best: - last_best = best_number - best_number =num_workers + # last_best = best_number + best_number = num_workers best = total_time if num_workers - best_number > 1: @@ -143,22 +121,18 @@ def benchmark_workers(a_bench_func = None, the_data = None): # so we stop with testing at this number. break - return best_number - - -class WorkerQueue(object): - - def __init__(self, num_workers = 20): +class WorkerQueue: + def __init__(self, num_workers=20): self.queue = Queue() self.pool = [] self._setup_workers(num_workers) def _setup_workers(self, num_workers): - """ Sets up the worker threads - NOTE: undefined behaviour if you call this again. + """Sets up the worker threads + NOTE: undefined behaviour if you call this again. """ self.pool = [] @@ -169,81 +143,74 @@ class WorkerQueue(object): a_thread.setDaemon(True) a_thread.start() - def do(self, f, *args, **kwArgs): - """ puts a function on a queue for running later. - """ + """puts a function on a queue for running later.""" self.queue.put((f, args, kwArgs)) - def stop(self): - """ Stops the WorkerQueue, waits for all of the threads to finish up. - """ + """Stops the WorkerQueue, waits for all of the threads to finish up.""" self.queue.put(STOP) for thread in self.pool: thread.join() - - def threadloop(self): #, finish = False): - """ Loops until all of the tasks are finished. - """ + def threadloop(self): # , finish=False): + """Loops until all of the tasks are finished.""" while True: args = self.queue.get() if args is STOP: self.queue.put(STOP) self.queue.task_done() break - else: - try: - args[0](*args[1], **args[2]) - finally: - # clean up the queue, raise the exception. - self.queue.task_done() - #raise - + try: + args[0](*args[1], **args[2]) + finally: + # clean up the queue, raise the exception. + self.queue.task_done() + # raise def wait(self): - """ waits until all tasks are complete. - """ + """waits until all tasks are complete.""" self.queue.join() + class FuncResult: - """ Used for wrapping up a function call so that the results are stored - inside the instances result attribute. + """Used for wrapping up a function call so that the results are stored + inside the instances result attribute. """ - def __init__(self, f, callback = None, errback = None): - """ f - is the function we that we call - callback(result) - this is called when the function(f) returns - errback(exception) - this is called when the function(f) raises - an exception. + + def __init__(self, f, callback=None, errback=None): + """f - is the function we that we call + callback(result) - this is called when the function(f) returns + errback(exception) - this is called when the function(f) raises + an exception. """ self.f = f self.exception = None + self.result = None self.callback = callback self.errback = errback def __call__(self, *args, **kwargs): - - #we try to call the function here. If it fails we store the exception. + # we try to call the function here. If it fails we store the exception. try: self.result = self.f(*args, **kwargs) if self.callback: self.callback(self.result) - except Exception: - self.exception = geterror() + except Exception as e: + self.exception = e if self.errback: self.errback(self.exception) -def tmap(f, seq_args, num_workers = 20, worker_queue = None, wait = True, stop_on_error = True): - """ like map, but uses a thread pool to execute. - num_workers - the number of worker threads that will be used. If pool - is passed in, then the num_workers arg is ignored. - worker_queue - you can optionally pass in an existing WorkerQueue. - wait - True means that the results are returned when everything is finished. - False means that we return the [worker_queue, results] right away instead. - results, is returned as a list of FuncResult instances. - stop_on_error - +def tmap(f, seq_args, num_workers=20, worker_queue=None, wait=True, stop_on_error=True): + """like map, but uses a thread pool to execute. + num_workers - the number of worker threads that will be used. If pool + is passed in, then the num_workers arg is ignored. + worker_queue - you can optionally pass in an existing WorkerQueue. + wait - True means that the results are returned when everything is finished. + False means that we return the [worker_queue, results] right away instead. + results, is returned as a list of FuncResult instances. + stop_on_error - """ if worker_queue: @@ -263,48 +230,42 @@ def tmap(f, seq_args, num_workers = 20, worker_queue = None, wait = True, stop_o if len(wq.pool) == 0: return map(f, seq_args) - #print ("queue size:%s" % wq.queue.qsize()) - + # print("queue size:%s" % wq.queue.qsize()) - #TODO: divide the data (seq_args) into even chunks and + # TODO: divide the data (seq_args) into even chunks and # then pass each thread a map(f, equal_part(seq_args)) # That way there should be less locking, and overhead. - - results = [] for sa in seq_args: results.append(FuncResult(f)) wq.do(results[-1], sa) - - #wq.stop() + # wq.stop() if wait: - #print ("wait") + # print("wait") wq.wait() - #print ("after wait") - #print ("queue size:%s" % wq.queue.qsize()) + # print("after wait") + # print("queue size:%s" % wq.queue.qsize()) if wq.queue.qsize(): - raise Exception("buggy threadmap") + raise RuntimeError("buggy threadmap") # if we created a worker queue, we need to stop it. if not worker_queue and not _wq: - #print ("stoping") + # print("stopping") wq.stop() if wq.queue.qsize(): um = wq.queue.get() if not um is STOP: - raise Exception("buggy threadmap") - - + raise RuntimeError("buggy threadmap") + # see if there were any errors. If so raise the first one. This matches map behaviour. # TODO: the traceback doesn't show up nicely. # NOTE: TODO: we might want to return the results anyway? This should be an option. if stop_on_error: - error_ones = list(filter(lambda x:x.exception, results)) + error_ones = list(filter(lambda x: x.exception, results)) if error_ones: raise error_ones[0].exception - - return map(lambda x:x.result, results) - else: - return [wq, results] + + return map(lambda x: x.result, results) + return [wq, results] diff --git a/venv/Lib/site-packages/pygame/time.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/time.cp37-win_amd64.pyd deleted file mode 100644 index e502a5fb242c82ba30a46ae27176e2517bc1d157..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/time.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/transform.cp37-win_amd64.pyd b/venv/Lib/site-packages/pygame/transform.cp37-win_amd64.pyd deleted file mode 100644 index d52a213e229ad92ef7c4aba28303cc3a3c5d557e..0000000000000000000000000000000000000000 Binary files a/venv/Lib/site-packages/pygame/transform.cp37-win_amd64.pyd and /dev/null differ diff --git a/venv/Lib/site-packages/pygame/version.py b/venv/Lib/site-packages/pygame/version.py index 7b1862daefc63b7e11d34b76ad261b6a958947df..8233ad42eb1584dcce1a9a5b8216c3f218c6c3ee 100644 --- a/venv/Lib/site-packages/pygame/version.py +++ b/venv/Lib/site-packages/pygame/version.py @@ -26,20 +26,47 @@ pygame version without importing the main pygame module. The python version information should always compare greater than any previous releases. (hmm, until we get to versions > 10) """ +from pygame.base import get_sdl_version -class PygameVersion(tuple): +############### +# This file is generated with version.py.in +## + +class SoftwareVersion(tuple): + """ + A class for storing data about software versions. + """ __slots__ = () - fields = 'major', 'minor', 'patch' + fields = "major", "minor", "patch" + def __new__(cls, major, minor, patch): return tuple.__new__(cls, (major, minor, patch)) + def __repr__(self): - fields = ('{}={}'.format(fld, val) for fld, val in zip(self.fields, self)) - return '{}({})'.format(str(self.__class__.__name__), ', '.join(fields)) + fields = (f"{fld}={val}" for fld, val in zip(self.fields, self)) + return f"{str(self.__class__.__name__)}({', '.join(fields)})" + def __str__(self): - return '{}.{}.{}'.format(*self) + return f"{self.major}.{self.minor}.{self.patch}" + major = property(lambda self: self[0]) minor = property(lambda self: self[1]) patch = property(lambda self: self[2]) -ver = "1.9.6" -vernum = PygameVersion(1, 9, 6) -rev = "" + +class PygameVersion(SoftwareVersion): + """ + Pygame Version class. + """ + +class SDLVersion(SoftwareVersion): + """ + SDL Version class. + """ + +_sdl_tuple = get_sdl_version() +SDL = SDLVersion(_sdl_tuple[0], _sdl_tuple[1], _sdl_tuple[2]) +ver = "2.5.2" # pylint: disable=invalid-name +vernum = PygameVersion(2, 5, 2) +rev = "" # pylint: disable=invalid-name + +__all__ = ["SDL", "ver", "vernum", "rev"] diff --git a/venv/Lib/site-packages/pygame/zlib1.dll b/venv/Lib/site-packages/pygame/zlib1.dll index 2d3b770495f205757cc2ad299d34d0119979e20a..e7493de315264b254bdf914737274034e9017598 100644 Binary files a/venv/Lib/site-packages/pygame/zlib1.dll and b/venv/Lib/site-packages/pygame/zlib1.dll differ diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/INSTALLER b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e38a32041e49332e5e81c2d363dc418d68..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/LICENSE b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/LICENSE deleted file mode 100644 index 6e0693b4b010a8c42e34f89ab8fa1e18b5bae1cc..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2016 Jason R Coombs - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/METADATA b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/METADATA deleted file mode 100644 index 7927f6f51265f2562d8652474f76e5193c0d7bc1..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/METADATA +++ /dev/null @@ -1,113 +0,0 @@ -Metadata-Version: 2.1 -Name: setuptools -Version: 49.2.0 -Summary: Easily download, build, install, upgrade, and uninstall Python packages -Home-page: https://github.com/pypa/setuptools -Author: Python Packaging Authority -Author-email: distutils-sig@python.org -License: UNKNOWN -Project-URL: Documentation, https://setuptools.readthedocs.io/ -Keywords: CPAN PyPI distutils eggs package management -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -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 :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Archiving :: Packaging -Classifier: Topic :: System :: Systems Administration -Classifier: Topic :: Utilities -Requires-Python: >=3.5 -Description-Content-Type: text/x-rst; charset=UTF-8 -Provides-Extra: certs -Requires-Dist: certifi (==2016.9.26) ; extra == 'certs' -Provides-Extra: docs -Requires-Dist: sphinx ; extra == 'docs' -Requires-Dist: jaraco.packaging (>=6.1) ; extra == 'docs' -Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' -Requires-Dist: pygments-github-lexers (==0.0.5) ; extra == 'docs' -Provides-Extra: ssl -Requires-Dist: wincertstore (==0.2) ; (sys_platform == "win32") and extra == 'ssl' -Provides-Extra: tests -Requires-Dist: mock ; extra == 'tests' -Requires-Dist: pytest-flake8 ; extra == 'tests' -Requires-Dist: virtualenv (>=13.0.0) ; extra == 'tests' -Requires-Dist: pytest-virtualenv (>=1.2.7) ; extra == 'tests' -Requires-Dist: pytest (>=3.7) ; extra == 'tests' -Requires-Dist: wheel ; extra == 'tests' -Requires-Dist: coverage (>=4.5.1) ; extra == 'tests' -Requires-Dist: pytest-cov (>=2.5.1) ; extra == 'tests' -Requires-Dist: pip (>=19.1) ; extra == 'tests' -Requires-Dist: futures ; (python_version == "2.7") and extra == 'tests' -Requires-Dist: flake8-2020 ; (python_version >= "3.6") and extra == 'tests' -Requires-Dist: paver ; (python_version >= "3.6") and extra == 'tests' - -.. image:: https://raw.githubusercontent.com/pypa/setuptools/master/docs/logo/banner%201%20line%20color.svg - - -.. image:: https://img.shields.io/pypi/v/setuptools.svg - :target: `PyPI link`_ - -.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg - :target: `PyPI link`_ - -.. _PyPI link: https://pypi.org/project/setuptools - -.. image:: https://dev.azure.com/jaraco/setuptools/_apis/build/status/pypa.setuptools?branchName=master - :target: https://dev.azure.com/jaraco/setuptools/_build/latest?definitionId=1&branchName=master - -.. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white - :target: https://travis-ci.org/pypa/setuptools - -.. image:: https://img.shields.io/appveyor/ci/pypa/setuptools/master.svg?label=Windows%20CI&logo=appveyor&logoColor=white - :target: https://ci.appveyor.com/project/pypa/setuptools/branch/master - -.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg - :target: https://setuptools.readthedocs.io - -.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white - :target: https://codecov.io/gh/pypa/setuptools - -.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat - :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme - - -See the `Installation Instructions -`_ in the Python Packaging -User's Guide for instructions on installing, upgrading, and uninstalling -Setuptools. - -Questions and comments should be directed to the `distutils-sig -mailing list `_. -Bug reports and especially tested patches may be -submitted directly to the `bug tracker -`_. - -To report a security vulnerability, please use the -`Tidelift security contact `_. -Tidelift will coordinate the fix and disclosure. - - -For Enterprise -============== - -Available as part of the Tidelift Subscription. - -Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. - -`Learn more `_. - -Code of Conduct -=============== - -Everyone interacting in the setuptools project's codebases, issue trackers, -chat rooms, and mailing lists is expected to follow the -`PyPA Code of Conduct `_. - - diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/RECORD b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/RECORD deleted file mode 100644 index bd130196bdb4d2a1a31e0a3d0baf28b3faaedc17..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/RECORD +++ /dev/null @@ -1,296 +0,0 @@ -../../Scripts/easy_install-3.7.exe,sha256=JFbJ5uQQ7w9W4HP0DXlsTTlJJfFGgWXoOpm0aI53GHE,102775 -../../Scripts/easy_install.exe,sha256=JFbJ5uQQ7w9W4HP0DXlsTTlJJfFGgWXoOpm0aI53GHE,102775 -__pycache__/easy_install.cpython-37.pyc,, -easy_install.py,sha256=MDC9vt5AxDsXX5qcKlBz2TnW6Tpuv_AobnfhCJ9X3PM,126 -pkg_resources/__init__.py,sha256=44G2LkL_lXbDzjTukLmR5baLQtE3S4IaFciSZPDcOM8,108481 -pkg_resources/__pycache__/__init__.cpython-37.pyc,, -pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pkg_resources/_vendor/__pycache__/__init__.cpython-37.pyc,, -pkg_resources/_vendor/__pycache__/appdirs.cpython-37.pyc,, -pkg_resources/_vendor/__pycache__/pyparsing.cpython-37.pyc,, -pkg_resources/_vendor/__pycache__/six.cpython-37.pyc,, -pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 -pkg_resources/_vendor/packaging/__about__.py,sha256=CpuMSyh1V7adw8QMjWKkY3LtdqRUkRX4MgJ6nF4stM0,744 -pkg_resources/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 -pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/markers.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/tags.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/utils.cpython-37.pyc,, -pkg_resources/_vendor/packaging/__pycache__/version.cpython-37.pyc,, -pkg_resources/_vendor/packaging/_compat.py,sha256=Ugdm-qcneSchW25JrtMIKgUxfEEBcCAz6WrEeXeqz9o,865 -pkg_resources/_vendor/packaging/_structures.py,sha256=pVd90XcXRGwpZRB_qdFuVEibhCHpX_bL5zYr9-N0mc8,1416 -pkg_resources/_vendor/packaging/markers.py,sha256=-3GbxB_JjpWPBlTjvo_rCMJZ17i96VvHjtZ3URklwhg,8277 -pkg_resources/_vendor/packaging/requirements.py,sha256=syt3EodrY6_UtlfeJDuhVYXcEDEweTSt2pyslLYlX3I,4757 -pkg_resources/_vendor/packaging/specifiers.py,sha256=0ZzQpcUnvrQ6LjR-mQRLzMr8G6hdRv-mY0VSf_amFtI,27778 -pkg_resources/_vendor/packaging/tags.py,sha256=EPLXhO6GTD7_oiWEO1U0l0PkfR8R_xivpMDHXnsTlts,12933 -pkg_resources/_vendor/packaging/utils.py,sha256=VaTC0Ei7zO2xl9ARiWmz2YFLFt89PuuhLbAlXMyAGms,1520 -pkg_resources/_vendor/packaging/version.py,sha256=Npdwnb8OHedj_2L86yiUqscujb7w_i5gmSK1PhOAFzg,11978 -pkg_resources/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 -pkg_resources/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 -pkg_resources/extern/__init__.py,sha256=w_3T8ntsvFFioQYOgYoGGqafDiv4sLzecQRDjsB5yeE,2101 -pkg_resources/extern/__pycache__/__init__.cpython-37.pyc,, -setuptools-49.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -setuptools-49.2.0.dist-info/LICENSE,sha256=wyo6w5WvYyHv0ovnPQagDw22q4h9HCHU_sRhKNIFbVo,1078 -setuptools-49.2.0.dist-info/METADATA,sha256=FbKZwSNlCe_hiX-UOm_7cl2AfrM_8Wjd-z9f4eenctY,4917 -setuptools-49.2.0.dist-info/RECORD,, -setuptools-49.2.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92 -setuptools-49.2.0.dist-info/dependency_links.txt,sha256=HlkCFkoK5TbZ5EMLbLKYhLcY_E31kBWD8TqW2EgmatQ,239 -setuptools-49.2.0.dist-info/entry_points.txt,sha256=1K5Fr0-5Ph3ZRZFuwNaw8ERGiNLVqHvdKDNt3oXGS6w,3143 -setuptools-49.2.0.dist-info/top_level.txt,sha256=2HUXVVwA4Pff1xgTFr3GsTXXKaPaO6vlG6oNJ_4u4Tg,38 -setuptools-49.2.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 -setuptools/__init__.py,sha256=MeXBA4OH_MiIlHhecZLuoNjYbQP2CrMof2wS5qfKDNg,7943 -setuptools/__pycache__/__init__.cpython-37.pyc,, -setuptools/__pycache__/_deprecation_warning.cpython-37.pyc,, -setuptools/__pycache__/_imp.cpython-37.pyc,, -setuptools/__pycache__/archive_util.cpython-37.pyc,, -setuptools/__pycache__/build_meta.cpython-37.pyc,, -setuptools/__pycache__/config.cpython-37.pyc,, -setuptools/__pycache__/dep_util.cpython-37.pyc,, -setuptools/__pycache__/depends.cpython-37.pyc,, -setuptools/__pycache__/dist.cpython-37.pyc,, -setuptools/__pycache__/distutils_patch.cpython-37.pyc,, -setuptools/__pycache__/errors.cpython-37.pyc,, -setuptools/__pycache__/extension.cpython-37.pyc,, -setuptools/__pycache__/glob.cpython-37.pyc,, -setuptools/__pycache__/installer.cpython-37.pyc,, -setuptools/__pycache__/launch.cpython-37.pyc,, -setuptools/__pycache__/lib2to3_ex.cpython-37.pyc,, -setuptools/__pycache__/monkey.cpython-37.pyc,, -setuptools/__pycache__/msvc.cpython-37.pyc,, -setuptools/__pycache__/namespaces.cpython-37.pyc,, -setuptools/__pycache__/package_index.cpython-37.pyc,, -setuptools/__pycache__/py27compat.cpython-37.pyc,, -setuptools/__pycache__/py31compat.cpython-37.pyc,, -setuptools/__pycache__/py33compat.cpython-37.pyc,, -setuptools/__pycache__/py34compat.cpython-37.pyc,, -setuptools/__pycache__/sandbox.cpython-37.pyc,, -setuptools/__pycache__/ssl_support.cpython-37.pyc,, -setuptools/__pycache__/unicode_utils.cpython-37.pyc,, -setuptools/__pycache__/version.cpython-37.pyc,, -setuptools/__pycache__/wheel.cpython-37.pyc,, -setuptools/__pycache__/windows_support.cpython-37.pyc,, -setuptools/_deprecation_warning.py,sha256=jU9-dtfv6cKmtQJOXN8nP1mm7gONw5kKEtiPtbwnZyI,218 -setuptools/_distutils/__init__.py,sha256=lpQAphR_7uhWC2fbSEps4Ja9W4YwezN_IX_LJEt3khU,250 -setuptools/_distutils/__pycache__/__init__.cpython-37.pyc,, -setuptools/_distutils/__pycache__/_msvccompiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/archive_util.cpython-37.pyc,, -setuptools/_distutils/__pycache__/bcppcompiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/ccompiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/cmd.cpython-37.pyc,, -setuptools/_distutils/__pycache__/config.cpython-37.pyc,, -setuptools/_distutils/__pycache__/core.cpython-37.pyc,, -setuptools/_distutils/__pycache__/cygwinccompiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/debug.cpython-37.pyc,, -setuptools/_distutils/__pycache__/dep_util.cpython-37.pyc,, -setuptools/_distutils/__pycache__/dir_util.cpython-37.pyc,, -setuptools/_distutils/__pycache__/dist.cpython-37.pyc,, -setuptools/_distutils/__pycache__/errors.cpython-37.pyc,, -setuptools/_distutils/__pycache__/extension.cpython-37.pyc,, -setuptools/_distutils/__pycache__/fancy_getopt.cpython-37.pyc,, -setuptools/_distutils/__pycache__/file_util.cpython-37.pyc,, -setuptools/_distutils/__pycache__/filelist.cpython-37.pyc,, -setuptools/_distutils/__pycache__/log.cpython-37.pyc,, -setuptools/_distutils/__pycache__/msvc9compiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/msvccompiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/spawn.cpython-37.pyc,, -setuptools/_distutils/__pycache__/sysconfig.cpython-37.pyc,, -setuptools/_distutils/__pycache__/text_file.cpython-37.pyc,, -setuptools/_distutils/__pycache__/unixccompiler.cpython-37.pyc,, -setuptools/_distutils/__pycache__/util.cpython-37.pyc,, -setuptools/_distutils/__pycache__/version.cpython-37.pyc,, -setuptools/_distutils/__pycache__/versionpredicate.cpython-37.pyc,, -setuptools/_distutils/_msvccompiler.py,sha256=MM6HcKUbe6hIFEuFWnrV-AO6_kjwZ3LG791IK3L0i-w,19907 -setuptools/_distutils/archive_util.py,sha256=qW-uiGwYexTvK5e-iSel_31Dshx-CqTanNPK6snwf98,8572 -setuptools/_distutils/bcppcompiler.py,sha256=OJDVpCUmX6H8v_7lV1zifV1fcx92Cr2dhiUh6989UJI,14894 -setuptools/_distutils/ccompiler.py,sha256=cMd3GSDOP4Tm4nFwWb7UOrGS0_g03QhGH251jHnfdBI,47417 -setuptools/_distutils/cmd.py,sha256=eco6LAGUtobLuPafuhmgKgkwRRL_WY8KJ4YeDCHpcls,18079 -setuptools/_distutils/command/__init__.py,sha256=2TA-rlNDlzeI-csbWHXFjGD8uOYqALMfyWOhT49nC6g,799 -setuptools/_distutils/command/__pycache__/__init__.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/bdist.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/bdist_msi.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/bdist_wininst.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/build.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/build_clib.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/build_ext.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/build_py.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/build_scripts.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/check.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/clean.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/config.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/install.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/install_data.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/install_egg_info.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/install_headers.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/install_lib.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/install_scripts.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/register.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/sdist.cpython-37.pyc,, -setuptools/_distutils/command/__pycache__/upload.cpython-37.pyc,, -setuptools/_distutils/command/bdist.py,sha256=2z4eudRl_n7m3lG9leL0IYqes4bsm8c0fxfZuiafjMg,5562 -setuptools/_distutils/command/bdist_dumb.py,sha256=BTur9jcIppyP7Piavjfsk7YjElqvxeYO2npUyPPOekc,4913 -setuptools/_distutils/command/bdist_msi.py,sha256=EVFQYN_X-ExeeP8gmdV9JcINsuUGsLJUz9afMU0Rt8c,35579 -setuptools/_distutils/command/bdist_rpm.py,sha256=gjOw22GhDSbcq0bdq25cTb-n6HWWm0bShLQad_mkJ4k,21537 -setuptools/_distutils/command/bdist_wininst.py,sha256=iGlaI-VfElHOneeczKHWnSN5a10-7IMcJaXuR1mdS3c,16030 -setuptools/_distutils/command/build.py,sha256=11NyR2UAUzalrkTZ2ph0BAHFWFC2jtSsN7gIaF-NC08,5767 -setuptools/_distutils/command/build_clib.py,sha256=bgVTHh28eLQA2Gkw68amApd_j7qQBX4MTI-zTvAK_J4,8022 -setuptools/_distutils/command/build_ext.py,sha256=MMJPCxHlf9rgUkizn4Kjq9vYeAEfxyqfq8XsTE-EpWM,31635 -setuptools/_distutils/command/build_py.py,sha256=S_Nlw4hZE8PnIgqX5OFMdmt-GSmOhPQQ4f2jr1uBnoU,17190 -setuptools/_distutils/command/build_scripts.py,sha256=aKycJJPx3LfZ1cvZgSJaxnD2LnvRM5WJ-8xkpdgcLsI,6232 -setuptools/_distutils/command/check.py,sha256=5qDtI75ccZg3sAItQWeaIu8y3FR314O4rr9Smz4HsEo,5637 -setuptools/_distutils/command/clean.py,sha256=2TCt47ru4hZZM0RfVfUYj5bbpicpGLP4Qhw5jBtvp9k,2776 -setuptools/_distutils/command/config.py,sha256=2aTjww3PwjMB8-ZibCe4P7B-qG1hM1gn_rJXYyxRz6c,13117 -setuptools/_distutils/command/install.py,sha256=oOM2rD7l_SglARNVDmiZn8u6DAfidXRF_yE5QS328B4,27482 -setuptools/_distutils/command/install_data.py,sha256=YhGOAwh3gJPqF7em5XA0rmpR42z1bLh80ooElzDyUvk,2822 -setuptools/_distutils/command/install_egg_info.py,sha256=0kW0liVMeadkjX0ZcRfMptKFen07Gw6gyw1VHT5KIwc,2603 -setuptools/_distutils/command/install_headers.py,sha256=XQ6idkbIDfr1ljXCOznuVUMvOFpHBn6cK0Wz9gIM2b4,1298 -setuptools/_distutils/command/install_lib.py,sha256=9AofR-MO9lAtjwwuukCptepOaJEKMZW2VHiyR5hU7HA,8397 -setuptools/_distutils/command/install_scripts.py,sha256=_CLUeQwGJRcY2kik7azPMn5IdtDCrjWdUvZ1khlG6ck,2017 -setuptools/_distutils/command/register.py,sha256=2jaq9968rt2puRVDBx1HbNiXv27uOk8idE_4lPf_3VM,11712 -setuptools/_distutils/command/sdist.py,sha256=qotJjAOzyhJjq2-oDImjNFrOtaSneEFDJTB-sEk1wnU,19005 -setuptools/_distutils/command/upload.py,sha256=BLO1w7eSAqsCjCLXtf_CRVSjwF1WmyOByGVGNdcQ8oY,7597 -setuptools/_distutils/config.py,sha256=dtHgblx9JhfyrKx1-J7Jlxw_f7s8ZbPFQii2UWMTZpY,4827 -setuptools/_distutils/core.py,sha256=jbdOkpOK09xi-56vhhwvn3fYdhLb5DJO8q3K1fnQz0Q,8876 -setuptools/_distutils/cygwinccompiler.py,sha256=9U4JAusUzlAGJl0Y5nToPkQ3ldzseAtiye434mwJ0ow,16380 -setuptools/_distutils/debug.py,sha256=N6MrTAqK6l9SVk6tWweR108PM8Ol7qNlfyV-nHcLhsY,139 -setuptools/_distutils/dep_util.py,sha256=GuR9Iw_jzZRkyemJ5HX8rB_wRGxkIBcBm1qh54r7zhk,3491 -setuptools/_distutils/dir_util.py,sha256=UwhBOUTcV65GTwce4SPuTXR8Z8q3LYEcmttqcGb0bYo,7778 -setuptools/_distutils/dist.py,sha256=Biuf6ca8uiFfMScRFsYUKtb5neMPtxKxRtXn50_1f3U,50421 -setuptools/_distutils/errors.py,sha256=Yr6tKZGdzBoNi53vBtiq0UJ__X05CmxSdQJqOWaw6SY,3577 -setuptools/_distutils/extension.py,sha256=bTb3Q0CoevGKYv5dX1ls--Ln8tlB0-UEOsi9BwzlZ-s,10515 -setuptools/_distutils/fancy_getopt.py,sha256=OPxp2CxHi1Yp_d1D8JxW4Ueq9fC71tegQFaafh58GGU,17784 -setuptools/_distutils/file_util.py,sha256=0hUqfItN_x2DVihR0MHdA4KCMVCOO8VoByaFp_a6MDg,8148 -setuptools/_distutils/filelist.py,sha256=8bRxhzp2FsaoHT7TuKD4Qjcuh_B9Ow_xTt_htZJvN2Q,12832 -setuptools/_distutils/log.py,sha256=hWBmdUC2K927QcVv3REMW3HMPclxccPQngxLSuUXQl0,1969 -setuptools/_distutils/msvc9compiler.py,sha256=uv0TAfoWrxEBOQL-Z2uws5g4AXoTPahUEMuq6FLkCYY,30453 -setuptools/_distutils/msvccompiler.py,sha256=ZYsnUgIC4tZT2WkJbTkTUyVSCAc2nFM9DVKIuIfPBU0,23540 -setuptools/_distutils/spawn.py,sha256=XBmUqzhxXfay_JE18RkaalHf9kgi7NvXeBPW9BfTqmw,4408 -setuptools/_distutils/sysconfig.py,sha256=5z55MU7gXeceL_G9FK6ex-2OvdeIXJRZJafrtthJcfU,21349 -setuptools/_distutils/text_file.py,sha256=PsuAJeWdKJoLSV_6N6IpB5-0Pa84KzLUucJMFRazw3I,12483 -setuptools/_distutils/unixccompiler.py,sha256=E65edChYLoHY8wi4OxFu_wKt3hJe3GySF6v51G_ZzL0,14696 -setuptools/_distutils/util.py,sha256=Z-FtpvCo1szNkssI-it-uWhA35996XHcttLZiUzc1_Y,20913 -setuptools/_distutils/version.py,sha256=8NogP6NPPQpp3EUMZcT9czEHia-ehqPo8spo_e7AgUU,12514 -setuptools/_distutils/versionpredicate.py,sha256=ZxpEA-TQv88mUWc6hetUO4qSqA2sa7ipjZ3QEK5evDk,5133 -setuptools/_imp.py,sha256=Qx0LJzEBaWk_6PfICamJtfBN2rh5K9sJq1wXvtZW-mc,2388 -setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -setuptools/_vendor/__pycache__/__init__.cpython-37.pyc,, -setuptools/_vendor/__pycache__/ordered_set.cpython-37.pyc,, -setuptools/_vendor/__pycache__/pyparsing.cpython-37.pyc,, -setuptools/_vendor/__pycache__/six.cpython-37.pyc,, -setuptools/_vendor/ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 -setuptools/_vendor/packaging/__about__.py,sha256=CpuMSyh1V7adw8QMjWKkY3LtdqRUkRX4MgJ6nF4stM0,744 -setuptools/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 -setuptools/_vendor/packaging/__pycache__/__about__.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/__init__.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/_compat.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/_structures.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/markers.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/requirements.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/tags.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/utils.cpython-37.pyc,, -setuptools/_vendor/packaging/__pycache__/version.cpython-37.pyc,, -setuptools/_vendor/packaging/_compat.py,sha256=Ugdm-qcneSchW25JrtMIKgUxfEEBcCAz6WrEeXeqz9o,865 -setuptools/_vendor/packaging/_structures.py,sha256=pVd90XcXRGwpZRB_qdFuVEibhCHpX_bL5zYr9-N0mc8,1416 -setuptools/_vendor/packaging/markers.py,sha256=-meFl9Fr9V8rF5Rduzgett5EHK9wBYRUqssAV2pj0lw,8268 -setuptools/_vendor/packaging/requirements.py,sha256=3dwIJekt8RRGCUbgxX8reeAbgmZYjb0wcCRtmH63kxI,4742 -setuptools/_vendor/packaging/specifiers.py,sha256=0ZzQpcUnvrQ6LjR-mQRLzMr8G6hdRv-mY0VSf_amFtI,27778 -setuptools/_vendor/packaging/tags.py,sha256=EPLXhO6GTD7_oiWEO1U0l0PkfR8R_xivpMDHXnsTlts,12933 -setuptools/_vendor/packaging/utils.py,sha256=VaTC0Ei7zO2xl9ARiWmz2YFLFt89PuuhLbAlXMyAGms,1520 -setuptools/_vendor/packaging/version.py,sha256=Npdwnb8OHedj_2L86yiUqscujb7w_i5gmSK1PhOAFzg,11978 -setuptools/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 -setuptools/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 -setuptools/archive_util.py,sha256=F1-XrQJTdXHRPRA09kxPWwm9Z2Ms1lE_IQZKG_JZ7rM,6638 -setuptools/build_meta.py,sha256=qFxrLAwgKPS3TxEi8NNbFxfEvb192pzSgARS8nZZ_Ek,9917 -setuptools/cli-32.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 -setuptools/cli-64.exe,sha256=KLABu5pyrnokJCv6skjXZ6GsXeyYHGcqOUT3oHI3Xpo,74752 -setuptools/cli.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 -setuptools/command/__init__.py,sha256=QCAuA9whnq8Bnoc0bBaS6Lw_KAUO0DiHYZQXEMNn5hg,568 -setuptools/command/__pycache__/__init__.cpython-37.pyc,, -setuptools/command/__pycache__/alias.cpython-37.pyc,, -setuptools/command/__pycache__/bdist_egg.cpython-37.pyc,, -setuptools/command/__pycache__/bdist_rpm.cpython-37.pyc,, -setuptools/command/__pycache__/bdist_wininst.cpython-37.pyc,, -setuptools/command/__pycache__/build_clib.cpython-37.pyc,, -setuptools/command/__pycache__/build_ext.cpython-37.pyc,, -setuptools/command/__pycache__/build_py.cpython-37.pyc,, -setuptools/command/__pycache__/develop.cpython-37.pyc,, -setuptools/command/__pycache__/dist_info.cpython-37.pyc,, -setuptools/command/__pycache__/easy_install.cpython-37.pyc,, -setuptools/command/__pycache__/egg_info.cpython-37.pyc,, -setuptools/command/__pycache__/install.cpython-37.pyc,, -setuptools/command/__pycache__/install_egg_info.cpython-37.pyc,, -setuptools/command/__pycache__/install_lib.cpython-37.pyc,, -setuptools/command/__pycache__/install_scripts.cpython-37.pyc,, -setuptools/command/__pycache__/py36compat.cpython-37.pyc,, -setuptools/command/__pycache__/register.cpython-37.pyc,, -setuptools/command/__pycache__/rotate.cpython-37.pyc,, -setuptools/command/__pycache__/saveopts.cpython-37.pyc,, -setuptools/command/__pycache__/sdist.cpython-37.pyc,, -setuptools/command/__pycache__/setopt.cpython-37.pyc,, -setuptools/command/__pycache__/test.cpython-37.pyc,, -setuptools/command/__pycache__/upload.cpython-37.pyc,, -setuptools/command/__pycache__/upload_docs.cpython-37.pyc,, -setuptools/command/alias.py,sha256=KjpE0sz_SDIHv3fpZcIQK-sCkJz-SrC6Gmug6b9Nkc8,2426 -setuptools/command/bdist_egg.py,sha256=pVY95-nsM0U1_QmK01eLRedjWDw9ruEwrZxBae8FyZA,18482 -setuptools/command/bdist_rpm.py,sha256=B7l0TnzCGb-0nLlm6rS00jWLkojASwVmdhW2w5Qz_Ak,1508 -setuptools/command/bdist_wininst.py,sha256=Tmqa9wW0F8i_72KHWpu9pDdnCN6Er_8uJUs2UmCAwTA,922 -setuptools/command/build_clib.py,sha256=fWHSFGkk10VCddBWCszvNhowbG9Z9CZXVjQ2uSInoOs,4415 -setuptools/command/build_ext.py,sha256=RYS8cJvCwvusFnbKllvLtd6-HcR0dVIzX6zVrtw1Vc8,13187 -setuptools/command/build_py.py,sha256=fho10QRGOaJcc3vttQ5vk5KYMV6HdZwj9HUIob6NHDM,9737 -setuptools/command/develop.py,sha256=wF2CiU9wjCF8ZcfFzn02j2ylez8r13z_fEco6vWx3DM,8118 -setuptools/command/dist_info.py,sha256=5t6kOfrdgALT-P3ogss6PF9k-Leyesueycuk3dUyZnI,960 -setuptools/command/easy_install.py,sha256=T1d_3uQFLur6qNrNtEiiRVzleECvHBe9etr7o3Imquw,86924 -setuptools/command/egg_info.py,sha256=LKrhZuy-IoRJZ59orIB2-_Gj7NBj9MHm5uu16zZdE7U,25560 -setuptools/command/install.py,sha256=8doMxeQEDoK4Eco0mO2WlXXzzp9QnsGJQ7Z7yWkZPG8,4705 -setuptools/command/install_egg_info.py,sha256=bMgeIeRiXzQ4DAGPV1328kcjwQjHjOWU4FngAWLV78Q,2203 -setuptools/command/install_lib.py,sha256=Uz42McsyHZAjrB6cw9E7Bz0xsaTbzxnM1PI9CBhiPtE,3875 -setuptools/command/install_scripts.py,sha256=x7sdEICuyFpaf5LuWXcTp49oYt8EeNbwKkW2Pv-TVXI,2519 -setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628 -setuptools/command/py36compat.py,sha256=TKqF6CPv-vsEFpOJUYmjBzmck-mCv_zHJMXO500PEAI,4994 -setuptools/command/register.py,sha256=kk3DxXCb5lXTvqnhfwx2g6q7iwbUmgTyXUCaBooBOUk,468 -setuptools/command/rotate.py,sha256=1KD9hHoDWpyvsbc2L7ULrQxUpJsG5zIMlPfx8yLowk4,2176 -setuptools/command/saveopts.py,sha256=za7QCBcQimKKriWcoCcbhxPjUz30gSB74zuTL47xpP4,658 -setuptools/command/sdist.py,sha256=14kBw_QOZ9L_RQDqgf9DAlEuoj0zC30X5mfDWeiyZwU,8092 -setuptools/command/setopt.py,sha256=NTWDyx-gjDF-txf4dO577s7LOzHVoKR0Mq33rFxaRr8,5085 -setuptools/command/test.py,sha256=okVw2id6qYh8hFAVGziX6dEYekAbaYfMtEx7XhgsSbg,9623 -setuptools/command/upload.py,sha256=XT3YFVfYPAmA5qhGg0euluU98ftxRUW-PzKcODMLxUs,462 -setuptools/command/upload_docs.py,sha256=G2gHjeNPcUGe_pr3EEk_6AoVD0E6nCp52mZgU2nkCpU,7314 -setuptools/config.py,sha256=Ncxt5IQTVyM9qvX3PxB-Eb67-zoZLq5WbDuyu3I2gd0,21782 -setuptools/dep_util.py,sha256=BDx1BkzNQntvAB4alypHbW5UVBzjqths000PrUL4Zqc,949 -setuptools/depends.py,sha256=qt2RWllArRvhnm8lxsyRpcthEZYp4GHQgREl1q0LkFw,5517 -setuptools/dist.py,sha256=Of69bBpUzFWt9o_RTptPt-3MWVc3k_LId3b7hh8rBQs,39350 -setuptools/distutils_patch.py,sha256=r8LauqtVguTUFxguvU7tDhF8HTgAkIBHg5-hgPeSJ5c,1754 -setuptools/errors.py,sha256=MVOcv381HNSajDgEUWzOQ4J6B5BHCBMSjHfaWcEwA1o,524 -setuptools/extension.py,sha256=uc6nHI-MxwmNCNPbUiBnybSyqhpJqjbhvOQ-emdvt_E,1729 -setuptools/extern/__init__.py,sha256=BilMS9Hq18nBaUOzcCrzoI9HnIhju45iVJBscqTqlDI,2128 -setuptools/extern/__pycache__/__init__.cpython-37.pyc,, -setuptools/glob.py,sha256=o75cHrOxYsvn854thSxE0x9k8JrKDuhP_rRXlVB00Q4,5084 -setuptools/gui-32.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 -setuptools/gui-64.exe,sha256=aYKMhX1IJLn4ULHgWX0sE0yREUt6B3TEHf_jOw6yNyE,75264 -setuptools/gui.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 -setuptools/installer.py,sha256=mJ6SdRmhWpZ1Cg3H_LWd1IoZoeC2t4BSkkXMuvhYeKw,5343 -setuptools/launch.py,sha256=TyPT-Ic1T2EnYvGO26gfNRP4ysBlrhpbRjQxWsiO414,812 -setuptools/lib2to3_ex.py,sha256=lrjhfs4QVtWp65PuATWjPBcXxwubg9d81e0qrv0qOpI,2384 -setuptools/monkey.py,sha256=FGc9fffh7gAxMLFmJs2DW_OYWpBjkdbNS2n14UAK4NA,5264 -setuptools/msvc.py,sha256=8xIqn20nZ_poynw6sDvZuUECN_KlOjdTNfossrlSMcY,51225 -setuptools/namespaces.py,sha256=QuvIR8S5-u_S8_fLjPpn_utruUIsu2twdRu_KJPrKU0,3223 -setuptools/package_index.py,sha256=oKRvghWBzlqlQV4iRUERwbpBs_rXL5mwlzNZdKI2yXs,40777 -setuptools/py27compat.py,sha256=CWHkWWAYodu3QgiIAr8-34T-G6fiSgiVF0y7h11Ld7U,1504 -setuptools/py31compat.py,sha256=h2rtZghOfwoGYd8sQ0-auaKiF3TcL3qX0bX3VessqcE,838 -setuptools/py33compat.py,sha256=SMF9Z8wnGicTOkU1uRNwZ_kz5Z_bj29PUBbqdqeeNsc,1330 -setuptools/py34compat.py,sha256=KYOd6ybRxjBW8NJmYD8t_UyyVmysppFXqHpFLdslGXU,245 -setuptools/sandbox.py,sha256=KOWl011mwUX2OdLmcTM690CTOneZEJxK9RIXbXyGL_o,14251 -setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218 -setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 -setuptools/ssl_support.py,sha256=TNNOq3VyV-4wkRwm0dmyIzF-iXBeWv4yIQ99eWa_bV8,8543 -setuptools/unicode_utils.py,sha256=NOiZ_5hD72A6w-4wVj8awHFM3n51Kmw1Ic_vx15XFqw,996 -setuptools/version.py,sha256=og_cuZQb0QI6ukKZFfZWPlr1HgJBPPn2vO2m_bI9ZTE,144 -setuptools/wheel.py,sha256=YLN2fczDVxkX3wjHlt_EMh-4MfHO6Ns6ldRpnkn-aa8,8371 -setuptools/windows_support.py,sha256=5GrfqSP2-dLGJoZTq2g6dCKkyQxxa2n5IQiXlJCoYEE,714 diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/WHEEL b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/WHEEL deleted file mode 100644 index b552003ff90e66227ec90d1b159324f140d46001..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/dependency_links.txt b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/dependency_links.txt deleted file mode 100644 index e87d02103ede91545d70783dd59653d183424b68..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/dependency_links.txt +++ /dev/null @@ -1,2 +0,0 @@ -https://files.pythonhosted.org/packages/source/c/certifi/certifi-2016.9.26.tar.gz#md5=baa81e951a29958563689d868ef1064d -https://files.pythonhosted.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2 diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/entry_points.txt b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/entry_points.txt deleted file mode 100644 index f57c6d2a5ceef5d7c89702a28ce5a91177b87e17..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/entry_points.txt +++ /dev/null @@ -1,68 +0,0 @@ -[console_scripts] -easy_install = setuptools.command.easy_install:main -easy_install-3.8 = setuptools.command.easy_install:main - -[distutils.commands] -alias = setuptools.command.alias:alias -bdist_egg = setuptools.command.bdist_egg:bdist_egg -bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm -bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst -build_clib = setuptools.command.build_clib:build_clib -build_ext = setuptools.command.build_ext:build_ext -build_py = setuptools.command.build_py:build_py -develop = setuptools.command.develop:develop -dist_info = setuptools.command.dist_info:dist_info -easy_install = setuptools.command.easy_install:easy_install -egg_info = setuptools.command.egg_info:egg_info -install = setuptools.command.install:install -install_egg_info = setuptools.command.install_egg_info:install_egg_info -install_lib = setuptools.command.install_lib:install_lib -install_scripts = setuptools.command.install_scripts:install_scripts -rotate = setuptools.command.rotate:rotate -saveopts = setuptools.command.saveopts:saveopts -sdist = setuptools.command.sdist:sdist -setopt = setuptools.command.setopt:setopt -test = setuptools.command.test:test -upload_docs = setuptools.command.upload_docs:upload_docs - -[distutils.setup_keywords] -convert_2to3_doctests = setuptools.dist:assert_string_list -dependency_links = setuptools.dist:assert_string_list -eager_resources = setuptools.dist:assert_string_list -entry_points = setuptools.dist:check_entry_points -exclude_package_data = setuptools.dist:check_package_data -extras_require = setuptools.dist:check_extras -include_package_data = setuptools.dist:assert_bool -install_requires = setuptools.dist:check_requirements -namespace_packages = setuptools.dist:check_nsp -package_data = setuptools.dist:check_package_data -packages = setuptools.dist:check_packages -python_requires = setuptools.dist:check_specifier -setup_requires = setuptools.dist:check_requirements -test_loader = setuptools.dist:check_importable -test_runner = setuptools.dist:check_importable -test_suite = setuptools.dist:check_test_suite -tests_require = setuptools.dist:check_requirements -use_2to3 = setuptools.dist:assert_bool -use_2to3_exclude_fixers = setuptools.dist:assert_string_list -use_2to3_fixers = setuptools.dist:assert_string_list -zip_safe = setuptools.dist:assert_bool - -[egg_info.writers] -PKG-INFO = setuptools.command.egg_info:write_pkg_info -dependency_links.txt = setuptools.command.egg_info:overwrite_arg -depends.txt = setuptools.command.egg_info:warn_depends_obsolete -eager_resources.txt = setuptools.command.egg_info:overwrite_arg -entry_points.txt = setuptools.command.egg_info:write_entries -namespace_packages.txt = setuptools.command.egg_info:overwrite_arg -requires.txt = setuptools.command.egg_info:write_requirements -top_level.txt = setuptools.command.egg_info:write_toplevel_names - -[setuptools.finalize_distribution_options] -2to3_doctests = setuptools.dist:Distribution._finalize_2to3_doctests -keywords = setuptools.dist:Distribution._finalize_setup_keywords -parent_finalize = setuptools.dist:_Distribution.finalize_options - -[setuptools.installation] -eggsecutable = setuptools.command.easy_install:bootstrap - diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/top_level.txt b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/top_level.txt deleted file mode 100644 index 4577c6a795e510bf7578236665f582c3770fb42e..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/top_level.txt +++ /dev/null @@ -1,3 +0,0 @@ -easy_install -pkg_resources -setuptools diff --git a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/zip-safe b/venv/Lib/site-packages/setuptools-49.2.0.dist-info/zip-safe deleted file mode 100644 index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools-49.2.0.dist-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/venv/Lib/site-packages/setuptools/__init__.py b/venv/Lib/site-packages/setuptools/__init__.py index 8388251115d2fd2dd5e83722904feea6003f6162..43d1c96eb28b97580f7d4878c680a852e929ba7a 100644 --- a/venv/Lib/site-packages/setuptools/__init__.py +++ b/venv/Lib/site-packages/setuptools/__init__.py @@ -1,51 +1,41 @@ """Extensions to the 'distutils' for large or complex distributions""" -import os +from fnmatch import fnmatchcase import functools +import os +import re -# Disabled for now due to: #2228, #2230 -import setuptools.distutils_patch # noqa: F401 +import _distutils_hack.override # noqa: F401 import distutils.core -import distutils.filelist -import re from distutils.errors import DistutilsOptionError from distutils.util import convert_path -from fnmatch import fnmatchcase from ._deprecation_warning import SetuptoolsDeprecationWarning -from setuptools.extern.six import PY3, string_types -from setuptools.extern.six.moves import filter, map - import setuptools.version from setuptools.extension import Extension from setuptools.dist import Distribution from setuptools.depends import Require from . import monkey - -__metaclass__ = type +from . import logging __all__ = [ - 'setup', 'Distribution', 'Command', 'Extension', 'Require', + 'setup', + 'Distribution', + 'Command', + 'Extension', + 'Require', 'SetuptoolsDeprecationWarning', - 'find_packages' + 'find_packages', + 'find_namespace_packages', ] -if PY3: - __all__.append('find_namespace_packages') - __version__ = setuptools.version.__version__ bootstrap_install_from = None -# If we run 2to3 on .py files, should we also convert docstrings? -# Default: yes; assume that we can detect doctests reliably -run_2to3_on_doctests = True -# Standard package names for fixer packages -lib2to3_fixer_packages = ['lib2to3.fixes'] - class PackageFinder: """ @@ -70,10 +60,13 @@ class PackageFinder: shell style wildcard patterns just like 'exclude'. """ - return list(cls._find_packages_iter( - convert_path(where), - cls._build_filter('ez_setup', '*__pycache__', *exclude), - cls._build_filter(*include))) + return list( + cls._find_packages_iter( + convert_path(where), + cls._build_filter('ez_setup', '*__pycache__', *exclude), + cls._build_filter(*include), + ) + ) @classmethod def _find_packages_iter(cls, where, exclude, include): @@ -92,7 +85,7 @@ class PackageFinder: package = rel_path.replace(os.path.sep, '.') # Skip directory trees that are not valid packages - if ('.' in dir or not cls._looks_like_package(full_path)): + if '.' in dir or not cls._looks_like_package(full_path): continue # Should this package be included? @@ -124,9 +117,7 @@ class PEP420PackageFinder(PackageFinder): find_packages = PackageFinder.find - -if PY3: - find_namespace_packages = PEP420PackageFinder.find +find_namespace_packages = PEP420PackageFinder.find def _install_setup_requires(attrs): @@ -137,12 +128,10 @@ def _install_setup_requires(attrs): A minimal version of a distribution for supporting the fetch_build_eggs interface. """ + def __init__(self, attrs): _incl = 'dependency_links', 'setup_requires' - filtered = { - k: attrs[k] - for k in set(_incl) & set(attrs) - } + filtered = {k: attrs[k] for k in set(_incl) & set(attrs)} distutils.core.Distribution.__init__(self, filtered) def finalize_options(self): @@ -161,6 +150,7 @@ def _install_setup_requires(attrs): def setup(**attrs): # Make sure we have any requirements needed to interpret 'attrs'. + logging.configure() _install_setup_requires(attrs) return distutils.core.setup(**attrs) @@ -189,9 +179,10 @@ class Command(_Command): if val is None: setattr(self, option, default) return default - elif not isinstance(val, string_types): - raise DistutilsOptionError("'%s' must be a %s (got `%s`)" - % (option, what, val)) + elif not isinstance(val, str): + raise DistutilsOptionError( + "'%s' must be a %s (got `%s`)" % (option, what, val) + ) return val def ensure_string_list(self, option): @@ -203,17 +194,17 @@ class Command(_Command): val = getattr(self, option) if val is None: return - elif isinstance(val, string_types): + elif isinstance(val, str): setattr(self, option, re.split(r',\s*|\s+', val)) else: if isinstance(val, list): - ok = all(isinstance(v, string_types) for v in val) + ok = all(isinstance(v, str) for v in val) else: ok = False if not ok: raise DistutilsOptionError( - "'%s' must be a list of strings (got %r)" - % (option, val)) + "'%s' must be a list of strings (got %r)" % (option, val) + ) def reinitialize_command(self, command, reinit_subcommands=0, **kw): cmd = _Command.reinitialize_command(self, command, reinit_subcommands) diff --git a/venv/Lib/site-packages/setuptools/_distutils/__init__.py b/venv/Lib/site-packages/setuptools/_distutils/__init__.py index 7dac55b601eef6950ddf24be9170f1656cb15366..8fd493b42c76a5a630ef7eb3bb002314a9b15c94 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/__init__.py +++ b/venv/Lib/site-packages/setuptools/_distutils/__init__.py @@ -9,7 +9,16 @@ used from a setup script as """ import sys +import importlib __version__ = sys.version[:sys.version.index(' ')] -local = True + +try: + # Allow Debian and pkgsrc (only) to customize system + # behavior. Ref pypa/distutils#2 and pypa/distutils#16. + # This hook is deprecated and no other environments + # should use it. + importlib.import_module('_distutils_system_mod') +except ImportError: + pass diff --git a/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py index 0e98692e31633f4b964407c3d065bbe13e128eb1..c41ea9ae30b6f3d753c3fb71d2766e7be263df0b 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py +++ b/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py @@ -15,7 +15,11 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler. import os import subprocess -import winreg +import contextlib +import warnings +import unittest.mock +with contextlib.suppress(ImportError): + import winreg from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -244,7 +248,7 @@ class MSVCCompiler(CCompiler) : # Future releases of Python 3.x will include all past # versions of vcruntime*.dll for compatibility. self.compile_options = [ - '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' + '/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD' ] self.compile_options_debug = [ @@ -501,8 +505,30 @@ class MSVCCompiler(CCompiler) : log.debug("skipping %s (up-to-date)", output_filename) def spawn(self, cmd): - env = dict(os.environ, path=self._paths) - return super().spawn(cmd, env=env) + env = dict(os.environ, PATH=self._paths) + with self._fallback_spawn(cmd, env) as fallback: + return super().spawn(cmd, env=env) + return fallback.value + + @contextlib.contextmanager + def _fallback_spawn(self, cmd, env): + """ + Discovered in pypa/distutils#15, some tools monkeypatch the compiler, + so the 'env' kwarg causes a TypeError. Detect this condition and + restore the legacy, unsafe behavior. + """ + bag = type('Bag', (), {})() + try: + yield bag + except TypeError as exc: + if "unexpected keyword argument 'env'" not in str(exc): + raise + else: + return + warnings.warn( + "Fallback spawn triggered. Please update distutils monkeypatch.") + with unittest.mock.patch.dict('os.environ', env): + bag.value = super().spawn(cmd) # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in diff --git a/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py index b5ef143e72c56481128b8d7f91784096336e2bff..777fc661eac7822343fdefd4586d7bfb3ad870e9 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py +++ b/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py @@ -392,7 +392,7 @@ class CCompiler: return output_dir, macros, include_dirs def _prep_compile(self, sources, output_dir, depends=None): - """Decide which souce files must be recompiled. + """Decide which source files must be recompiled. Determine the list of object files corresponding to 'sources', and figure out which ones really need to be recompiled. @@ -792,6 +792,8 @@ int main (int argc, char **argv) { objects = self.compile([fname], include_dirs=include_dirs) except CompileError: return False + finally: + os.remove(fname) try: self.link_executable(objects, "a.out", @@ -799,6 +801,11 @@ int main (int argc, char **argv) { library_dirs=library_dirs) except (LinkError, TypeError): return False + else: + os.remove(os.path.join(self.output_dir or '', "a.out")) + finally: + for fn in objects: + os.remove(fn) return True def find_library_file (self, dirs, lib, debug=0): @@ -906,8 +913,8 @@ int main (int argc, char **argv) { def execute(self, func, args, msg=None, level=1): execute(func, args, msg, self.dry_run) - def spawn(self, cmd): - spawn(cmd, dry_run=self.dry_run) + def spawn(self, cmd, **kwargs): + spawn(cmd, dry_run=self.dry_run, **kwargs) def move_file(self, src, dst): return move_file(src, dst, dry_run=self.dry_run) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build.py b/venv/Lib/site-packages/setuptools/_distutils/command/build.py index a86df0bc7f921889bc0b28eeee6600a9bab41ebc..4355a63235c7b6d8a9b8389c3fc203b9b3529a36 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/command/build.py +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build.py @@ -102,7 +102,7 @@ class build(Command): # particular module distribution -- if user didn't supply it, pick # one of 'build_purelib' or 'build_platlib'. if self.build_lib is None: - if self.distribution.ext_modules: + if self.distribution.has_ext_modules(): self.build_lib = self.build_platlib else: self.build_lib = self.build_purelib diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py index 1a9bd1200f82358c5a758c69c433bb55621e157e..181671bf19ffbfd1740be925a202423ad863336c 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py @@ -16,6 +16,7 @@ from distutils.dep_util import newer_group from distutils.extension import Extension from distutils.util import get_platform from distutils import log +from . import py37compat from site import USER_BASE @@ -201,9 +202,7 @@ class build_ext(Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.dirname(get_config_h_filename())) - _sys_home = getattr(sys, '_home', None) - if _sys_home: - self.library_dirs.append(_sys_home) + self.library_dirs.append(sys.base_exec_prefix) # Use the .lib files for the correct architecture if self.plat_name == 'win32': @@ -219,7 +218,7 @@ class build_ext(Command): # For extensions under Cygwin, Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin': - if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): + if not sysconfig.python_build: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), @@ -689,13 +688,15 @@ class build_ext(Command): provided, "PyInit_" + module_name. Only relevant on Windows, where the .pyd file (DLL) must export the module "PyInit_" function. """ - suffix = '_' + ext.name.split('.')[-1] + name = ext.name.split('.')[-1] try: # Unicode module name support as defined in PEP-489 # https://www.python.org/dev/peps/pep-0489/#export-hook-name - suffix.encode('ascii') + name.encode('ascii') except UnicodeEncodeError: - suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') + suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii') + else: + suffix = "_" + name initfunc_name = "PyInit" + suffix if initfunc_name not in ext.export_symbols: @@ -751,4 +752,4 @@ class build_ext(Command): ldversion = get_config_var('LDVERSION') return ext.libraries + ['python' + ldversion] - return ext.libraries + return ext.libraries + py37compat.pythonlib() diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py index edc2171cd122dda26a96a2770d2cfa69ccab417b..7ef9bcefdec05490393466f032548f24d41ea0b8 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py @@ -9,7 +9,7 @@ import glob from distutils.core import Command from distutils.errors import * -from distutils.util import convert_path, Mixin2to3 +from distutils.util import convert_path from distutils import log class build_py (Command): @@ -390,27 +390,3 @@ class build_py (Command): if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) - -class build_py_2to3(build_py, Mixin2to3): - def run(self): - self.updated_files = [] - - # Base class code - if self.py_modules: - self.build_modules() - if self.packages: - self.build_packages() - self.build_package_data() - - # 2to3 - self.run_2to3(self.updated_files) - - # Remaining base class code - self.byte_compile(self.get_outputs(include_bytecode=0)) - - def build_module(self, module, module_file, package): - res = build_py.build_module(self, module, module_file, package) - if res[1]: - # file was copied - self.updated_files.append(res[0]) - return res diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py index ccc70e6465016ef2bbde845ea7eb99b6078eb9d8..e3312cf0caa2a8f4e6afd99435442fa01dd8cf65 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py @@ -7,7 +7,7 @@ from stat import ST_MODE from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer -from distutils.util import convert_path, Mixin2to3 +from distutils.util import convert_path from distutils import log import tokenize @@ -150,11 +150,3 @@ class build_scripts(Command): os.chmod(file, newmode) # XXX should we modify self.outfiles? return outfiles, updated_files - -class build_scripts_2to3(build_scripts, Mixin2to3): - - def copy_scripts(self): - outfiles, updated_files = build_scripts.copy_scripts(self) - if not self.dry_run: - self.run_2to3(updated_files) - return outfiles, updated_files diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install.py b/venv/Lib/site-packages/setuptools/_distutils/command/install.py index 13feeb890ff169f23bf0c1948e9579220c62e295..cdcc05281437bd03b27008796ede8f6f6eee7ac7 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/command/install.py +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install.py @@ -4,6 +4,9 @@ Implements the Distutils 'install' command.""" import sys import os +import contextlib +import sysconfig +import itertools from distutils import log from distutils.core import Command @@ -20,62 +23,62 @@ from site import USER_SITE HAS_USER_SITE = True WINDOWS_SCHEME = { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', + 'purelib': '{base}/Lib/site-packages', + 'platlib': '{base}/Lib/site-packages', + 'headers': '{base}/Include/{dist_name}', + 'scripts': '{base}/Scripts', + 'data' : '{base}', } INSTALL_SCHEMES = { - 'unix_prefix': { - 'purelib': '$base/lib/python$py_version_short/site-packages', - 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages', - 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', + 'posix_prefix': { + 'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages', + 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}/site-packages', + 'headers': '{base}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}', + 'scripts': '{base}/bin', + 'data' : '{base}', }, - 'unix_home': { - 'purelib': '$base/lib/python', - 'platlib': '$base/$platlibdir/python', - 'headers': '$base/include/python/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', + 'posix_home': { + 'purelib': '{base}/lib/{implementation_lower}', + 'platlib': '{base}/{platlibdir}/{implementation_lower}', + 'headers': '{base}/include/{implementation_lower}/{dist_name}', + 'scripts': '{base}/bin', + 'data' : '{base}', }, 'nt': WINDOWS_SCHEME, 'pypy': { - 'purelib': '$base/site-packages', - 'platlib': '$base/site-packages', - 'headers': '$base/include/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', + 'purelib': '{base}/site-packages', + 'platlib': '{base}/site-packages', + 'headers': '{base}/include/{dist_name}', + 'scripts': '{base}/bin', + 'data' : '{base}', }, 'pypy_nt': { - 'purelib': '$base/site-packages', - 'platlib': '$base/site-packages', - 'headers': '$base/include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', + 'purelib': '{base}/site-packages', + 'platlib': '{base}/site-packages', + 'headers': '{base}/include/{dist_name}', + 'scripts': '{base}/Scripts', + 'data' : '{base}', }, } # user site schemes if HAS_USER_SITE: INSTALL_SCHEMES['nt_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Python$py_version_nodot/Scripts', - 'data' : '$userbase', + 'purelib': '{usersite}', + 'platlib': '{usersite}', + 'headers': '{userbase}/{implementation}{py_version_nodot}/Include/{dist_name}', + 'scripts': '{userbase}/{implementation}{py_version_nodot}/Scripts', + 'data' : '{userbase}', } - INSTALL_SCHEMES['unix_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', + INSTALL_SCHEMES['posix_user'] = { + 'purelib': '{usersite}', + 'platlib': '{usersite}', 'headers': - '$userbase/include/python$py_version_short$abiflags/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', + '{userbase}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}', + 'scripts': '{userbase}/bin', + 'data' : '{userbase}', } # The keys to an installation scheme; if any new types of files are to be @@ -84,6 +87,96 @@ if HAS_USER_SITE: SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') +def _load_sysconfig_schemes(): + with contextlib.suppress(AttributeError): + return { + scheme: sysconfig.get_paths(scheme, expand=False) + for scheme in sysconfig.get_scheme_names() + } + + +def _load_schemes(): + """ + Extend default schemes with schemes from sysconfig. + """ + + sysconfig_schemes = _load_sysconfig_schemes() or {} + + return { + scheme: { + **INSTALL_SCHEMES.get(scheme, {}), + **sysconfig_schemes.get(scheme, {}), + } + for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes)) + } + + +def _get_implementation(): + if hasattr(sys, 'pypy_version_info'): + return 'PyPy' + else: + return 'Python' + + +def _select_scheme(ob, name): + scheme = _inject_headers(name, _load_scheme(_resolve_scheme(name))) + vars(ob).update(_remove_set(ob, _scheme_attrs(scheme))) + + +def _remove_set(ob, attrs): + """ + Include only attrs that are None in ob. + """ + return { + key: value + for key, value in attrs.items() + if getattr(ob, key) is None + } + + +def _resolve_scheme(name): + os_name, sep, key = name.partition('_') + try: + resolved = sysconfig.get_preferred_scheme(key) + except Exception: + resolved = _pypy_hack(name) + return resolved + + +def _load_scheme(name): + return _load_schemes()[name] + + +def _inject_headers(name, scheme): + """ + Given a scheme name and the resolved scheme, + if the scheme does not include headers, resolve + the fallback scheme for the name and use headers + from it. pypa/distutils#88 + """ + # Bypass the preferred scheme, which may not + # have defined headers. + fallback = _load_scheme(_pypy_hack(name)) + scheme.setdefault('headers', fallback['headers']) + return scheme + + +def _scheme_attrs(scheme): + """Resolve install directories by applying the install schemes.""" + return { + f'install_{key}': scheme[key] + for key in SCHEME_KEYS + } + + +def _pypy_hack(name): + PY37 = sys.version_info < (3, 8) + old_pypy = hasattr(sys, 'pypy_version_info') and PY37 + prefix = not name.endswith(('_user', '_home')) + pypy_name = 'pypy' + '_nt' * (os.name == 'nt') + return pypy_name if old_pypy and prefix else name + + class install(Command): description = "install everything from build directory" @@ -278,7 +371,7 @@ class install(Command): # input a heady brew of prefix, exec_prefix, home, install_base, # install_platbase, user-supplied versions of # install_{purelib,platlib,lib,scripts,data,...}, and the - # INSTALL_SCHEME dictionary above. Phew! + # install schemes. Phew! self.dump_dirs("pre-finalize_{unix,other}") @@ -313,6 +406,9 @@ class install(Command): 'exec_prefix': exec_prefix, 'abiflags': abiflags, 'platlibdir': getattr(sys, 'platlibdir', 'lib'), + 'implementation_lower': _get_implementation().lower(), + 'implementation': _get_implementation(), + 'platsubdir': sysconfig.get_config_var('platsubdir'), } if HAS_USER_SITE: @@ -327,6 +423,8 @@ class install(Command): # everything else. self.config_vars['base'] = self.install_base self.config_vars['platbase'] = self.install_platbase + self.config_vars['installed_base'] = ( + sysconfig.get_config_vars()['installed_base']) if DEBUG: from pprint import pprint @@ -348,7 +446,7 @@ class install(Command): # module distribution is pure or not. Of course, if the user # already specified install_lib, use their selection. if self.install_lib is None: - if self.distribution.ext_modules: # has extensions: non-pure + if self.distribution.has_ext_modules(): # has extensions: non-pure self.install_lib = self.install_platlib else: self.install_lib = self.install_purelib @@ -407,12 +505,17 @@ class install(Command): def finalize_unix(self): """Finalizes options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: - if ((self.install_lib is None and - self.install_purelib is None and - self.install_platlib is None) or + incomplete_scheme = ( + ( + self.install_lib is None and + self.install_purelib is None and + self.install_platlib is None + ) or self.install_headers is None or self.install_scripts is None or - self.install_data is None): + self.install_data is None + ) + if incomplete_scheme: raise DistutilsOptionError( "install-base or install-platbase supplied, but " "installation scheme is incomplete") @@ -423,18 +526,23 @@ class install(Command): raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("unix_user") + self.select_scheme("posix_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") + self.select_scheme("posix_home") else: if self.prefix is None: if self.exec_prefix is not None: raise DistutilsOptionError( "must not supply exec-prefix without prefix") - self.prefix = os.path.normpath(sys.prefix) - self.exec_prefix = os.path.normpath(sys.exec_prefix) + # Allow Fedora to add components to the prefix + _prefix_addition = getattr(sysconfig, '_prefix_addition', "") + + self.prefix = ( + os.path.normpath(sys.prefix) + _prefix_addition) + self.exec_prefix = ( + os.path.normpath(sys.exec_prefix) + _prefix_addition) else: if self.exec_prefix is None: @@ -442,7 +550,7 @@ class install(Command): self.install_base = self.prefix self.install_platbase = self.exec_prefix - self.select_scheme("unix_prefix") + self.select_scheme("posix_prefix") def finalize_other(self): """Finalizes options for non-posix platforms""" @@ -454,7 +562,7 @@ class install(Command): self.select_scheme(os.name + "_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") + self.select_scheme("posix_home") else: if self.prefix is None: self.prefix = os.path.normpath(sys.prefix) @@ -467,19 +575,7 @@ class install(Command): "I don't know how to install stuff on '%s'" % os.name) def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" - # it's the caller's problem if they supply a bad name! - if (hasattr(sys, 'pypy_version_info') and - not name.endswith(('_user', '_home'))): - if os.name == 'nt': - name = 'pypy_nt' - else: - name = 'pypy' - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: - attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) + _select_scheme(self, name) def _expand_attrs(self, attrs): for attr in attrs: @@ -553,7 +649,7 @@ class install(Command): return home = convert_path(os.path.expanduser("~")) for name, path in self.config_vars.items(): - if path.startswith(home) and not os.path.isdir(path): + if str(path).startswith(home) and not os.path.isdir(path): self.debug_print("os.makedirs('%s', 0o700)" % path) os.makedirs(path, 0o700) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py b/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py index 0ddc7367cc608dac2cfb408a08c8b442278a8207..adc0323f98fde748a70aada6930ea29fd22724b1 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py @@ -19,14 +19,21 @@ class install_egg_info(Command): def initialize_options(self): self.install_dir = None - def finalize_options(self): - self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%d.%d.egg-info" % ( + @property + def basename(self): + """ + Allow basename to be overridden by child class. + Ref pypa/distutils#2. + """ + return "%s-%s-py%d.%d.egg-info" % ( to_filename(safe_name(self.distribution.get_name())), to_filename(safe_version(self.distribution.get_version())), *sys.version_info[:2] ) - self.target = os.path.join(self.install_dir, basename) + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + self.target = os.path.join(self.install_dir, self.basename) self.outputs = [self.target] def run(self): diff --git a/venv/Lib/site-packages/setuptools/_distutils/core.py b/venv/Lib/site-packages/setuptools/_distutils/core.py index d603d4a45a73ee4f373fbd1ef934738bda0a1991..f43888ea609dca16f2e6e7d2e758da2dd9e47864 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/core.py +++ b/venv/Lib/site-packages/setuptools/_distutils/core.py @@ -8,6 +8,7 @@ really defined in distutils.dist and distutils.cmd. import os import sys +import tokenize from distutils.debug import DEBUG from distutils.errors import * @@ -144,29 +145,41 @@ def setup (**attrs): # And finally, run all the commands found on the command line. if ok: - try: - dist.run_commands() - except KeyboardInterrupt: - raise SystemExit("interrupted") - except OSError as exc: - if DEBUG: - sys.stderr.write("error: %s\n" % (exc,)) - raise - else: - raise SystemExit("error: %s" % (exc,)) - - except (DistutilsError, - CCompilerError) as msg: - if DEBUG: - raise - else: - raise SystemExit("error: " + str(msg)) + return run_commands(dist) return dist # setup () +def run_commands (dist): + """Given a Distribution object run all the commands, + raising ``SystemExit`` errors in the case of failure. + + This function assumes that either ``sys.argv`` or ``dist.script_args`` + is already set accordingly. + """ + try: + dist.run_commands() + except KeyboardInterrupt: + raise SystemExit("interrupted") + except OSError as exc: + if DEBUG: + sys.stderr.write("error: %s\n" % (exc,)) + raise + else: + raise SystemExit("error: %s" % (exc,)) + + except (DistutilsError, + CCompilerError) as msg: + if DEBUG: + raise + else: + raise SystemExit("error: " + str(msg)) + + return dist + + def run_setup (script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful @@ -205,14 +218,16 @@ def run_setup (script_name, script_args=None, stop_after="run"): _setup_stop_after = stop_after save_argv = sys.argv.copy() - g = {'__file__': script_name} + g = {'__file__': script_name, '__name__': '__main__'} try: try: sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - with open(script_name, 'rb') as f: - exec(f.read(), g) + # tokenize.open supports automatic encoding detection + with tokenize.open(script_name) as f: + code = f.read().replace(r'\r\n', r'\n') + exec(code, g) finally: sys.argv = save_argv _setup_stop_after = None diff --git a/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py index 66c12dd35830b29d9b80bc1fecef65bbc46fe862..fd082f6d2789737ec0c96bdde7a033e2c1a3b9ab 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py +++ b/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py @@ -44,19 +44,21 @@ cygwin in no-cygwin mode). # (ld supports -shared) # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) +# * llvm-mingw with Clang 11 works +# (lld supports -shared) import os import sys import copy -from subprocess import Popen, PIPE, check_output -import re +import shlex +import warnings +from subprocess import check_output from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import (DistutilsExecError, CCompilerError, CompileError, UnknownFileError) -from distutils.version import LooseVersion -from distutils.spawn import find_executable +from distutils.version import LooseVersion, suppress_known_deprecation def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -80,6 +82,15 @@ def get_msvcr(): elif msc_ver == '1600': # VS2010 / MSVC 10.0 return ['msvcr100'] + elif msc_ver == '1700': + # VS2012 / MSVC 11.0 + return ['msvcr110'] + elif msc_ver == '1800': + # VS2013 / MSVC 12.0 + return ['msvcr120'] + elif 1900 <= int(msc_ver) < 2000: + # VS2015 / MSVC 14.0 + return ['ucrt', 'vcruntime140'] else: raise ValueError("Unknown MS Compiler version %s " % msc_ver) @@ -109,50 +120,37 @@ class CygwinCCompiler(UnixCCompiler): "Compiling may fail because of undefined preprocessor macros." % details) - self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_versions() - self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % - (self.gcc_version, - self.ld_version, - self.dllwrap_version) ) - - # ld_version >= "2.10.90" and < "2.13" should also be able to use - # gcc -mdll instead of dllwrap - # Older dllwraps had own version numbers, newer ones use the - # same as the rest of binutils ( also ld ) - # dllwrap 2.10.90 is buggy - if self.ld_version >= "2.10.90": - self.linker_dll = "gcc" - else: - self.linker_dll = "dllwrap" + self.cc = os.environ.get('CC', 'gcc') + self.cxx = os.environ.get('CXX', 'g++') - # ld_version >= "2.13" support -shared so use it instead of - # -mdll -static - if self.ld_version >= "2.13": - shared_option = "-shared" - else: - shared_option = "-mdll -static" - - # Hard-code GCC because that's what this is all about. - # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -mcygwin -O -Wall', - compiler_so='gcc -mcygwin -mdll -O -Wall', - compiler_cxx='g++ -mcygwin -O -Wall', - linker_exe='gcc -mcygwin', + self.linker_dll = self.cc + shared_option = "-shared" + + self.set_executables(compiler='%s -mcygwin -O -Wall' % self.cc, + compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc, + compiler_cxx='%s -mcygwin -O -Wall' % self.cxx, + linker_exe='%s -mcygwin' % self.cc, linker_so=('%s -mcygwin %s' % (self.linker_dll, shared_option))) - # cygwin and mingw32 need different sets of libraries - if self.gcc_version == "2.91.57": - # cygwin shouldn't need msvcrt, but without the dlls will crash - # (gcc version 2.91.57) -- perhaps something about initialization - self.dll_libraries=["msvcrt"] - self.warn( - "Consider upgrading to a newer version of gcc") - else: - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. - self.dll_libraries = get_msvcr() + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() + + @property + def gcc_version(self): + # Older numpy dependend on this existing to check for ancient + # gcc versions. This doesn't make much sense with clang etc so + # just hardcode to something recent. + # https://github.com/numpy/numpy/pull/20333 + warnings.warn( + "gcc_version attribute of CygwinCCompiler is deprecated. " + "Instead of returning actual gcc version a fixed value 11.2.0 is returned.", + DeprecationWarning, + stacklevel=2, + ) + with suppress_known_deprecation(): + return LooseVersion("11.2.0") def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): """Compiles the source by spawning GCC and windres if needed.""" @@ -214,24 +212,17 @@ class CygwinCCompiler(UnixCCompiler): # next add options for def-file and to creating import libraries - # dllwrap uses different options than gcc/ld - if self.linker_dll == "dllwrap": - extra_preargs.extend(["--output-lib", lib_file]) - # for dllwrap we have to use a special option - extra_preargs.extend(["--def", def_file]) - # we use gcc/ld here and can be sure ld is >= 2.9.10 - else: - # doesn't work: bfd_close build\...\libfoo.a: Invalid operation - #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) - # for gcc/ld the def-file is specified as any object files - objects.append(def_file) + # doesn't work: bfd_close build\...\libfoo.a: Invalid operation + #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) + # for gcc/ld the def-file is specified as any object files + objects.append(def_file) #end: if ((export_symbols is not None) and # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): # who wants symbols and a many times larger output file # should explicitly switch the debug mode on - # otherwise we let dllwrap/ld strip the output file + # otherwise we let ld strip the output file # (On my machine: 10KiB < stripped_file < ??100KiB # unstripped_file = stripped_file + XXX KiB # ( XXX=254 for a typical python extension)) @@ -279,31 +270,19 @@ class Mingw32CCompiler(CygwinCCompiler): CygwinCCompiler.__init__ (self, verbose, dry_run, force) - # ld_version >= "2.13" support -shared so use it instead of - # -mdll -static - if self.ld_version >= "2.13": - shared_option = "-shared" - else: - shared_option = "-mdll -static" + shared_option = "-shared" - # A real mingw32 doesn't need to specify a different entry point, - # but cygwin 2.91.57 in no-cygwin-mode needs it. - if self.gcc_version <= "2.91.57": - entry_point = '--entry _DllMain@12' - else: - entry_point = '' - - if is_cygwingcc(): + if is_cygwincc(self.cc): raise CCompilerError( 'Cygwin gcc cannot be used with --compiler=mingw32') - self.set_executables(compiler='gcc -O -Wall', - compiler_so='gcc -mdll -O -Wall', - compiler_cxx='g++ -O -Wall', - linker_exe='gcc', - linker_so='%s %s %s' - % (self.linker_dll, shared_option, - entry_point)) + self.set_executables(compiler='%s -O -Wall' % self.cc, + compiler_so='%s -mdll -O -Wall' % self.cc, + compiler_cxx='%s -O -Wall' % self.cxx, + linker_exe='%s' % self.cc, + linker_so='%s %s' + % (self.linker_dll, shared_option)) + # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) # (-mthreads: Support thread-safe exception handling on `Mingw32') @@ -351,6 +330,10 @@ def check_config_h(): if "GCC" in sys.version: return CONFIG_H_OK, "sys.version mentions 'GCC'" + # Clang would also work + if "Clang" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'Clang'" + # let's see if __GNUC__ is mentioned in python.h fn = sysconfig.get_config_h_filename() try: @@ -366,38 +349,14 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') +def is_cygwincc(cc): + '''Try to determine if the compiler that would be used is from cygwin.''' + out_string = check_output(shlex.split(cc) + ['-dumpmachine']) + return out_string.strip().endswith(b'cygwin') -def _find_exe_version(cmd): - """Find the version of an executable by running `cmd` in the shell. - If the command is not found, or the output does not match - `RE_VERSION`, returns None. - """ - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - out = Popen(cmd, shell=True, stdout=PIPE).stdout - try: - out_string = out.read() - finally: - out.close() - result = RE_VERSION.search(out_string) - if result is None: - return None - # LooseVersion works with strings - # so we need to decode our bytes - return LooseVersion(result.group(1).decode()) - -def get_versions(): - """ Try to find out the versions of gcc, ld and dllwrap. - - If not possible it returns None for it. - """ - commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] - return tuple([_find_exe_version(cmd) for cmd in commands]) - -def is_cygwingcc(): - '''Try to determine if the gcc that would be used is from cygwin.''' - out_string = check_output(['gcc', '-dumpmachine']) - return out_string.strip().endswith(b'cygwin') +get_versions = None +""" +A stand-in for the previous get_versions() function to prevent failures +when monkeypatched. See pypa/setuptools#2969. +""" diff --git a/venv/Lib/site-packages/setuptools/_distutils/filelist.py b/venv/Lib/site-packages/setuptools/_distutils/filelist.py index c92d5fdba393bb4bab0718ae7006d54eb80e1ec5..82a77384dcbc56690494ad4549ef859071d90af2 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/filelist.py +++ b/venv/Lib/site-packages/setuptools/_distutils/filelist.py @@ -4,13 +4,16 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ -import os, re +import os +import re import fnmatch import functools + from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log + class FileList: """A list of files built by on exploring the filesystem and filtered by applying various patterns to what we find there. @@ -46,7 +49,7 @@ class FileList: if DEBUG: print(msg) - # -- List-like methods --------------------------------------------- + # Collection methods def append(self, item): self.files.append(item) @@ -61,8 +64,7 @@ class FileList: for sort_tuple in sortable_files: self.files.append(os.path.join(*sort_tuple)) - - # -- Other miscellaneous utility methods --------------------------- + # Other miscellaneous utility methods def remove_duplicates(self): # Assumes list has been sorted! @@ -70,8 +72,7 @@ class FileList: if self.files[i] == self.files[i - 1]: del self.files[i] - - # -- "File template" methods --------------------------------------- + # "File template" methods def _parse_template_line(self, line): words = line.split() @@ -146,9 +147,11 @@ class FileList: (dir, ' '.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): - log.warn(("warning: no files found matching '%s' " - "under directory '%s'"), - pattern, dir) + msg = ( + "warning: no files found matching '%s' " + "under directory '%s'" + ) + log.warn(msg, pattern, dir) elif action == 'recursive-exclude': self.debug_print("recursive-exclude %s %s" % @@ -174,8 +177,7 @@ class FileList: raise DistutilsInternalError( "this cannot happen: invalid action '%s'" % action) - - # -- Filtering/selection methods ----------------------------------- + # Filtering/selection methods def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that @@ -219,9 +221,8 @@ class FileList: files_found = True return files_found - - def exclude_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): + def exclude_pattern( + self, pattern, anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match 'pattern'. Other parameters are the same as for 'include_pattern()', above. @@ -240,21 +241,47 @@ class FileList: return files_found -# ---------------------------------------------------------------------- # Utility functions def _find_all_simple(path): """ Find all files under 'path' """ + all_unique = _UniqueDirs.filter(os.walk(path, followlinks=True)) results = ( os.path.join(base, file) - for base, dirs, files in os.walk(path, followlinks=True) + for base, dirs, files in all_unique for file in files ) return filter(os.path.isfile, results) +class _UniqueDirs(set): + """ + Exclude previously-seen dirs from walk results, + avoiding infinite recursion. + Ref https://bugs.python.org/issue44497. + """ + def __call__(self, walk_item): + """ + Given an item from an os.walk result, determine + if the item represents a unique dir for this instance + and if not, prevent further traversal. + """ + base, dirs, files = walk_item + stat = os.stat(base) + candidate = stat.st_dev, stat.st_ino + found = candidate in self + if found: + del dirs[:] + self.add(candidate) + return not found + + @classmethod + def filter(cls, items): + return filter(cls(), items) + + def findall(dir=os.curdir): """ Find all files under 'dir' and return the list of full filenames. @@ -319,7 +346,8 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): if os.sep == '\\': sep = r'\\' pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] - pattern_re = r'%s\A%s%s.*%s%s' % (start, prefix_re, sep, pattern_re, end) + pattern_re = r'%s\A%s%s.*%s%s' % ( + start, prefix_re, sep, pattern_re, end) else: # no prefix -- respect anchor flag if anchor: pattern_re = r'%s\A%s' % (start, pattern_re[len(start):]) diff --git a/venv/Lib/site-packages/setuptools/_distutils/log.py b/venv/Lib/site-packages/setuptools/_distutils/log.py index 8ef6b28ea2ec097ae6ab5d9ac9b2e0d3fc8f4809..a68b156b5b8ded870240b89c20f9d49aec20f786 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/log.py +++ b/venv/Lib/site-packages/setuptools/_distutils/log.py @@ -3,13 +3,14 @@ # The class here is styled after PEP 282 so that it could later be # replaced with a standard Python logging implementation. +import sys + DEBUG = 1 INFO = 2 WARN = 3 ERROR = 4 FATAL = 5 -import sys class Log: @@ -54,6 +55,7 @@ class Log: def fatal(self, msg, *args): self._log(FATAL, msg, args) + _global_log = Log() log = _global_log.log debug = _global_log.debug @@ -62,12 +64,14 @@ warn = _global_log.warn error = _global_log.error fatal = _global_log.fatal + def set_threshold(level): # return the old threshold for use from tests old = _global_log.threshold _global_log.threshold = level return old + def set_verbosity(v): if v <= 0: set_threshold(WARN) diff --git a/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py b/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py index 6934e964abd69b371aa4b0ad9587dea61602049f..14d137752d22df0876c2d07085e29df3f8981322 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py +++ b/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py @@ -291,8 +291,6 @@ def query_vcvarsall(version, arch="x86"): # More globals VERSION = get_build_version() -if VERSION < 8.0: - raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) # MACROS = MacroExpander(VERSION) class MSVCCompiler(CCompiler) : @@ -339,6 +337,8 @@ class MSVCCompiler(CCompiler) : def initialize(self, plat_name=None): # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" + if self.__version < 8.0: + raise DistutilsPlatformError("VC %0.1f is not supported by this module" % self.__version) if plat_name is None: plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. @@ -399,13 +399,13 @@ class MSVCCompiler(CCompiler) : self.preprocess_options = None if self.__arch == "x86": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', + self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG'] else: # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GS-' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', '/Z7', '/D_DEBUG'] diff --git a/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py index d5857cb1ffe423a1d4bb19fd199ef2f6cf7d158f..2d447b857d34c8e8ed1e16b48e7d1d6a7e5c293a 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py +++ b/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py @@ -283,13 +283,13 @@ class MSVCCompiler(CCompiler) : self.preprocess_options = None if self.__arch == "Intel": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , + self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GX' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', '/Z7', '/D_DEBUG'] else: # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GS-' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', '/Z7', '/D_DEBUG'] diff --git a/venv/Lib/site-packages/setuptools/_distutils/spawn.py b/venv/Lib/site-packages/setuptools/_distutils/spawn.py index fc592d4a91249d89941f2b18ca52d20c4fe1ecbd..6e1c89f1f235b29809bfacb6df2cf00f2215a47f 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/spawn.py +++ b/venv/Lib/site-packages/setuptools/_distutils/spawn.py @@ -15,11 +15,6 @@ from distutils.debug import DEBUG from distutils import log -if sys.platform == 'darwin': - _cfg_target = None - _cfg_target_split = None - - def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): """Run another program, specified as a command list 'cmd', in a new process. @@ -40,7 +35,7 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): # in, protect our %-formatting code against horrible death cmd = list(cmd) - log.info(' '.join(cmd)) + log.info(subprocess.list2cmdline(cmd)) if dry_run: return @@ -52,24 +47,10 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): env = env if env is not None else dict(os.environ) if sys.platform == 'darwin': - global _cfg_target, _cfg_target_split - if _cfg_target is None: - from distutils import sysconfig - _cfg_target = sysconfig.get_config_var( - 'MACOSX_DEPLOYMENT_TARGET') or '' - if _cfg_target: - _cfg_target_split = [int(x) for x in _cfg_target.split('.')] - if _cfg_target: - # ensure that the deployment target of build process is not less - # than that used when the interpreter was built. This ensures - # extension modules are built with correct compatibility values - cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) - if _cfg_target_split > [int(x) for x in cur_target.split('.')]: - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' - 'now "%s" but "%s" during configure' - % (cur_target, _cfg_target)) - raise DistutilsPlatformError(my_msg) - env.update(MACOSX_DEPLOYMENT_TARGET=cur_target) + from distutils.util import MACOSX_VERSION_VAR, get_macosx_target_ver + macosx_target_ver = get_macosx_target_ver() + if macosx_target_ver: + env[MACOSX_VERSION_VAR] = macosx_target_ver try: proc = subprocess.Popen(cmd, env=env) diff --git a/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py b/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py index 879b6981ed444e709f5b412c262a4afcb4496374..4a77a431dcf4a8ed61730b0db8caefb269dd3ccf 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py +++ b/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py @@ -13,6 +13,7 @@ import _imp import os import re import sys +import sysconfig from .errors import DistutilsPlatformError @@ -99,9 +100,9 @@ def get_python_inc(plat_specific=0, prefix=None): """ if prefix is None: prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX - if IS_PYPY: - return os.path.join(prefix, 'include') - elif os.name == "posix": + if os.name == "posix": + if IS_PYPY and sys.version_info < (3, 8): + return os.path.join(prefix, 'include') if python_build: # Assume the executable is in the build directory. The # pyconfig.h file should be in the same directory. Since @@ -113,7 +114,8 @@ def get_python_inc(plat_specific=0, prefix=None): else: incdir = os.path.join(get_config_var('srcdir'), 'Include') return os.path.normpath(incdir) - python_dir = 'python' + get_python_version() + build_flags + implementation = 'pypy' if IS_PYPY else 'python' + python_dir = implementation + get_python_version() + build_flags return os.path.join(prefix, "include", python_dir) elif os.name == "nt": if python_build: @@ -128,6 +130,14 @@ def get_python_inc(plat_specific=0, prefix=None): "on platform '%s'" % os.name) +# allow this behavior to be monkey-patched. Ref pypa/distutils#2. +def _posix_lib(standard_lib, libpython, early_prefix, prefix): + if standard_lib: + return libpython + else: + return os.path.join(libpython, "site-packages") + + def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): """Return the directory containing the Python library (standard or site additions). @@ -142,7 +152,8 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): If 'prefix' is supplied, use it instead of sys.base_prefix or sys.base_exec_prefix -- i.e., ignore 'plat_specific'. """ - if IS_PYPY: + + if IS_PYPY and sys.version_info < (3, 8): # PyPy-specific schema if prefix is None: prefix = PREFIX @@ -150,6 +161,8 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return os.path.join(prefix, "lib-python", sys.version[0]) return os.path.join(prefix, 'site-packages') + early_prefix = prefix + if prefix is None: if standard_lib: prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX @@ -164,12 +177,10 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): else: # Pure Python libdir = "lib" + implementation = 'pypy' if IS_PYPY else 'python' libpython = os.path.join(prefix, libdir, - "python" + get_python_version()) - if standard_lib: - return libpython - else: - return os.path.join(libpython, "site-packages") + implementation + get_python_version()) + return _posix_lib(standard_lib, libpython, early_prefix, prefix) elif os.name == "nt": if standard_lib: return os.path.join(prefix, "Lib") @@ -211,10 +222,9 @@ def customize_compiler(compiler): if 'CC' in os.environ: newcc = os.environ['CC'] - if (sys.platform == 'darwin' - and 'LDSHARED' not in os.environ + if('LDSHARED' not in os.environ and ldshared.startswith(cc)): - # On OS X, if CC is overridden, use that as the default + # If CC is overridden, use that as the default # command for LDSHARED as well ldshared = newcc + ldshared[len(cc):] cc = newcc @@ -252,6 +262,9 @@ def customize_compiler(compiler): linker_exe=cc, archiver=archiver) + if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None): + compiler.set_executables(ranlib=os.environ['RANLIB']) + compiler.shared_lib_extension = shlib_suffix @@ -262,21 +275,15 @@ def get_config_h_filename(): inc_dir = os.path.join(_sys_home or project_base, "PC") else: inc_dir = _sys_home or project_base + return os.path.join(inc_dir, 'pyconfig.h') else: - inc_dir = get_python_inc(plat_specific=1) + return sysconfig.get_config_h_filename() - return os.path.join(inc_dir, 'pyconfig.h') def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" - if python_build: - return os.path.join(_sys_home or project_base, "Makefile") - lib_dir = get_python_lib(plat_specific=0, standard_lib=1) - config_file = 'config-{}{}'.format(get_python_version(), build_flags) - if hasattr(sys.implementation, '_multiarch'): - config_file += '-%s' % sys.implementation._multiarch - return os.path.join(lib_dir, config_file, 'Makefile') + return sysconfig.get_makefile_filename() def parse_config_h(fp, g=None): @@ -286,26 +293,7 @@ def parse_config_h(fp, g=None): optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - if g is None: - g = {} - define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") - # - while True: - line = fp.readline() - if not line: - break - m = define_rx.match(line) - if m: - n, v = m.group(1, 2) - try: v = int(v) - except ValueError: pass - g[n] = v - else: - m = undef_rx.match(line) - if m: - g[m.group(1)] = 0 - return g + return sysconfig.parse_config_h(fp, vars=g) # Regexes needed for parsing Makefile (and similar syntaxes, @@ -447,15 +435,21 @@ def expand_makefile_vars(s, vars): _config_vars = None + +_sysconfig_name_tmpl = '_sysconfigdata_{abi}_{platform}_{multiarch}' + + def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', - '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( - abi=sys.abiflags, - platform=sys.platform, - multiarch=getattr(sys.implementation, '_multiarch', ''), - )) + name = os.environ.get( + '_PYTHON_SYSCONFIGDATA_NAME', + _sysconfig_name_tmpl.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + ), + ) try: _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) except ImportError: diff --git a/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py index 4d7a6de740ab3a647a6a2157c0e1dae4b7a10fd4..a07e59889044931c89f7d6699b20da0fa5e0e03e 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py +++ b/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py @@ -13,7 +13,7 @@ the "typical" Unix-style command-line C compiler: * link shared library handled by 'cc -shared' """ -import os, sys, re +import os, sys, re, shlex from distutils import sysconfig from distutils.dep_util import newer @@ -231,33 +231,30 @@ class UnixCCompiler(CCompiler): # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. - compiler = os.path.basename(sysconfig.get_config_var("CC")) + compiler = os.path.basename(shlex.split(sysconfig.get_config_var("CC"))[0]) if sys.platform[:6] == "darwin": - # MacOSX's linker doesn't understand the -R flag at all - return "-L" + dir + from distutils.util import get_macosx_target_ver, split_version + macosx_target_ver = get_macosx_target_ver() + if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]: + return "-Wl,-rpath," + dir + else: # no support for -rpath on earlier macOS versions + return "-L" + dir elif sys.platform[:7] == "freebsd": return "-Wl,-rpath=" + dir elif sys.platform[:5] == "hp-ux": if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] + + # For all compilers, `-Wl` is the presumed way to + # pass a compiler option to the linker and `-R` is + # the way to pass an RPATH. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir else: - if self._is_gcc(compiler): - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir - else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. - return "-R" + dir + return "-Wl,-R" + dir def library_option(self, lib): return "-l" + lib diff --git a/venv/Lib/site-packages/setuptools/_distutils/util.py b/venv/Lib/site-packages/setuptools/_distutils/util.py index 4b002ecef1df8ffd79a9aa067640eb43027a9bf2..ac6d446d681a0231892493fba10dfda450d6d71e 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/util.py +++ b/venv/Lib/site-packages/setuptools/_distutils/util.py @@ -14,6 +14,8 @@ from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log from distutils.errors import DistutilsByteCompileError +from .py35compat import _optim_args_from_interpreter_flags + def get_host_platform(): """Return a string that identifies the current platform. This is used mainly to @@ -79,8 +81,8 @@ def get_host_platform(): machine += ".%s" % bitness[sys.maxsize] # fall through to standard osname-release-machine representation elif osname[:3] == "aix": - from _aix_support import aix_platform - return aix_platform() + from .py38compat import aix_platform + return aix_platform(osname, version, release) elif osname[:6] == "cygwin": osname = "cygwin" rel_re = re.compile (r'[\d.]+', re.ASCII) @@ -101,11 +103,66 @@ def get_platform(): 'x86' : 'win32', 'x64' : 'win-amd64', 'arm' : 'win-arm32', + 'arm64': 'win-arm64', } return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() else: return get_host_platform() + +if sys.platform == 'darwin': + _syscfg_macosx_ver = None # cache the version pulled from sysconfig +MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET' + +def _clear_cached_macosx_ver(): + """For testing only. Do not call.""" + global _syscfg_macosx_ver + _syscfg_macosx_ver = None + +def get_macosx_target_ver_from_syscfg(): + """Get the version of macOS latched in the Python interpreter configuration. + Returns the version as a string or None if can't obtain one. Cached.""" + global _syscfg_macosx_ver + if _syscfg_macosx_ver is None: + from distutils import sysconfig + ver = sysconfig.get_config_var(MACOSX_VERSION_VAR) or '' + if ver: + _syscfg_macosx_ver = ver + return _syscfg_macosx_ver + +def get_macosx_target_ver(): + """Return the version of macOS for which we are building. + + The target version defaults to the version in sysconfig latched at time + the Python interpreter was built, unless overridden by an environment + variable. If neither source has a value, then None is returned""" + + syscfg_ver = get_macosx_target_ver_from_syscfg() + env_ver = os.environ.get(MACOSX_VERSION_VAR) + + if env_ver: + # Validate overridden version against sysconfig version, if have both. + # Ensure that the deployment target of the build process is not less + # than 10.3 if the interpreter was built for 10.3 or later. This + # ensures extension modules are built with correct compatibility + # values, specifically LDSHARED which can use + # '-undefined dynamic_lookup' which only works on >= 10.3. + if syscfg_ver and split_version(syscfg_ver) >= [10, 3] and \ + split_version(env_ver) < [10, 3]: + my_msg = ('$' + MACOSX_VERSION_VAR + ' mismatch: ' + 'now "%s" but "%s" during configure; ' + 'must use 10.3 or later' + % (env_ver, syscfg_ver)) + raise DistutilsPlatformError(my_msg) + return env_ver + return syscfg_ver + + +def split_version(s): + """Convert a dot-separated string into a list of numbers for comparisons""" + return [int(n) for n in s.split('.')] + + def convert_path (pathname): """Return 'pathname' as a name that will work on the native filesystem, i.e. split it on '/' and put it back together again using the current @@ -185,30 +242,43 @@ def check_environ (): def subst_vars (s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. Every - occurrence of '$' followed by a name is considered a variable, and - variable is substituted by the value found in the 'local_vars' - dictionary, or in 'os.environ' if it's not in 'local_vars'. + """ + Perform variable substitution on 'string'. + Variables are indicated by format-style braces ("{var}"). + Variable is substituted by the value found in the 'local_vars' + dictionary or in 'os.environ' if it's not in 'local_vars'. 'os.environ' is first checked/augmented to guarantee that it contains certain values: see 'check_environ()'. Raise ValueError for any variables not found in either 'local_vars' or 'os.environ'. """ check_environ() - def _subst (match, local_vars=local_vars): - var_name = match.group(1) - if var_name in local_vars: - return str(local_vars[var_name]) - else: - return os.environ[var_name] - + lookup = dict(os.environ) + lookup.update((name, str(value)) for name, value in local_vars.items()) try: - return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) + return _subst_compat(s).format_map(lookup) except KeyError as var: - raise ValueError("invalid variable '$%s'" % var) + raise ValueError(f"invalid variable {var}") # subst_vars () +def _subst_compat(s): + """ + Replace shell/Perl-style variable substitution with + format-style. For compatibility. + """ + def _subst(match): + return f'{{{match.group(1)}}}' + repl = re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) + if repl != s: + import warnings + warnings.warn( + "shell/Perl-style substitions are deprecated", + DeprecationWarning, + ) + return repl + + def grok_environment_error (exc, prefix="error: "): # Function kept for backward compatibility. # Used to try clever things with EnvironmentErrors, @@ -420,7 +490,7 @@ byte_compile(files, optimize=%r, force=%r, """ % (optimize, force, prefix, base_dir, verbose)) cmd = [sys.executable] - cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.extend(_optim_args_from_interpreter_flags()) cmd.append(script_name) spawn(cmd, dry_run=dry_run) execute(os.remove, (script_name,), "removing %s" % script_name, @@ -476,84 +546,3 @@ def rfc822_escape (header): lines = header.split('\n') sep = '\n' + 8 * ' ' return sep.join(lines) - -# 2to3 support - -def run_2to3(files, fixer_names=None, options=None, explicit=None): - """Invoke 2to3 on a list of Python files. - The files should all come from the build area, as the - modification is done in-place. To reduce the build time, - only files modified since the last invocation of this - function should be passed in the files argument.""" - - if not files: - return - - # Make this class local, to delay import of 2to3 - from lib2to3.refactor import RefactoringTool, get_fixers_from_package - class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - if fixer_names is None: - fixer_names = get_fixers_from_package('lib2to3.fixes') - r = DistutilsRefactoringTool(fixer_names, options=options) - r.refactor(files, write=True) - -def copydir_run_2to3(src, dest, template=None, fixer_names=None, - options=None, explicit=None): - """Recursively copy a directory, only copying new and changed files, - running run_2to3 over all newly copied Python modules afterward. - - If you give a template string, it's parsed like a MANIFEST.in. - """ - from distutils.dir_util import mkpath - from distutils.file_util import copy_file - from distutils.filelist import FileList - filelist = FileList() - curdir = os.getcwd() - os.chdir(src) - try: - filelist.findall() - finally: - os.chdir(curdir) - filelist.files[:] = filelist.allfiles - if template: - for line in template.splitlines(): - line = line.strip() - if not line: continue - filelist.process_template_line(line) - copied = [] - for filename in filelist.files: - outname = os.path.join(dest, filename) - mkpath(os.path.dirname(outname)) - res = copy_file(os.path.join(src, filename), outname, update=1) - if res[1]: copied.append(outname) - run_2to3([fn for fn in copied if fn.lower().endswith('.py')], - fixer_names=fixer_names, options=options, explicit=explicit) - return copied - -class Mixin2to3: - '''Mixin class for commands that run 2to3. - To configure 2to3, setup scripts may either change - the class variables, or inherit from individual commands - to override how 2to3 is invoked.''' - - # provide list of fixers to run; - # defaults to all from lib2to3.fixers - fixer_names = None - - # options dictionary - options = None - - # list of fixers to invoke even though they are marked as explicit - explicit = None - - def run_2to3(self, files): - return run_2to3(files, self.fixer_names, self.options, self.explicit) diff --git a/venv/Lib/site-packages/setuptools/_distutils/version.py b/venv/Lib/site-packages/setuptools/_distutils/version.py index c33bebaed26aeead3a97b48dcd4f34308ca3976e..35e181dbb6dc23fa4ceb6c6b6a552f82aad038de 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/version.py +++ b/venv/Lib/site-packages/setuptools/_distutils/version.py @@ -27,6 +27,20 @@ Every version number class implements the following interface: """ import re +import warnings +import contextlib + + +@contextlib.contextmanager +def suppress_known_deprecation(): + with warnings.catch_warnings(record=True) as ctx: + warnings.filterwarnings( + action='default', + category=DeprecationWarning, + message="distutils Version classes are deprecated.", + ) + yield ctx + class Version: """Abstract base class for version numbering classes. Just provides @@ -36,6 +50,12 @@ class Version: """ def __init__ (self, vstring=None): + warnings.warn( + "distutils Version classes are deprecated. " + "Use packaging.version instead.", + DeprecationWarning, + stacklevel=2, + ) if vstring: self.parse(vstring) @@ -165,7 +185,8 @@ class StrictVersion (Version): def _cmp (self, other): if isinstance(other, str): - other = StrictVersion(other) + with suppress_known_deprecation(): + other = StrictVersion(other) elif not isinstance(other, StrictVersion): return NotImplemented @@ -301,11 +322,6 @@ class LooseVersion (Version): component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) - def __init__ (self, vstring=None): - if vstring: - self.parse(vstring) - - def parse (self, vstring): # I've given up on thinking I can reconstruct the version string # from the parsed tuple -- so I just store the string here for diff --git a/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py b/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py index 062c98f2489951a9b215c5f02d7cdb71605ec1b3..55f25d91ae06bbc18ad3974f1908f5ac8270d519 100644 --- a/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py +++ b/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py @@ -23,7 +23,9 @@ def splitUp(pred): if not res: raise ValueError("bad package restriction syntax: %r" % pred) comp, verStr = res.groups() - return (comp, distutils.version.StrictVersion(verStr)) + with distutils.version.suppress_known_deprecation(): + other = distutils.version.StrictVersion(verStr) + return (comp, other) compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, ">": operator.gt, ">=": operator.ge, "!=": operator.ne} @@ -162,5 +164,6 @@ def split_provision(value): raise ValueError("illegal provides specification: %r" % value) ver = m.group(2) or None if ver: - ver = distutils.version.StrictVersion(ver) + with distutils.version.suppress_known_deprecation(): + ver = distutils.version.StrictVersion(ver) return m.group(1), ver diff --git a/venv/Lib/site-packages/setuptools/_imp.py b/venv/Lib/site-packages/setuptools/_imp.py index 451e45a831d8cf293099ff924a8528b902185fe0..47efd792b3cd04f0646adf7d3ef1811d201f8873 100644 --- a/venv/Lib/site-packages/setuptools/_imp.py +++ b/venv/Lib/site-packages/setuptools/_imp.py @@ -41,12 +41,12 @@ def find_module(module, paths=None): spec.loader, importlib.machinery.FrozenImporter): kind = PY_FROZEN path = None # imp compabilty - suffix = mode = '' # imp compability + suffix = mode = '' # imp compatibility elif spec.origin == 'built-in' or static and issubclass( spec.loader, importlib.machinery.BuiltinImporter): kind = C_BUILTIN path = None # imp compabilty - suffix = mode = '' # imp compability + suffix = mode = '' # imp compatibility elif spec.has_location: path = spec.origin suffix = os.path.splitext(path)[1] diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py index dc95138d049ba3194964d528b552a6d1514fa382..c359122f97125ed630760029f7fd0689f1caefd3 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py @@ -1,7 +1,6 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function __all__ = [ "__title__", @@ -18,10 +17,10 @@ __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "19.2" +__version__ = "21.2" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" -__license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2014-2019 %s" % __author__ +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py index a0cf67df5245be16a020ca048832e180f7ce8661..3c50c5dcfeeda2efed282200a5c5cc8c5f7542f7 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py @@ -1,7 +1,6 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function from .__about__ import ( __author__, diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_compat.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_compat.py deleted file mode 100644 index 25da473c196855ad59a6d2d785ef1ddef49795be..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/_compat.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -import sys - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -# flake8: noqa - -if PY3: - string_types = (str,) -else: - string_types = (basestring,) - - -def with_metaclass(meta, *bases): - """ - Create a base class with a metaclass. - """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py index 68dcca634d8e3f0081bad2f9ae5e653a2942db68..951549753afa255148c7c60d868303963f8c1813 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py @@ -1,68 +1,67 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function -class Infinity(object): - def __repr__(self): +class InfinityType: + def __repr__(self) -> str: return "Infinity" - def __hash__(self): + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): + def __lt__(self, other: object) -> bool: return False - def __le__(self, other): + def __le__(self, other: object) -> bool: return False - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): + def __gt__(self, other: object) -> bool: return True - def __ge__(self, other): + def __ge__(self, other: object) -> bool: return True - def __neg__(self): + def __neg__(self: object) -> "NegativeInfinityType": return NegativeInfinity -Infinity = Infinity() +Infinity = InfinityType() -class NegativeInfinity(object): - def __repr__(self): +class NegativeInfinityType: + def __repr__(self) -> str: return "-Infinity" - def __hash__(self): + def __hash__(self) -> int: return hash(repr(self)) - def __lt__(self, other): + def __lt__(self, other: object) -> bool: return True - def __le__(self, other): + def __le__(self, other: object) -> bool: return True - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return isinstance(other, self.__class__) - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not isinstance(other, self.__class__) - def __gt__(self, other): + def __gt__(self, other: object) -> bool: return False - def __ge__(self, other): + def __ge__(self, other: object) -> bool: return False - def __neg__(self): + def __neg__(self: object) -> InfinityType: return Infinity -NegativeInfinity = NegativeInfinity() +NegativeInfinity = NegativeInfinityType() diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py index 4bdfdb24f2096eac046bb9a576065bb96cfd476e..eb0541b83a77f09f5e598bf88eeb38a84e305ae0 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py @@ -1,20 +1,26 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import operator import os import platform import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from setuptools.extern.pyparsing import ( # noqa: N817 + Forward, + Group, + Literal as L, + ParseException, + ParseResults, + QuotedString, + ZeroOrMore, + stringEnd, + stringStart, +) -from setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd -from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString -from setuptools.extern.pyparsing import Literal as L # noqa - -from ._compat import string_types -from .specifiers import Specifier, InvalidSpecifier - +from .specifiers import InvalidSpecifier, Specifier __all__ = [ "InvalidMarker", @@ -24,6 +30,8 @@ __all__ = [ "default_environment", ] +Operator = Callable[[str, str], bool] + class InvalidMarker(ValueError): """ @@ -44,32 +52,32 @@ class UndefinedEnvironmentName(ValueError): """ -class Node(object): - def __init__(self, value): +class Node: + def __init__(self, value: Any) -> None: self.value = value - def __str__(self): + def __str__(self) -> str: return str(self.value) - def __repr__(self): - return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" - def serialize(self): + def serialize(self) -> str: raise NotImplementedError class Variable(Node): - def serialize(self): + def serialize(self) -> str: return str(self) class Value(Node): - def serialize(self): - return '"{0}"'.format(self) + def serialize(self) -> str: + return f'"{self}"' class Op(Node): - def serialize(self): + def serialize(self) -> str: return str(self) @@ -85,13 +93,13 @@ VARIABLE = ( | L("python_version") | L("sys_platform") | L("os_name") - | L("os.name") + | L("os.name") # PEP-345 | L("sys.platform") # PEP-345 | L("platform.version") # PEP-345 | L("platform.machine") # PEP-345 | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # PEP-345 - | L("extra") # undocumented setuptools legacy + | L("python_implementation") # undocumented setuptools legacy + | L("extra") # PEP-508 ) ALIASES = { "os.name": "os_name", @@ -130,15 +138,18 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) MARKER = stringStart + MARKER_EXPR + stringEnd -def _coerce_parse_result(results): +def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: if isinstance(results, ParseResults): return [_coerce_parse_result(i) for i in results] else: return results -def _format_marker(marker, first=True): - assert isinstance(marker, (list, tuple, string_types)) +def _format_marker( + marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) # Sometimes we have a structure like [[...]] which is a single item list # where the single item is itself it's own list. In that case we want skip @@ -163,7 +174,7 @@ def _format_marker(marker, first=True): return marker -_operators = { +_operators: Dict[str, Operator] = { "in": lambda lhs, rhs: lhs in rhs, "not in": lambda lhs, rhs: lhs not in rhs, "<": operator.lt, @@ -175,7 +186,7 @@ _operators = { } -def _eval_op(lhs, op, rhs): +def _eval_op(lhs: str, op: Op, rhs: str) -> bool: try: spec = Specifier("".join([op.serialize(), rhs])) except InvalidSpecifier: @@ -183,34 +194,36 @@ def _eval_op(lhs, op, rhs): else: return spec.contains(lhs) - oper = _operators.get(op.serialize()) + oper: Optional[Operator] = _operators.get(op.serialize()) if oper is None: - raise UndefinedComparison( - "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) - ) + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") return oper(lhs, rhs) -_undefined = object() +class Undefined: + pass + + +_undefined = Undefined() -def _get_env(environment, name): - value = environment.get(name, _undefined) +def _get_env(environment: Dict[str, str], name: str) -> str: + value: Union[str, Undefined] = environment.get(name, _undefined) - if value is _undefined: + if isinstance(value, Undefined): raise UndefinedEnvironmentName( - "{0!r} does not exist in evaluation environment.".format(name) + f"{name!r} does not exist in evaluation environment." ) return value -def _evaluate_markers(markers, environment): - groups = [[]] +def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] for marker in markers: - assert isinstance(marker, (list, tuple, string_types)) + assert isinstance(marker, (list, tuple, str)) if isinstance(marker, list): groups[-1].append(_evaluate_markers(marker, environment)) @@ -233,7 +246,7 @@ def _evaluate_markers(markers, environment): return any(all(item) for item in groups) -def format_full_version(info): +def format_full_version(info: "sys._version_info") -> str: version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel if kind != "final": @@ -241,14 +254,9 @@ def format_full_version(info): return version -def default_environment(): - if hasattr(sys, "implementation"): - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name - else: - iver = "0" - implementation_name = "" - +def default_environment() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name return { "implementation_name": implementation_name, "implementation_version": iver, @@ -264,23 +272,23 @@ def default_environment(): } -class Marker(object): - def __init__(self, marker): +class Marker: + def __init__(self, marker: str) -> None: try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: - err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc : e.loc + 8] + raise InvalidMarker( + f"Invalid marker: {marker!r}, parse error at " + f"{marker[e.loc : e.loc + 8]!r}" ) - raise InvalidMarker(err_str) - def __str__(self): + def __str__(self) -> str: return _format_marker(self._markers) - def __repr__(self): - return "".format(str(self)) + def __repr__(self) -> str: + return f"" - def evaluate(self, environment=None): + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: """Evaluate a marker. Return the boolean from evaluating the given marker against the diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py index 8a0c2cb9be06e633b26c7205d6efe42827835910..0d93231b4613b27acd2bf7c1283d4ae99d595bdc 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py @@ -1,15 +1,24 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function -import string import re - -from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException -from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine -from setuptools.extern.pyparsing import Literal as L # noqa -from setuptools.extern.six.moves.urllib import parse as urlparse +import string +import urllib.parse +from typing import List, Optional as TOptional, Set + +from setuptools.extern.pyparsing import ( # noqa + Combine, + Literal as L, + Optional, + ParseException, + Regex, + Word, + ZeroOrMore, + originalTextFor, + stringEnd, + stringStart, +) from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet @@ -51,7 +60,7 @@ VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY VERSION_MANY = Combine( VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False )("_raw_spec") -_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) _VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") @@ -75,7 +84,7 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd REQUIREMENT.parseString("x[]") -class Requirement(object): +class Requirement: """Parse a requirement. Parse a given requirement string into its parts, such as name, specifier, @@ -88,51 +97,50 @@ class Requirement(object): # the thing as well as the version? What about the markers? # TODO: Can we normalize the name and extra name? - def __init__(self, requirement_string): + def __init__(self, requirement_string: str) -> None: try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( - 'Parse error at "{0!r}": {1}'.format( - requirement_string[e.loc : e.loc + 8], e.msg - ) + f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' ) - self.name = req.name + self.name: str = req.name if req.url: - parsed_url = urlparse.urlparse(req.url) + parsed_url = urllib.parse.urlparse(req.url) if parsed_url.scheme == "file": - if urlparse.urlunparse(parsed_url) != req.url: + if urllib.parse.urlunparse(parsed_url) != req.url: raise InvalidRequirement("Invalid URL given") elif not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): - raise InvalidRequirement("Invalid URL: {0}".format(req.url)) - self.url = req.url + raise InvalidRequirement(f"Invalid URL: {req.url}") + self.url: TOptional[str] = req.url else: self.url = None - self.extras = set(req.extras.asList() if req.extras else []) - self.specifier = SpecifierSet(req.specifier) - self.marker = req.marker if req.marker else None + self.extras: Set[str] = set(req.extras.asList() if req.extras else []) + self.specifier: SpecifierSet = SpecifierSet(req.specifier) + self.marker: TOptional[Marker] = req.marker if req.marker else None - def __str__(self): - parts = [self.name] + def __str__(self) -> str: + parts: List[str] = [self.name] if self.extras: - parts.append("[{0}]".format(",".join(sorted(self.extras)))) + formatted_extras = ",".join(sorted(self.extras)) + parts.append(f"[{formatted_extras}]") if self.specifier: parts.append(str(self.specifier)) if self.url: - parts.append("@ {0}".format(self.url)) + parts.append(f"@ {self.url}") if self.marker: parts.append(" ") if self.marker: - parts.append("; {0}".format(self.marker)) + parts.append(f"; {self.marker}") return "".join(parts) - def __repr__(self): - return "".format(str(self)) + def __repr__(self) -> str: + return f"" diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py index 743576a080a0af8d0995f307ea6afc645b13ca61..ce66bd4addbde1e332e9a42f6eb62adc471193e5 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py @@ -1,15 +1,33 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import abc import functools import itertools import re - -from ._compat import string_types, with_metaclass -from .version import Version, LegacyVersion, parse +import warnings +from typing import ( + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Pattern, + Set, + Tuple, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import LegacyVersion, Version, parse + +ParsedVersion = Union[Version, LegacyVersion] +UnparsedVersion = Union[Version, LegacyVersion, str] +VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) +CallableOperator = Callable[[ParsedVersion, str], bool] class InvalidSpecifier(ValueError): @@ -18,56 +36,58 @@ class InvalidSpecifier(ValueError): """ -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): +class BaseSpecifier(metaclass=abc.ABCMeta): @abc.abstractmethod - def __str__(self): + def __str__(self) -> str: """ Returns the str representation of this Specifier like object. This should be representative of the Specifier itself. """ @abc.abstractmethod - def __hash__(self): + def __hash__(self) -> int: """ Returns a hash value for this Specifier like object. """ @abc.abstractmethod - def __eq__(self, other): + def __eq__(self, other: object) -> bool: """ Returns a boolean representing whether or not the two Specifier like objects are equal. """ @abc.abstractmethod - def __ne__(self, other): + def __ne__(self, other: object) -> bool: """ Returns a boolean representing whether or not the two Specifier like objects are not equal. """ @abc.abstractproperty - def prereleases(self): + def prereleases(self) -> Optional[bool]: """ Returns whether or not pre-releases as a whole are allowed by this specifier. """ @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: """ Sets whether or not pre-releases as a whole are allowed by this specifier. """ @abc.abstractmethod - def contains(self, item, prereleases=None): + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod - def filter(self, iterable, prereleases=None): + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. @@ -76,48 +96,56 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): class _IndividualSpecifier(BaseSpecifier): - _operators = {} + _operators: Dict[str, str] = {} + _regex: Pattern[str] - def __init__(self, spec="", prereleases=None): + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: match = self._regex.search(spec) if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - self._spec = (match.group("operator").strip(), match.group("version").strip()) + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases - def __repr__(self): + def __repr__(self) -> str: pre = ( - ", prereleases={0!r}".format(self.prereleases) + f", prereleases={self.prereleases!r}" if self._prereleases is not None else "" ) - return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) + return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre) - def __str__(self): - return "{0}{1}".format(*self._spec) + def __str__(self) -> str: + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + return self._spec[0], canonicalize_version(self._spec[1]) - def __hash__(self): - return hash(self._spec) + def __hash__(self) -> int: + return hash(self._canonical_spec) - def __eq__(self, other): - if isinstance(other, string_types): + def __eq__(self, other: object) -> bool: + if isinstance(other, str): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): return NotImplemented - return self._spec == other._spec + return self._canonical_spec == other._canonical_spec - def __ne__(self, other): - if isinstance(other, string_types): + def __ne__(self, other: object) -> bool: + if isinstance(other, str): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): @@ -125,53 +153,63 @@ class _IndividualSpecifier(BaseSpecifier): return self._spec != other._spec - def _get_operator(self, op): - return getattr(self, "_compare_{0}".format(self._operators[op])) + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable - def _coerce_version(self, version): + def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: if not isinstance(version, (LegacyVersion, Version)): version = parse(version) return version @property - def operator(self): + def operator(self) -> str: return self._spec[0] @property - def version(self): + def version(self) -> str: return self._spec[1] @property - def prereleases(self): + def prereleases(self) -> Optional[bool]: return self._prereleases @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: self._prereleases = value - def __contains__(self, item): + def __contains__(self, item: str) -> bool: return self.contains(item) - def contains(self, item, prereleases=None): + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + # Determine if prereleases are to be allowed or not. if prereleases is None: prereleases = self.prereleases # Normalize item to a Version or LegacyVersion, this allows us to have # a shortcut for ``"2.0" in Specifier(">=2") - item = self._coerce_version(item) + normalized_item = self._coerce_version(item) # Determine if we should be supporting prereleases in this specifier # or not, if we do not support prereleases than we can short circuit # logic if this version is a prereleases. - if item.is_prerelease and not prereleases: + if normalized_item.is_prerelease and not prereleases: return False # Actually do the comparison to determine if this item is contained # within this Specifier or not. - return self._get_operator(self.operator)(item, self.version) + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: - def filter(self, iterable, prereleases=None): yielded = False found_prereleases = [] @@ -184,7 +222,7 @@ class _IndividualSpecifier(BaseSpecifier): if self.contains(parsed_version, **kw): # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later incase nothing + # prereleases, then we'll store it for later in case nothing # else matches this specifier. if parsed_version.is_prerelease and not ( prereleases or self.prereleases @@ -229,33 +267,46 @@ class LegacySpecifier(_IndividualSpecifier): ">": "greater_than", } - def _coerce_version(self, version): + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + super().__init__(spec, prereleases) + + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: if not isinstance(version, LegacyVersion): version = LegacyVersion(str(version)) return version - def _compare_equal(self, prospective, spec): + def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective == self._coerce_version(spec) - def _compare_not_equal(self, prospective, spec): + def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective != self._coerce_version(spec) - def _compare_less_than_equal(self, prospective, spec): + def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: return prospective <= self._coerce_version(spec) - def _compare_greater_than_equal(self, prospective, spec): + def _compare_greater_than_equal( + self, prospective: LegacyVersion, spec: str + ) -> bool: return prospective >= self._coerce_version(spec) - def _compare_less_than(self, prospective, spec): + def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: return prospective < self._coerce_version(spec) - def _compare_greater_than(self, prospective, spec): + def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: return prospective > self._coerce_version(spec) -def _require_version_compare(fn): +def _require_version_compare( + fn: Callable[["Specifier", ParsedVersion, str], bool] +) -> Callable[["Specifier", ParsedVersion, str], bool]: @functools.wraps(fn) - def wrapped(self, prospective, spec): + def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: if not isinstance(prospective, Version): return False return fn(self, prospective, spec) @@ -372,7 +423,8 @@ class Specifier(_IndividualSpecifier): } @_require_version_compare - def _compare_compatible(self, prospective, spec): + def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: + # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to # implement this in terms of the other specifiers instead of @@ -380,15 +432,9 @@ class Specifier(_IndividualSpecifier): # the other specifiers. # We want everything but the last item in the version, but we want to - # ignore post and dev releases and we want to treat the pre-release as - # it's own separate segment. + # ignore suffix segments. prefix = ".".join( - list( - itertools.takewhile( - lambda x: (not x.startswith("post") and not x.startswith("dev")), - _version_split(spec), - ) - )[:-1] + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] ) # Add the prefix notation to the end of our string @@ -399,57 +445,73 @@ class Specifier(_IndividualSpecifier): ) @_require_version_compare - def _compare_equal(self, prospective, spec): + def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: + # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. prospective = Version(prospective.public) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. - spec = _version_split(spec[:-2]) # Remove the trailing .* + split_spec = _version_split(spec[:-2]) # Remove the trailing .* # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. - prospective = _version_split(str(prospective)) + split_prospective = _version_split(str(prospective)) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - prospective = prospective[: len(spec)] + shortened_prospective = split_prospective[: len(split_spec)] # Pad out our two sides with zeros so that they both equal the same # length. - spec, prospective = _pad_version(spec, prospective) + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec else: # Convert our spec string into a Version - spec = Version(spec) + spec_version = Version(spec) # If the specifier does not have a local segment, then we want to # act as if the prospective version also does not have a local # segment. - if not spec.local: + if not spec_version.local: prospective = Version(prospective.public) - return prospective == spec + return prospective == spec_version @_require_version_compare - def _compare_not_equal(self, prospective, spec): + def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: return not self._compare_equal(prospective, spec) @_require_version_compare - def _compare_less_than_equal(self, prospective, spec): - return prospective <= Version(spec) + def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) @_require_version_compare - def _compare_greater_than_equal(self, prospective, spec): - return prospective >= Version(spec) + def _compare_greater_than_equal( + self, prospective: ParsedVersion, spec: str + ) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) @_require_version_compare - def _compare_less_than(self, prospective, spec): + def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is less than the spec # version. If it's not we can short circuit and just return False now @@ -471,10 +533,11 @@ class Specifier(_IndividualSpecifier): return True @_require_version_compare - def _compare_greater_than(self, prospective, spec): + def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is greater than the spec # version. If it's not we can short circuit and just return False now @@ -501,11 +564,12 @@ class Specifier(_IndividualSpecifier): # same version in the spec. return True - def _compare_arbitrary(self, prospective, spec): + def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: return str(prospective).lower() == str(spec).lower() @property - def prereleases(self): + def prereleases(self) -> bool: + # If there is an explicit prereleases set for this, then we'll just # blindly use that. if self._prereleases is not None: @@ -529,15 +593,15 @@ class Specifier(_IndividualSpecifier): return False @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: self._prereleases = value _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") -def _version_split(version): - result = [] +def _version_split(version: str) -> List[str]: + result: List[str] = [] for item in version.split("."): match = _prefix_regex.search(item) if match: @@ -547,7 +611,13 @@ def _version_split(version): return result -def _pad_version(left, right): +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: left_split, right_split = [], [] # Get the release segment of our versions @@ -566,15 +636,18 @@ def _pad_version(left, right): class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): - # Split on , to break each indidivual specifier into it's own item, and + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + + # Split on , to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a # Specifier and falling back to a LegacySpecifier. - parsed = set() - for specifier in specifiers: + parsed: Set[_IndividualSpecifier] = set() + for specifier in split_specifiers: try: parsed.add(Specifier(specifier)) except InvalidSpecifier: @@ -587,23 +660,23 @@ class SpecifierSet(BaseSpecifier): # we accept prereleases or not. self._prereleases = prereleases - def __repr__(self): + def __repr__(self) -> str: pre = ( - ", prereleases={0!r}".format(self.prereleases) + f", prereleases={self.prereleases!r}" if self._prereleases is not None else "" ) - return "".format(str(self), pre) + return "".format(str(self), pre) - def __str__(self): + def __str__(self) -> str: return ",".join(sorted(str(s) for s in self._specs)) - def __hash__(self): + def __hash__(self) -> int: return hash(self._specs) - def __and__(self, other): - if isinstance(other, string_types): + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + if isinstance(other, str): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -625,34 +698,31 @@ class SpecifierSet(BaseSpecifier): return specifier - def __eq__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + def __eq__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs == other._specs - def __ne__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + def __ne__(self, other: object) -> bool: + if isinstance(other, (str, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs != other._specs - def __len__(self): + def __len__(self) -> int: return len(self._specs) - def __iter__(self): + def __iter__(self) -> Iterator[_IndividualSpecifier]: return iter(self._specs) @property - def prereleases(self): + def prereleases(self) -> Optional[bool]: + # If we have been given an explicit prerelease modifier, then we'll # pass that through here. if self._prereleases is not None: @@ -669,13 +739,16 @@ class SpecifierSet(BaseSpecifier): return any(s.prereleases for s in self._specs) @prereleases.setter - def prereleases(self, value): + def prereleases(self, value: bool) -> None: self._prereleases = value - def __contains__(self, item): + def __contains__(self, item: UnparsedVersion) -> bool: return self.contains(item) - def contains(self, item, prereleases=None): + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + # Ensure that our item is a Version or LegacyVersion instance. if not isinstance(item, (LegacyVersion, Version)): item = parse(item) @@ -701,7 +774,10 @@ class SpecifierSet(BaseSpecifier): # will always return True, this is an explicit design decision. return all(s.contains(item, prereleases=prereleases) for s in self._specs) - def filter(self, iterable, prereleases=None): + def filter( + self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None + ) -> Iterable[VersionTypeVar]: + # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. @@ -719,8 +795,11 @@ class SpecifierSet(BaseSpecifier): # which will filter out any pre-releases, unless there are no final # releases, and which will filter out LegacyVersion in general. else: - filtered = [] - found_prereleases = [] + filtered: List[VersionTypeVar] = [] + found_prereleases: List[VersionTypeVar] = [] + + item: UnparsedVersion + parsed_version: Union[Version, LegacyVersion] for item in iterable: # Ensure that we some kind of Version class for this item. diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py index ec9942f0f6627f34554082a8c0909bc70bd2a260..e65890a90cd709489865750e953bf347720c75cd 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py @@ -2,25 +2,32 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import - -import distutils.util - -try: - from importlib.machinery import EXTENSION_SUFFIXES -except ImportError: # pragma: no cover - import imp - - EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] - del imp +import logging import platform -import re import sys import sysconfig -import warnings - - -INTERPRETER_SHORT_NAMES = { +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { "python": "py", # Generic. "cpython": "cp", "pypy": "pp", @@ -32,45 +39,67 @@ INTERPRETER_SHORT_NAMES = { _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 -class Tag(object): +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ - __slots__ = ["_interpreter", "_abi", "_platform"] + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] - def __init__(self, interpreter, abi, platform): + def __init__(self, interpreter: str, abi: str, platform: str) -> None: self._interpreter = interpreter.lower() self._abi = abi.lower() self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) @property - def interpreter(self): + def interpreter(self) -> str: return self._interpreter @property - def abi(self): + def abi(self) -> str: return self._abi @property - def platform(self): + def platform(self) -> str: return self._platform - def __eq__(self, other): + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + return ( - (self.platform == other.platform) - and (self.abi == other.abi) - and (self.interpreter == other.interpreter) + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) ) - def __hash__(self): - return hash((self._interpreter, self._abi, self._platform)) + def __hash__(self) -> int: + return self._hash - def __str__(self): - return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" - def __repr__(self): + def __repr__(self) -> str: return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) -def parse_tag(tag): +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ tags = set() interpreters, abis, platforms = tag.split("-") for interpreter in interpreters.split("."): @@ -80,20 +109,34 @@ def parse_tag(tag): return frozenset(tags) -def _normalize_string(string): +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string: str) -> str: return string.replace(".", "_").replace("-", "_") -def _cpython_interpreter(py_version): - # TODO: Is using py_version_nodot for interpreter version critical? - return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) +def _abi3_applies(python_version: PythonVersion) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version): +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: + py_version = tuple(py_version) # To allow for version comparison. abis = [] - version = "{}{}".format(*py_version[:2]) + version = _version_nodot(py_version[:2]) debug = pymalloc = ucs4 = "" - with_debug = sysconfig.get_config_var("Py_DEBUG") + with_debug = _get_config_var("Py_DEBUG", warn) has_refcount = hasattr(sys, "gettotalrefcount") # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled # extension modules is the best option. @@ -102,11 +145,11 @@ def _cpython_abis(py_version): if with_debug or (with_debug is None and (has_refcount or has_ext)): debug = "d" if py_version < (3, 8): - with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) if with_pymalloc or with_pymalloc is None: pymalloc = "m" if py_version < (3, 3): - unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) if unicode_size == 4 or ( unicode_size is None and sys.maxunicode == 0x10FFFF ): @@ -114,7 +157,7 @@ def _cpython_abis(py_version): elif debug: # Debug builds can also load "normal" extension modules. # We can also assume no UCS-4 or pymalloc requirement. - abis.append("cp{version}".format(version=version)) + abis.append(f"cp{version}") abis.insert( 0, "cp{version}{debug}{pymalloc}{ucs4}".format( @@ -124,86 +167,140 @@ def _cpython_abis(py_version): return abis -def _cpython_tags(py_version, interpreter, abis, platforms): +def cpython_tags( + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = "cp{}".format(_version_nodot(python_version[:2])) + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or platform_tags()) for abi in abis: for platform_ in platforms: yield Tag(interpreter, abi, platform_) - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): - yield tag - # PEP 384 was first implemented in Python 3.2. - for minor_version in range(py_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{major}{minor}".format( - major=py_version[0], minor=minor_version - ) - yield Tag(interpreter, "abi3", platform_) - - -def _pypy_interpreter(): - return "pp{py_major}{pypy_major}{pypy_minor}".format( - py_major=sys.version_info[0], - pypy_major=sys.pypy_version_info.major, - pypy_minor=sys.pypy_version_info.minor, - ) + if _abi3_applies(python_version): + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) -def _generic_abi(): +def _generic_abi() -> Iterator[str]: abi = sysconfig.get_config_var("SOABI") if abi: - return _normalize_string(abi) - else: - return "none" + yield _normalize_string(abi) -def _pypy_tags(py_version, interpreter, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform) for platform in platforms): - yield tag +def generic_tags( + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a generic interpreter. + The tags consist of: + - -- -def _generic_tags(interpreter, py_version, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - if abi != "none": - tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) - for tag in tags: - yield tag + The "none" ABI will be added if it was not explicitly provided. + """ + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) -def _py_interpreter_range(py_version): +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: """ - Yield Python versions in descending order. + Yields Python versions in descending order. After the latest version, the major-only version will be yielded, and then - all following versions up to 'end'. + all previous versions of that major version. """ - yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + if len(py_version) > 1: + yield "py{version}".format(version=_version_nodot(py_version[:2])) yield "py{major}".format(major=py_version[0]) - for minor in range(py_version[1] - 1, -1, -1): - yield "py{major}{minor}".format(major=py_version[0], minor=minor) + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield "py{version}".format(version=_version_nodot((py_version[0], minor))) -def _independent_tags(interpreter, py_version, platforms): +def compatible_tags( + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> Iterator[Tag]: """ - Return the sequence of tags that are consistent across implementations. + Yields the sequence of tags that are compatible with a specific version of Python. The tags consist of: - py*-none- - - -none-any + - -none-any # ... if `interpreter` is provided. - py*-none-any """ - for version in _py_interpreter_range(py_version): + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or platform_tags()) + for version in _py_interpreter_range(python_version): for platform_ in platforms: yield Tag(version, "none", platform_) - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(py_version): + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): yield Tag(version, "none", "any") -def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: if not is_32bit: return arch @@ -213,7 +310,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): return "i386" -def _mac_binary_formats(version, cpu_arch): +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -236,169 +333,152 @@ def _mac_binary_formats(version, cpu_arch): return [] formats.extend(["fat32", "fat"]) - formats.append("universal") + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + return formats -def _mac_platforms(version=None, arch=None): +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> Iterator[str]: + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ version_str, _, cpu_arch = platform.mac_ver() if version is None: - version = tuple(map(int, version_str.split(".")[:2])) + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version if arch is None: arch = _mac_arch(cpu_arch) - platforms = [] - for minor_version in range(version[1], -1, -1): - compat_version = version[0], minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - platforms.append( - "macosx_{major}_{minor}_{binary_format}".format( + else: + arch = arch + + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( major=compat_version[0], minor=compat_version[1], binary_format=binary_format, ) - ) - return platforms - - -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # Check for presence of _manylinux module. - try: - import _manylinux - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. - pass - - return _have_compatible_glibc(*glibc_version) - - -def _glibc_version_string(): - # Returns glibc version string, or None if not using glibc. - import ctypes - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - process_namespace = ctypes.CDLL(None) - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): - # Parse string and check against requested version. - # - # We use a regexp instead of str.split because we want to discard any - # random junk that might come after the minor version -- this might happen - # in patched/forked versions of glibc (e.g. Linaro's version of glibc - # uses version strings like "2.20-2014.11"). See gh-3588. - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) -def _have_compatible_glibc(required_major, minimum_minor): - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, required_major, minimum_minor) +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + _, arch = linux.split("_", 1) + yield from _manylinux.platform_tags(linux, arch) + yield from _musllinux.platform_tags(arch) + yield linux -def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): - linux = _normalize_string(distutils.util.get_platform()) - if linux == "linux_x86_64" and is_32bit: - linux = "linux_i686" - manylinux_support = ( - ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) - ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) - ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) - ) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - platforms = [linux.replace("linux", name)] - break - else: - platforms = [] - # Support for a later manylinux implies support for an earlier version. - platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] - platforms.append(linux) - return platforms +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) -def _generic_platforms(): - platform = _normalize_string(distutils.util.get_platform()) - return [platform] +def platform_tags() -> Iterator[str]: + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() -def _interpreter_name(): - name = platform.python_implementation().lower() +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + """ + name = sys.implementation.name return INTERPRETER_SHORT_NAMES.get(name) or name -def _generic_interpreter(name, py_version): - version = sysconfig.get_config_var("py_version_nodot") - if not version: - version = "".join(map(str, py_version[:2])) - return "{name}{version}".format(name=name, version=version) +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the version of the running interpreter. + """ + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) -def sys_tags(): + +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: """ Returns the sequence of tag triples for the running interpreter. The order of the sequence corresponds to priority order for the interpreter, from most to least important. """ - py_version = sys.version_info[:2] - interpreter_name = _interpreter_name() - if platform.system() == "Darwin": - platforms = _mac_platforms() - elif platform.system() == "Linux": - platforms = _linux_platforms() - else: - platforms = _generic_platforms() - - if interpreter_name == "cp": - interpreter = _cpython_interpreter(py_version) - abis = _cpython_abis(py_version) - for tag in _cpython_tags(py_version, interpreter, abis, platforms): - yield tag - elif interpreter_name == "pp": - interpreter = _pypy_interpreter() - abi = _generic_abi() - for tag in _pypy_tags(py_version, interpreter, abi, platforms): - yield tag + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) else: - interpreter = _generic_interpreter(interpreter_name, py_version) - abi = _generic_abi() - for tag in _generic_tags(interpreter, py_version, abi, platforms): - yield tag - for tag in _independent_tags(interpreter, py_version, platforms): - yield tag + yield from generic_tags() + + yield from compatible_tags() diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py index 88418786933b8bc5f6179b8e191f60f79efd7074..bab11b80c60f10a4f3bccb12eb5b17c48a449767 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py @@ -1,57 +1,136 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import re +from typing import FrozenSet, NewType, Tuple, Union, cast +from .tags import Tag, parse_tag from .version import InvalidVersion, Version +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + _canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") -def canonicalize_name(name): +def canonicalize_name(name: str) -> NormalizedName: # This is taken from PEP 503. - return _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) -def canonicalize_version(version): +def canonicalize_version(version: Union[Version, str]) -> str: """ - This is very similar to Version.__str__, but has one subtle differences + This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. """ - - try: - version = Version(version) - except InvalidVersion: - # Legacy versions cannot be normalized - return version + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version parts = [] # Epoch - if version.epoch != 0: - parts.append("{0}!".format(version.epoch)) + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") # Release segment # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) # Pre-release - if version.pre is not None: - parts.append("".join(str(x) for x in version.pre)) + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) # Post-release - if version.post is not None: - parts.append(".post{0}".format(version.post)) + if parsed.post is not None: + parts.append(f".post{parsed.post}") # Development release - if version.dev is not None: - parts.append(".dev{0}".format(version.dev)) + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") # Local version segment - if version.local is not None: - parts.append("+{0}".format(version.local)) + if parsed.local is not None: + parts.append(f"+{parsed.local}") return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py index 95157a1f78c26829ffbe1bd2463f7735b636d16f..de9a09a4ed3b078b37e7490a6686f660ae935aca 100644 --- a/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py @@ -1,24 +1,45 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import absolute_import, division, print_function import collections import itertools import re +import warnings +from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union -from ._structures import Infinity - +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType __all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] +InfiniteTypes = Union[InfinityType, NegativeInfinityType] +PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] +SubLocalType = Union[InfiniteTypes, int, str] +LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], +] +CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType +] +LegacyCmpKey = Tuple[int, Tuple[str, ...]] +VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool +] _Version = collections.namedtuple( "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) -def parse(version): +def parse(version: str) -> Union["LegacyVersion", "Version"]: """ Parse the given version string and return either a :class:`Version` object or a :class:`LegacyVersion` object depending on if the given version is @@ -36,88 +57,111 @@ class InvalidVersion(ValueError): """ -class _BaseVersion(object): - def __hash__(self): +class _BaseVersion: + _key: Union[CmpKey, LegacyCmpKey] + + def __hash__(self) -> int: return hash(self._key) - def __lt__(self, other): - return self._compare(other, lambda s, o: s < o) + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key - def __le__(self, other): - return self._compare(other, lambda s, o: s <= o) + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented - def __eq__(self, other): - return self._compare(other, lambda s, o: s == o) + return self._key == other._key - def __ge__(self, other): - return self._compare(other, lambda s, o: s >= o) + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented - def __gt__(self, other): - return self._compare(other, lambda s, o: s > o) + return self._key >= other._key - def __ne__(self, other): - return self._compare(other, lambda s, o: s != o) + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key - def _compare(self, other, method): + def __ne__(self, other: object) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented - return method(self._key, other._key) + return self._key != other._key class LegacyVersion(_BaseVersion): - def __init__(self, version): + def __init__(self, version: str) -> None: self._version = str(version) self._key = _legacy_cmpkey(self._version) - def __str__(self): + warnings.warn( + "Creating a LegacyVersion has been deprecated and will be " + "removed in the next major release", + DeprecationWarning, + ) + + def __str__(self) -> str: return self._version - def __repr__(self): - return "".format(repr(str(self))) + def __repr__(self) -> str: + return f"" @property - def public(self): + def public(self) -> str: return self._version @property - def base_version(self): + def base_version(self) -> str: return self._version @property - def epoch(self): + def epoch(self) -> int: return -1 @property - def release(self): + def release(self) -> None: return None @property - def pre(self): + def pre(self) -> None: return None @property - def post(self): + def post(self) -> None: return None @property - def dev(self): + def dev(self) -> None: return None @property - def local(self): + def local(self) -> None: return None @property - def is_prerelease(self): + def is_prerelease(self) -> bool: return False @property - def is_postrelease(self): + def is_postrelease(self) -> bool: return False @property - def is_devrelease(self): + def is_devrelease(self) -> bool: return False @@ -132,7 +176,7 @@ _legacy_version_replacement_map = { } -def _parse_version_parts(s): +def _parse_version_parts(s: str) -> Iterator[str]: for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -149,7 +193,8 @@ def _parse_version_parts(s): yield "*final" -def _legacy_cmpkey(version): +def _legacy_cmpkey(version: str) -> LegacyCmpKey: + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, @@ -158,7 +203,7 @@ def _legacy_cmpkey(version): # This scheme is taken from pkg_resources.parse_version setuptools prior to # it's adoption of the packaging library. - parts = [] + parts: List[str] = [] for part in _parse_version_parts(version.lower()): if part.startswith("*"): # remove "-" before a prerelease tag @@ -171,9 +216,8 @@ def _legacy_cmpkey(version): parts.pop() parts.append(part) - parts = tuple(parts) - return epoch, parts + return epoch, tuple(parts) # Deliberately not anchored to the start and end of the string, to make it @@ -214,11 +258,12 @@ class Version(_BaseVersion): _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) - def __init__(self, version): + def __init__(self, version: str) -> None: + # Validate the version and parse it into pieces match = self._regex.search(version) if not match: - raise InvalidVersion("Invalid version: '{0}'".format(version)) + raise InvalidVersion(f"Invalid version: '{version}'") # Store the parsed out pieces of the version self._version = _Version( @@ -242,15 +287,15 @@ class Version(_BaseVersion): self._version.local, ) - def __repr__(self): - return "".format(repr(str(self))) + def __repr__(self) -> str: + return f"" - def __str__(self): + def __str__(self) -> str: parts = [] # Epoch if self.epoch != 0: - parts.append("{0}!".format(self.epoch)) + parts.append(f"{self.epoch}!") # Release segment parts.append(".".join(str(x) for x in self.release)) @@ -261,56 +306,59 @@ class Version(_BaseVersion): # Post-release if self.post is not None: - parts.append(".post{0}".format(self.post)) + parts.append(f".post{self.post}") # Development release if self.dev is not None: - parts.append(".dev{0}".format(self.dev)) + parts.append(f".dev{self.dev}") # Local version segment if self.local is not None: - parts.append("+{0}".format(self.local)) + parts.append(f"+{self.local}") return "".join(parts) @property - def epoch(self): - return self._version.epoch + def epoch(self) -> int: + _epoch: int = self._version.epoch + return _epoch @property - def release(self): - return self._version.release + def release(self) -> Tuple[int, ...]: + _release: Tuple[int, ...] = self._version.release + return _release @property - def pre(self): - return self._version.pre + def pre(self) -> Optional[Tuple[str, int]]: + _pre: Optional[Tuple[str, int]] = self._version.pre + return _pre @property - def post(self): + def post(self) -> Optional[int]: return self._version.post[1] if self._version.post else None @property - def dev(self): + def dev(self) -> Optional[int]: return self._version.dev[1] if self._version.dev else None @property - def local(self): + def local(self) -> Optional[str]: if self._version.local: return ".".join(str(x) for x in self._version.local) else: return None @property - def public(self): + def public(self) -> str: return str(self).split("+", 1)[0] @property - def base_version(self): + def base_version(self) -> str: parts = [] # Epoch if self.epoch != 0: - parts.append("{0}!".format(self.epoch)) + parts.append(f"{self.epoch}!") # Release segment parts.append(".".join(str(x) for x in self.release)) @@ -318,19 +366,34 @@ class Version(_BaseVersion): return "".join(parts) @property - def is_prerelease(self): + def is_prerelease(self) -> bool: return self.dev is not None or self.pre is not None @property - def is_postrelease(self): + def is_postrelease(self) -> bool: return self.post is not None @property - def is_devrelease(self): + def is_devrelease(self) -> bool: return self.dev is not None + @property + def major(self) -> int: + return self.release[0] if len(self.release) >= 1 else 0 + + @property + def minor(self) -> int: + return self.release[1] if len(self.release) >= 2 else 0 + + @property + def micro(self) -> int: + return self.release[2] if len(self.release) >= 3 else 0 + + +def _parse_letter_version( + letter: str, number: Union[str, bytes, SupportsInt] +) -> Optional[Tuple[str, int]]: -def _parse_letter_version(letter, number): if letter: # We consider there to be an implicit 0 in a pre-release if there is # not a numeral associated with it. @@ -360,11 +423,13 @@ def _parse_letter_version(letter, number): return letter, int(number) + return None + _local_version_separators = re.compile(r"[\._-]") -def _parse_local_version(local): +def _parse_local_version(local: str) -> Optional[LocalType]: """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -373,15 +438,24 @@ def _parse_local_version(local): part.lower() if not part.isdigit() else int(part) for part in _local_version_separators.split(local) ) + return None + +def _cmpkey( + epoch: int, + release: Tuple[int, ...], + pre: Optional[Tuple[str, int]], + post: Optional[Tuple[str, int]], + dev: Optional[Tuple[str, int]], + local: Optional[Tuple[SubLocalType]], +) -> CmpKey: -def _cmpkey(epoch, release, pre, post, dev, local): # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now # leading zeros until we come to something non zero, then take the rest # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. - release = tuple( + _release = tuple( reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) ) @@ -390,23 +464,31 @@ def _cmpkey(epoch, release, pre, post, dev, local): # if there is not a pre or a post segment. If we have one of those then # the normal sorting rules will handle this case correctly. if pre is None and post is None and dev is not None: - pre = -Infinity + _pre: PrePostDevType = NegativeInfinity # Versions without a pre-release (except as noted above) should sort after # those with one. elif pre is None: - pre = Infinity + _pre = Infinity + else: + _pre = pre # Versions without a post segment should sort before those with one. if post is None: - post = -Infinity + _post: PrePostDevType = NegativeInfinity + + else: + _post = post # Versions without a development segment should sort after those with one. if dev is None: - dev = Infinity + _dev: PrePostDevType = Infinity + + else: + _dev = dev if local is None: # Versions without a local segment should sort before those with one. - local = -Infinity + _local: LocalType = NegativeInfinity else: # Versions with a local segment need that segment parsed to implement # the sorting rules in PEP440. @@ -415,6 +497,8 @@ def _cmpkey(epoch, release, pre, post, dev, local): # - Numeric segments sort numerically # - Shorter versions sort before longer versions when the prefixes # match exactly - local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local) + _local = tuple( + (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local + ) - return epoch, release, pre, post, dev, local + return epoch, _release, _pre, _post, _dev, _local diff --git a/venv/Lib/site-packages/setuptools/_vendor/six.py b/venv/Lib/site-packages/setuptools/_vendor/six.py deleted file mode 100644 index 190c0239cd7d7af82a6e0cbc8d68053fa2e3dfaf..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/_vendor/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/venv/Lib/site-packages/setuptools/archive_util.py b/venv/Lib/site-packages/setuptools/archive_util.py index 0ce190b8cf7258ef7ac1d9d71d9293038f36d69d..0f70284822f50098e21ad439550cdbd4d298d011 100644 --- a/venv/Lib/site-packages/setuptools/archive_util.py +++ b/venv/Lib/site-packages/setuptools/archive_util.py @@ -125,6 +125,56 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): os.chmod(target, unix_attributes) +def _resolve_tar_file_or_dir(tar_obj, tar_member_obj): + """Resolve any links and extract link targets as normal files.""" + while tar_member_obj is not None and ( + tar_member_obj.islnk() or tar_member_obj.issym()): + linkpath = tar_member_obj.linkname + if tar_member_obj.issym(): + base = posixpath.dirname(tar_member_obj.name) + linkpath = posixpath.join(base, linkpath) + linkpath = posixpath.normpath(linkpath) + tar_member_obj = tar_obj._getmember(linkpath) + + is_file_or_dir = ( + tar_member_obj is not None and + (tar_member_obj.isfile() or tar_member_obj.isdir()) + ) + if is_file_or_dir: + return tar_member_obj + + raise LookupError('Got unknown file type') + + +def _iter_open_tar(tar_obj, extract_dir, progress_filter): + """Emit member-destination pairs from a tar archive.""" + # don't do any chowning! + tar_obj.chown = lambda *args: None + + with contextlib.closing(tar_obj): + for member in tar_obj: + name = member.name + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name.split('/'): + continue + + prelim_dst = os.path.join(extract_dir, *name.split('/')) + + try: + member = _resolve_tar_file_or_dir(tar_obj, member) + except LookupError: + continue + + final_dst = progress_filter(name, prelim_dst) + if not final_dst: + continue + + if final_dst.endswith(os.sep): + final_dst = final_dst[:-1] + + yield member, final_dst + + def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` @@ -138,38 +188,18 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): raise UnrecognizedFormat( "%s is not a compressed or uncompressed tar file" % (filename,) ) from e - with contextlib.closing(tarobj): - # don't do any chowning! - tarobj.chown = lambda *args: None - for member in tarobj: - name = member.name - # don't extract absolute paths or ones with .. in them - if not name.startswith('/') and '..' not in name.split('/'): - prelim_dst = os.path.join(extract_dir, *name.split('/')) - - # resolve any links and to extract the link targets as normal - # files - while member is not None and ( - member.islnk() or member.issym()): - linkpath = member.linkname - if member.issym(): - base = posixpath.dirname(member.name) - linkpath = posixpath.join(base, linkpath) - linkpath = posixpath.normpath(linkpath) - member = tarobj._getmember(linkpath) - - if member is not None and (member.isfile() or member.isdir()): - final_dst = progress_filter(name, prelim_dst) - if final_dst: - if final_dst.endswith(os.sep): - final_dst = final_dst[:-1] - try: - # XXX Ugh - tarobj._extract_member(member, final_dst) - except tarfile.ExtractError: - # chown/chmod/mkfifo/mknode/makedev failed - pass - return True + + for member, final_dst in _iter_open_tar( + tarobj, extract_dir, progress_filter, + ): + try: + # XXX Ugh + tarobj._extract_member(member, final_dst) + except tarfile.ExtractError: + # chown/chmod/mkfifo/mknode/makedev failed + pass + + return True extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/venv/Lib/site-packages/setuptools/build_meta.py b/venv/Lib/site-packages/setuptools/build_meta.py index 46266814ad9cb70246426cb443bb3b0b554665c5..d0ac613ba38c511f63e629096777544cfb8831c2 100644 --- a/venv/Lib/site-packages/setuptools/build_meta.py +++ b/venv/Lib/site-packages/setuptools/build_meta.py @@ -32,10 +32,11 @@ import sys import tokenize import shutil import contextlib +import tempfile +import warnings import setuptools import distutils -from setuptools.py31compat import TemporaryDirectory from pkg_resources import parse_requirements @@ -75,17 +76,20 @@ class Distribution(setuptools.dist.Distribution): distutils.core.Distribution = orig -def _to_str(s): - """ - Convert a filename to a string (on Python 2, explicitly - a byte string, not Unicode) as distutils checks for the - exact type str. +@contextlib.contextmanager +def no_install_setup_requires(): + """Temporarily disable installing setup_requires + + Under PEP 517, the backend reports build dependencies to the frontend, + and the frontend is responsible for ensuring they're installed. + So setuptools (acting as a backend) should not try to install them. """ - if sys.version_info[0] == 2 and not isinstance(s, str): - # Assume it's Unicode, as that's what the PEP says - # should be provided. - return s.encode(sys.getfilesystemencoding()) - return s + orig = setuptools._install_setup_requires + setuptools._install_setup_requires = lambda attrs: None + try: + yield + finally: + setuptools._install_setup_requires = orig def _get_immediate_subdirectories(a_dir): @@ -98,7 +102,12 @@ def _file_with_extension(directory, extension): f for f in os.listdir(directory) if f.endswith(extension) ) - file, = matching + try: + file, = matching + except ValueError: + raise ValueError( + 'No distribution was found. Ensure that `setup.py` ' + 'is not empty and that it calls `setup()`.') return file @@ -110,6 +119,13 @@ def _open_setup_script(setup_script): return getattr(tokenize, 'open', open)(setup_script) +@contextlib.contextmanager +def suppress_known_deprecation(): + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', 'setup.py install is deprecated') + yield + + class _BuildMetaBackend(object): def _fix_config(self, config_settings): @@ -152,9 +168,10 @@ class _BuildMetaBackend(object): def prepare_metadata_for_build_wheel(self, metadata_directory, config_settings=None): - sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', - _to_str(metadata_directory)] - self.run_setup() + sys.argv = sys.argv[:1] + [ + 'dist_info', '--egg-base', metadata_directory] + with no_install_setup_requires(): + self.run_setup() dist_info_directory = metadata_directory while True: @@ -190,11 +207,12 @@ class _BuildMetaBackend(object): # Build in a temporary directory, then copy to the target. os.makedirs(result_directory, exist_ok=True) - with TemporaryDirectory(dir=result_directory) as tmp_dist_dir: + with tempfile.TemporaryDirectory(dir=result_directory) as tmp_dist_dir: sys.argv = (sys.argv[:1] + setup_command + ['--dist-dir', tmp_dist_dir] + config_settings["--global-option"]) - self.run_setup() + with no_install_setup_requires(): + self.run_setup() result_basename = _file_with_extension( tmp_dist_dir, result_extension) @@ -208,8 +226,9 @@ class _BuildMetaBackend(object): def build_wheel(self, wheel_directory, config_settings=None, metadata_directory=None): - return self._build_with_temp_dir(['bdist_wheel'], '.whl', - wheel_directory, config_settings) + with suppress_known_deprecation(): + return self._build_with_temp_dir(['bdist_wheel'], '.whl', + wheel_directory, config_settings) def build_sdist(self, sdist_directory, config_settings=None): return self._build_with_temp_dir(['sdist', '--formats', 'gztar'], diff --git a/venv/Lib/site-packages/setuptools/command/__init__.py b/venv/Lib/site-packages/setuptools/command/__init__.py index 743f5588faf3ad79850df7bd196749e7a6c03f93..b966dcea57a2072f98b96dbba75ceb26bd26d2dd 100644 --- a/venv/Lib/site-packages/setuptools/command/__init__.py +++ b/venv/Lib/site-packages/setuptools/command/__init__.py @@ -1,15 +1,6 @@ -__all__ = [ - 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', - 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', - 'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts', - 'bdist_wininst', 'upload_docs', 'build_clib', 'dist_info', -] - from distutils.command.bdist import bdist import sys -from setuptools.command import install_scripts - if 'egg' not in bdist.format_commands: bdist.format_command['egg'] = ('bdist_egg', "Python .egg file") bdist.format_commands.append('egg') diff --git a/venv/Lib/site-packages/setuptools/command/alias.py b/venv/Lib/site-packages/setuptools/command/alias.py index 4532b1cc0dca76227927e873f9c64f01008e565a..452a9244ea6766d8cf94425fb583583ef740baee 100644 --- a/venv/Lib/site-packages/setuptools/command/alias.py +++ b/venv/Lib/site-packages/setuptools/command/alias.py @@ -1,7 +1,5 @@ from distutils.errors import DistutilsOptionError -from setuptools.extern.six.moves import map - from setuptools.command.setopt import edit_config, option_base, config_file diff --git a/venv/Lib/site-packages/setuptools/command/bdist_egg.py b/venv/Lib/site-packages/setuptools/command/bdist_egg.py index 7af3165cd5633021475dfd80d4c8f366431c5335..e6b1609f7babcd4439376c0d826978f7a66dfa3f 100644 --- a/venv/Lib/site-packages/setuptools/command/bdist_egg.py +++ b/venv/Lib/site-packages/setuptools/command/bdist_egg.py @@ -2,7 +2,6 @@ Build .egg distributions""" -from distutils.errors import DistutilsSetupError from distutils.dir_util import remove_tree, mkpath from distutils import log from types import CodeType @@ -11,26 +10,16 @@ import os import re import textwrap import marshal -import warnings - -from setuptools.extern import six from pkg_resources import get_build_platform, Distribution, ensure_directory -from pkg_resources import EntryPoint from setuptools.extension import Library -from setuptools import Command, SetuptoolsDeprecationWarning +from setuptools import Command -try: - # Python 2.7 or >=3.2 - from sysconfig import get_path, get_python_version +from sysconfig import get_path, get_python_version - def _get_purelib(): - return get_path("purelib") -except ImportError: - from distutils.sysconfig import get_python_lib, get_python_version - def _get_purelib(): - return get_python_lib(False) +def _get_purelib(): + return get_path("purelib") def strip_module(filename): @@ -55,11 +44,12 @@ def write_stub(resource, pyfile): _stub_template = textwrap.dedent(""" def __bootstrap__(): global __bootstrap__, __loader__, __file__ - import sys, pkg_resources - from importlib.machinery import ExtensionFileLoader + import sys, pkg_resources, importlib.util __file__ = pkg_resources.resource_filename(__name__, %r) __loader__ = None; del __bootstrap__, __loader__ - ExtensionFileLoader(__name__,__file__).load_module() + spec = importlib.util.spec_from_file_location(__name__,__file__) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) __bootstrap__() """).lstrip() with open(pyfile, 'w') as f: @@ -160,7 +150,7 @@ class bdist_egg(Command): self.run_command(cmdname) return cmd - def run(self): + def run(self): # noqa: C901 # is too complex (14) # FIXME # Generate metadata first self.run_command("egg_info") # We run install_lib before install_data, because some data hacks @@ -275,49 +265,7 @@ class bdist_egg(Command): return analyze_egg(self.bdist_dir, self.stubs) def gen_header(self): - epm = EntryPoint.parse_map(self.distribution.entry_points or '') - ep = epm.get('setuptools.installation', {}).get('eggsecutable') - if ep is None: - return 'w' # not an eggsecutable, do it the usual way. - - warnings.warn( - "Eggsecutables are deprecated and will be removed in a future " - "version.", - SetuptoolsDeprecationWarning - ) - - if not ep.attrs or ep.extras: - raise DistutilsSetupError( - "eggsecutable entry point (%r) cannot have 'extras' " - "or refer to a module" % (ep,) - ) - - pyver = '{}.{}'.format(*sys.version_info) - pkg = ep.module_name - full = '.'.join(ep.attrs) - base = ep.attrs[0] - basename = os.path.basename(self.egg_output) - - header = ( - "#!/bin/sh\n" - 'if [ `basename $0` = "%(basename)s" ]\n' - 'then exec python%(pyver)s -c "' - "import sys, os; sys.path.insert(0, os.path.abspath('$0')); " - "from %(pkg)s import %(base)s; sys.exit(%(full)s())" - '" "$@"\n' - 'else\n' - ' echo $0 is not the correct name for this egg file.\n' - ' echo Please rename it back to %(basename)s and try again.\n' - ' exec false\n' - 'fi\n' - ) % locals() - - if not self.dry_run: - mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run) - f = open(self.egg_output, 'w') - f.write(header) - f.close() - return 'a' + return 'w' def copy_metadata_to(self, target_dir): "Copy metadata (egg info) to the target_dir" @@ -419,9 +367,7 @@ def scan_module(egg_dir, base, name, stubs): return True # Extension module pkg = base[len(egg_dir) + 1:].replace(os.sep, '.') module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0] - if six.PY2: - skip = 8 # skip magic & date - elif sys.version_info < (3, 7): + if sys.version_info < (3, 7): skip = 12 # skip magic & date & file size else: skip = 16 # skip magic & reserved? & date & file size @@ -452,7 +398,7 @@ def iter_symbols(code): for name in code.co_names: yield name for const in code.co_consts: - if isinstance(const, six.string_types): + if isinstance(const, str): yield const elif isinstance(const, CodeType): for name in iter_symbols(const): diff --git a/venv/Lib/site-packages/setuptools/command/bdist_rpm.py b/venv/Lib/site-packages/setuptools/command/bdist_rpm.py index 70730927ecaed778ebbdee98eb37c24ec3f1a8e6..98bf5dea8468bf1728f18d97d1b9a43be33fdf20 100644 --- a/venv/Lib/site-packages/setuptools/command/bdist_rpm.py +++ b/venv/Lib/site-packages/setuptools/command/bdist_rpm.py @@ -1,4 +1,7 @@ import distutils.command.bdist_rpm as orig +import warnings + +from setuptools import SetuptoolsDeprecationWarning class bdist_rpm(orig.bdist_rpm): @@ -8,36 +11,30 @@ class bdist_rpm(orig.bdist_rpm): 1. Run egg_info to ensure the name and version are properly calculated. 2. Always run 'install' using --single-version-externally-managed to disable eggs in RPM distributions. - 3. Replace dash with underscore in the version numbers for better RPM - compatibility. """ def run(self): + warnings.warn( + "bdist_rpm is deprecated and will be removed in a future " + "version. Use bdist_wheel (wheel packages) instead.", + SetuptoolsDeprecationWarning, + ) + # ensure distro name is up-to-date self.run_command('egg_info') orig.bdist_rpm.run(self) def _make_spec_file(self): - version = self.distribution.get_version() - rpmversion = version.replace('-', '_') spec = orig.bdist_rpm._make_spec_file(self) - line23 = '%define version ' + version - line24 = '%define version ' + rpmversion spec = [ line.replace( - "Source0: %{name}-%{version}.tar", - "Source0: %{name}-%{unmangled_version}.tar" - ).replace( "setup.py install ", "setup.py install --single-version-externally-managed " ).replace( "%setup", "%setup -n %{name}-%{unmangled_version}" - ).replace(line23, line24) + ) for line in spec ] - insert_loc = spec.index(line24) + 1 - unmangled_version = "%define unmangled_version " + version - spec.insert(insert_loc, unmangled_version) return spec diff --git a/venv/Lib/site-packages/setuptools/command/bdist_wininst.py b/venv/Lib/site-packages/setuptools/command/bdist_wininst.py deleted file mode 100644 index ff4b634592191e706efda087e083793ca9d15114..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/command/bdist_wininst.py +++ /dev/null @@ -1,30 +0,0 @@ -import distutils.command.bdist_wininst as orig -import warnings - -from setuptools import SetuptoolsDeprecationWarning - - -class bdist_wininst(orig.bdist_wininst): - def reinitialize_command(self, command, reinit_subcommands=0): - """ - Supplement reinitialize_command to work around - http://bugs.python.org/issue20819 - """ - cmd = self.distribution.reinitialize_command( - command, reinit_subcommands) - if command in ('install', 'install_lib'): - cmd.install_lib = None - return cmd - - def run(self): - warnings.warn( - "bdist_wininst is deprecated and will be removed in a future " - "version. Use bdist_wheel (wheel packages) instead.", - SetuptoolsDeprecationWarning - ) - - self._is_running = True - try: - orig.bdist_wininst.run(self) - finally: - self._is_running = False diff --git a/venv/Lib/site-packages/setuptools/command/build_ext.py b/venv/Lib/site-packages/setuptools/command/build_ext.py index 0eb29adc8b1b2dcb90e0b86f357b7513fc630eea..c59eff8bbf7bf9f3ec79e9f2ffbc426e2233df5c 100644 --- a/venv/Lib/site-packages/setuptools/command/build_ext.py +++ b/venv/Lib/site-packages/setuptools/command/build_ext.py @@ -1,6 +1,7 @@ import os import sys import itertools +from importlib.machinery import EXTENSION_SUFFIXES from distutils.command.build_ext import build_ext as _du_build_ext from distutils.file_util import copy_file from distutils.ccompiler import new_compiler @@ -9,15 +10,6 @@ from distutils.errors import DistutilsError from distutils import log from setuptools.extension import Library -from setuptools.extern import six - -if six.PY2: - import imp - - EXTENSION_SUFFIXES = [ - s for s, _, tp in imp.get_suffixes() if tp == imp.C_EXTENSION] -else: - from importlib.machinery import EXTENSION_SUFFIXES try: # Attempt to use Cython for building extensions, if available @@ -112,18 +104,20 @@ class build_ext(_build_ext): self.write_stub(package_dir or os.curdir, ext, True) def get_ext_filename(self, fullname): - filename = _build_ext.get_ext_filename(self, fullname) + so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX') + if so_ext: + filename = os.path.join(*fullname.split('.')) + so_ext + else: + filename = _build_ext.get_ext_filename(self, fullname) + so_ext = get_config_var('EXT_SUFFIX') + if fullname in self.ext_map: ext = self.ext_map[fullname] - use_abi3 = ( - not six.PY2 - and getattr(ext, 'py_limited_api') - and get_abi3_suffix() - ) + use_abi3 = getattr(ext, 'py_limited_api') and get_abi3_suffix() if use_abi3: - so_ext = get_config_var('EXT_SUFFIX') filename = filename[:-len(so_ext)] - filename = filename + get_abi3_suffix() + so_ext = get_abi3_suffix() + filename = filename + so_ext if isinstance(ext, Library): fn, ext = os.path.splitext(filename) return self.shlib_compiler.library_filename(fn, libtype) @@ -254,8 +248,8 @@ class build_ext(_build_ext): '\n'.join([ "def __bootstrap__():", " global __bootstrap__, __file__, __loader__", - " import sys, os, pkg_resources" + if_dl(", dl"), - " from importlib.machinery import ExtensionFileLoader", + " import sys, os, pkg_resources, importlib.util" + + if_dl(", dl"), " __file__ = pkg_resources.resource_filename" "(__name__,%r)" % os.path.basename(ext._file_name), @@ -267,8 +261,10 @@ class build_ext(_build_ext): " try:", " os.chdir(os.path.dirname(__file__))", if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), - " ExtensionFileLoader(__name__,", - " __file__).load_module()", + " spec = importlib.util.spec_from_file_location(", + " __name__, __file__)", + " mod = importlib.util.module_from_spec(spec)", + " spec.loader.exec_module(mod)", " finally:", if_dl(" sys.setdlopenflags(old_flags)"), " os.chdir(old_dir)", diff --git a/venv/Lib/site-packages/setuptools/command/build_py.py b/venv/Lib/site-packages/setuptools/command/build_py.py index 9d0288a50d773f4dac33e2cde9d3aefd75a223e9..c3fdc0927c551fa7b15f73355e30ae436f8ccd7a 100644 --- a/venv/Lib/site-packages/setuptools/command/build_py.py +++ b/venv/Lib/site-packages/setuptools/command/build_py.py @@ -8,24 +8,14 @@ import io import distutils.errors import itertools import stat - -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter, filterfalse - -try: - from setuptools.lib2to3_ex import Mixin2to3 -except ImportError: - - class Mixin2to3: - def run_2to3(self, files, doctests=True): - "do nothing" +from setuptools.extern.more_itertools import unique_everseen def make_writable(target): os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE) -class build_py(orig.build_py, Mixin2to3): +class build_py(orig.build_py): """Enhanced 'build_py' command that includes data files with packages The data files are specified via a 'package_data' argument to 'setup()'. @@ -38,12 +28,10 @@ class build_py(orig.build_py, Mixin2to3): def finalize_options(self): orig.build_py.finalize_options(self) self.package_data = self.distribution.package_data - self.exclude_package_data = (self.distribution.exclude_package_data or - {}) + self.exclude_package_data = self.distribution.exclude_package_data or {} if 'data_files' in self.__dict__: del self.__dict__['data_files'] self.__updated_files = [] - self.__doctests_2to3 = [] def run(self): """Build modules, packages, and copy data files to build directory""" @@ -57,10 +45,6 @@ class build_py(orig.build_py, Mixin2to3): self.build_packages() self.build_package_data() - self.run_2to3(self.__updated_files, False) - self.run_2to3(self.__updated_files, True) - self.run_2to3(self.__doctests_2to3, True) - # Only compile actual .py files, using our base class' idea of what our # output files are. self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0)) @@ -73,11 +57,7 @@ class build_py(orig.build_py, Mixin2to3): return orig.build_py.__getattr__(self, attr) def build_module(self, module, module_file, package): - if six.PY2 and isinstance(package, six.string_types): - # avoid errors on Python 2 when unicode is passed (#190) - package = package.split('.') - outfile, copied = orig.build_py.build_module(self, module, module_file, - package) + outfile, copied = orig.build_py.build_module(self, module, module_file, package) if copied: self.__updated_files.append(outfile) return outfile, copied @@ -87,6 +67,16 @@ class build_py(orig.build_py, Mixin2to3): self.analyze_manifest() return list(map(self._get_pkg_data_files, self.packages or ())) + def get_data_files_without_manifest(self): + """ + Generate list of ``(package,src_dir,build_dir,filenames)`` tuples, + but without triggering any attempt to analyze or build the manifest. + """ + # Prevent eventual errors from unset `manifest_files` + # (that would otherwise be set by `analyze_manifest`) + self.__dict__.setdefault('manifest_files', {}) + return list(map(self._get_pkg_data_files, self.packages or ())) + def _get_pkg_data_files(self, package): # Locate package source directory src_dir = self.get_package_dir(package) @@ -128,9 +118,6 @@ class build_py(orig.build_py, Mixin2to3): outf, copied = self.copy_file(srcfile, target) make_writable(target) srcfile = os.path.abspath(srcfile) - if (copied and - srcfile in self.distribution.convert_2to3_doctests): - self.__doctests_2to3.append(outf) def analyze_manifest(self): self.manifest_files = mf = {} @@ -207,20 +194,13 @@ class build_py(orig.build_py, Mixin2to3): package, src_dir, ) - match_groups = ( - fnmatch.filter(files, pattern) - for pattern in patterns - ) + match_groups = (fnmatch.filter(files, pattern) for pattern in patterns) # flatten the groups of matches into an iterable of matches matches = itertools.chain.from_iterable(match_groups) bad = set(matches) - keepers = ( - fn - for fn in files - if fn not in bad - ) + keepers = (fn for fn in files if fn not in bad) # ditch dupes - return list(_unique_everseen(keepers)) + return list(unique_everseen(keepers)) @staticmethod def _get_platform_patterns(spec, package, src_dir): @@ -241,36 +221,22 @@ class build_py(orig.build_py, Mixin2to3): ) -# from Python docs -def _unique_everseen(iterable, key=None): - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element - - def assert_relative(path): if not os.path.isabs(path): return path from distutils.errors import DistutilsSetupError - msg = textwrap.dedent(""" + msg = ( + textwrap.dedent( + """ Error: setup script specifies an absolute path: %s setup() arguments must *always* be /-separated paths relative to the setup.py directory, *never* absolute paths. - """).lstrip() % path + """ + ).lstrip() + % path + ) raise DistutilsSetupError(msg) diff --git a/venv/Lib/site-packages/setuptools/command/develop.py b/venv/Lib/site-packages/setuptools/command/develop.py index e7e03cd449d4407a69479b840c18619c2e6fbb6b..24fb0a7c81bc665844d5d307eee2d720079c039f 100644 --- a/venv/Lib/site-packages/setuptools/command/develop.py +++ b/venv/Lib/site-packages/setuptools/command/develop.py @@ -5,15 +5,11 @@ import os import glob import io -from setuptools.extern import six - import pkg_resources from setuptools.command.easy_install import easy_install from setuptools import namespaces import setuptools -__metaclass__ = type - class develop(namespaces.DevelopInstaller, easy_install): """Set up package for development""" @@ -67,7 +63,8 @@ class develop(namespaces.DevelopInstaller, easy_install): target = pkg_resources.normalize_path(self.egg_base) egg_path = pkg_resources.normalize_path( - os.path.join(self.install_dir, self.egg_path)) + os.path.join(self.install_dir, self.egg_path) + ) if egg_path != target: raise DistutilsOptionError( "--egg-path must be a relative path from the install" @@ -78,7 +75,7 @@ class develop(namespaces.DevelopInstaller, easy_install): self.dist = pkg_resources.Distribution( target, pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)), - project_name=ei.egg_name + project_name=ei.egg_name, ) self.setup_path = self._resolve_setup_path( @@ -103,41 +100,18 @@ class develop(namespaces.DevelopInstaller, easy_install): if resolved != pkg_resources.normalize_path(os.curdir): raise DistutilsOptionError( "Can't get a consistent path to setup script from" - " installation directory", resolved, - pkg_resources.normalize_path(os.curdir)) + " installation directory", + resolved, + pkg_resources.normalize_path(os.curdir), + ) return path_to_setup def install_for_development(self): - if not six.PY2 and getattr(self.distribution, 'use_2to3', False): - # If we run 2to3 we can not do this inplace: - - # Ensure metadata is up-to-date - self.reinitialize_command('build_py', inplace=0) - self.run_command('build_py') - bpy_cmd = self.get_finalized_command("build_py") - build_path = pkg_resources.normalize_path(bpy_cmd.build_lib) - - # Build extensions - self.reinitialize_command('egg_info', egg_base=build_path) - self.run_command('egg_info') - - self.reinitialize_command('build_ext', inplace=0) - self.run_command('build_ext') - - # Fixup egg-link and easy-install.pth - ei_cmd = self.get_finalized_command("egg_info") - self.egg_path = build_path - self.dist.location = build_path - # XXX - self.dist._provider = pkg_resources.PathMetadata( - build_path, ei_cmd.egg_info) - else: - # Without 2to3 inplace works fine: - self.run_command('egg_info') + self.run_command('egg_info') - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=1) - self.run_command('build_ext') + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') if setuptools.bootstrap_install_from: self.easy_install(setuptools.bootstrap_install_from) @@ -160,8 +134,7 @@ class develop(namespaces.DevelopInstaller, easy_install): egg_link_file = open(self.egg_link) contents = [line.rstrip() for line in egg_link_file] egg_link_file.close() - if contents not in ([self.egg_path], - [self.egg_path, self.setup_path]): + if contents not in ([self.egg_path], [self.egg_path, self.setup_path]): log.warn("Link points to %s: uninstall aborted", contents) return if not self.dry_run: diff --git a/venv/Lib/site-packages/setuptools/command/easy_install.py b/venv/Lib/site-packages/setuptools/command/easy_install.py index bcbd4f58f5d42f3270742196046dafc542f221d7..fb34d10e096be462285050d149b7416395ed8115 100644 --- a/venv/Lib/site-packages/setuptools/command/easy_install.py +++ b/venv/Lib/site-packages/setuptools/command/easy_install.py @@ -6,7 +6,7 @@ A tool for doing automatic download/extract/build of distutils-based Python packages. For detailed documentation, see the accompanying EasyInstall.txt file, or visit the `EasyInstall home page`__. -__ https://setuptools.readthedocs.io/en/latest/easy_install.html +__ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html """ @@ -17,10 +17,10 @@ from distutils.errors import ( DistutilsArgError, DistutilsOptionError, DistutilsError, DistutilsPlatformError, ) -from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS from distutils import log, dir_util from distutils.command.build_scripts import first_line_re from distutils.spawn import find_executable +from distutils.command import install import sys import os import zipimport @@ -38,18 +38,15 @@ import contextlib import subprocess import shlex import io +import configparser from sysconfig import get_config_vars, get_path from setuptools import SetuptoolsDeprecationWarning -from setuptools.extern import six -from setuptools.extern.six.moves import configparser, map - from setuptools import Command from setuptools.sandbox import run_setup -from setuptools.py27compat import rmtree_safe from setuptools.command import setopt from setuptools.archive_util import unpack_archive from setuptools.package_index import ( @@ -65,14 +62,12 @@ from pkg_resources import ( ) import pkg_resources -__metaclass__ = type - # Turn on PEP440Warnings warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) __all__ = [ 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', - 'main', 'get_exe_prefixes', + 'get_exe_prefixes', ] @@ -96,28 +91,16 @@ def samefile(p1, p2): return norm_p1 == norm_p2 -if six.PY2: +def _to_bytes(s): + return s.encode('utf8') - def _to_bytes(s): - return s - def isascii(s): - try: - six.text_type(s, 'ascii') - return True - except UnicodeError: - return False -else: - - def _to_bytes(s): - return s.encode('utf8') - - def isascii(s): - try: - s.encode('ascii') - return True - except UnicodeError: - return False +def isascii(s): + try: + s.encode('ascii') + return True + except UnicodeError: + return False def _one_liner(text): @@ -170,6 +153,12 @@ class easy_install(Command): create_index = PackageIndex def initialize_options(self): + warnings.warn( + "easy_install command is deprecated. " + "Use build and pip and other standards-based tools.", + EasyInstallDeprecationWarning, + ) + # the --user option seems to be an opt-in one, # so the default should be False. self.user = 0 @@ -243,7 +232,7 @@ class easy_install(Command): print(tmpl.format(**locals())) raise SystemExit() - def finalize_options(self): + def finalize_options(self): # noqa: C901 # is too complex (25) # FIXME self.version and self._render_version() py_version = sys.version.split()[0] @@ -262,7 +251,14 @@ class easy_install(Command): 'exec_prefix': exec_prefix, # Only python 3.2+ has abiflags 'abiflags': getattr(sys, 'abiflags', ''), + 'platlibdir': getattr(sys, 'platlibdir', 'lib'), } + with contextlib.suppress(AttributeError): + # only for distutils outside stdlib + self.config_vars.update({ + 'implementation_lower': install._get_implementation().lower(), + 'implementation': install._get_implementation(), + }) if site.ENABLE_USER_SITE: self.config_vars['userbase'] = self.install_userbase @@ -341,7 +337,7 @@ class easy_install(Command): self.local_index = Environment(self.shadow_path + sys.path) if self.find_links is not None: - if isinstance(self.find_links, six.string_types): + if isinstance(self.find_links, str): self.find_links = self.find_links.split() else: self.find_links = [] @@ -382,7 +378,7 @@ class easy_install(Command): msg = "User base directory is not specified" raise DistutilsPlatformError(msg) self.install_base = self.install_platbase = self.install_userbase - scheme_name = os.name.replace('posix', 'unix') + '_user' + scheme_name = f'{os.name}_user' self.select_scheme(scheme_name) def _expand_attrs(self, attrs): @@ -454,7 +450,7 @@ class easy_install(Command): def warn_deprecated_options(self): pass - def check_site_dir(self): + def check_site_dir(self): # noqa: C901 # is too complex (12) # FIXME """Verify that self.install_dir is .pth-capable dir, if needed""" instdir = normalize_path(self.install_dir) @@ -530,7 +526,7 @@ class easy_install(Command): For information on other options, you may wish to consult the documentation at: - https://setuptools.readthedocs.io/en/latest/easy_install.html + https://setuptools.pypa.io/en/latest/deprecated/easy_install.html Please make the appropriate changes for your system and try again. """).lstrip() # noqa @@ -650,7 +646,7 @@ class easy_install(Command): # cast to str as workaround for #709 and #710 and #712 yield str(tmpdir) finally: - os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) + os.path.exists(tmpdir) and rmtree(tmpdir) def easy_install(self, spec, deps=False): with self._tmpdir() as tmpdir: @@ -722,15 +718,16 @@ class easy_install(Command): return dist def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" - # it's the caller's problem if they supply a bad name! - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: - attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) - - def process_distribution(self, requirement, dist, deps=True, *info): + try: + install._select_scheme(self, name) + except AttributeError: + # stdlib distutils + install.install.select_scheme(self, name.replace('posix', 'unix')) + + # FIXME: 'easy_install.process_distribution' is too complex (12) + def process_distribution( # noqa: C901 + self, requirement, dist, deps=True, *info, + ): self.update_pth(dist) self.package_index.add(dist) if dist in self.local_index[dist.key]: @@ -854,12 +851,19 @@ class easy_install(Command): def install_eggs(self, spec, dist_filename, tmpdir): # .egg dirs or files are already built, so just return them - if dist_filename.lower().endswith('.egg'): - return [self.install_egg(dist_filename, tmpdir)] - elif dist_filename.lower().endswith('.exe'): - return [self.install_exe(dist_filename, tmpdir)] - elif dist_filename.lower().endswith('.whl'): - return [self.install_wheel(dist_filename, tmpdir)] + installer_map = { + '.egg': self.install_egg, + '.exe': self.install_exe, + '.whl': self.install_wheel, + } + try: + install_dist = installer_map[ + dist_filename.lower()[-4:] + ] + except KeyError: + pass + else: + return [install_dist(dist_filename, tmpdir)] # Anything else, try to extract and build setup_base = tmpdir @@ -904,7 +908,8 @@ class easy_install(Command): metadata = EggMetadata(zipimport.zipimporter(egg_path)) return Distribution.from_filename(egg_path, metadata=metadata) - def install_egg(self, egg_path, tmpdir): + # FIXME: 'easy_install.install_egg' is too complex (11) + def install_egg(self, egg_path, tmpdir): # noqa: C901 destination = os.path.join( self.install_dir, os.path.basename(egg_path), @@ -1003,7 +1008,8 @@ class easy_install(Command): # install the .egg return self.install_egg(egg_path, tmpdir) - def exe_to_egg(self, dist_filename, egg_tmp): + # FIXME: 'easy_install.exe_to_egg' is too complex (12) + def exe_to_egg(self, dist_filename, egg_tmp): # noqa: C901 """Extract a bdist_wininst to the directories an egg would use""" # Check for .pth file and set up prefix translations prefixes = get_exe_prefixes(dist_filename) @@ -1195,22 +1201,24 @@ class easy_install(Command): for key, val in ei_opts.items(): if key not in fetch_directives: continue - fetch_options[key.replace('_', '-')] = val[1] + fetch_options[key] = val[1] # create a settings dictionary suitable for `edit_config` settings = dict(easy_install=fetch_options) cfg_filename = os.path.join(base, 'setup.cfg') setopt.edit_config(cfg_filename, settings) - def update_pth(self, dist): + def update_pth(self, dist): # noqa: C901 # is too complex (11) # FIXME if self.pth_file is None: return for d in self.pth_file[dist.key]: # drop old entries - if self.multi_version or d.location != dist.location: - log.info("Removing %s from easy-install.pth file", d) - self.pth_file.remove(d) - if d.location in self.shadow_path: - self.shadow_path.remove(d.location) + if not self.multi_version and d.location == dist.location: + continue + + log.info("Removing %s from easy-install.pth file", d) + self.pth_file.remove(d) + if d.location in self.shadow_path: + self.shadow_path.remove(d.location) if not self.multi_version: if dist.location in self.pth_file.paths: @@ -1224,19 +1232,21 @@ class easy_install(Command): if dist.location not in self.shadow_path: self.shadow_path.append(dist.location) - if not self.dry_run: + if self.dry_run: + return - self.pth_file.save() + self.pth_file.save() - if dist.key == 'setuptools': - # Ensure that setuptools itself never becomes unavailable! - # XXX should this check for latest version? - filename = os.path.join(self.install_dir, 'setuptools.pth') - if os.path.islink(filename): - os.unlink(filename) - f = open(filename, 'wt') - f.write(self.pth_file.make_relative(dist.location) + '\n') - f.close() + if dist.key != 'setuptools': + return + + # Ensure that setuptools itself never becomes unavailable! + # XXX should this check for latest version? + filename = os.path.join(self.install_dir, 'setuptools.pth') + if os.path.islink(filename): + os.unlink(filename) + with open(filename, 'wt') as f: + f.write(self.pth_file.make_relative(dist.location) + '\n') def unpack_progress(self, src, dst): # Progress filter for unpacking @@ -1307,7 +1317,7 @@ class easy_install(Command): * You can set up the installation directory to support ".pth" files by using one of the approaches described here: - https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations + https://setuptools.pypa.io/en/latest/deprecated/easy_install.html#custom-installation-locations Please make the appropriate changes for your system and try again. @@ -1318,7 +1328,7 @@ class easy_install(Command): if not self.user: return home = convert_path(os.path.expanduser("~")) - for name, path in six.iteritems(self.config_vars): + for name, path in self.config_vars.items(): if path.startswith(home) and not os.path.isdir(path): self.debug_print("os.makedirs('%s', 0o700)" % path) os.makedirs(path, 0o700) @@ -1377,58 +1387,63 @@ def get_site_dirs(): if sys.exec_prefix != sys.prefix: prefixes.append(sys.exec_prefix) for prefix in prefixes: - if prefix: - if sys.platform in ('os2emx', 'riscos'): - sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) - elif os.sep == '/': - sitedirs.extend([ - os.path.join( - prefix, - "lib", - "python{}.{}".format(*sys.version_info), - "site-packages", - ), - os.path.join(prefix, "lib", "site-python"), - ]) - else: - sitedirs.extend([ + if not prefix: + continue + + if sys.platform in ('os2emx', 'riscos'): + sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) + elif os.sep == '/': + sitedirs.extend([ + os.path.join( prefix, - os.path.join(prefix, "lib", "site-packages"), - ]) - if sys.platform == 'darwin': - # for framework builds *only* we add the standard Apple - # locations. Currently only per-user, but /Library and - # /Network/Library could be added too - if 'Python.framework' in prefix: - home = os.environ.get('HOME') - if home: - home_sp = os.path.join( - home, - 'Library', - 'Python', - '{}.{}'.format(*sys.version_info), - 'site-packages', - ) - sitedirs.append(home_sp) + "lib", + "python{}.{}".format(*sys.version_info), + "site-packages", + ), + os.path.join(prefix, "lib", "site-python"), + ]) + else: + sitedirs.extend([ + prefix, + os.path.join(prefix, "lib", "site-packages"), + ]) + if sys.platform != 'darwin': + continue + + # for framework builds *only* we add the standard Apple + # locations. Currently only per-user, but /Library and + # /Network/Library could be added too + if 'Python.framework' not in prefix: + continue + + home = os.environ.get('HOME') + if not home: + continue + + home_sp = os.path.join( + home, + 'Library', + 'Python', + '{}.{}'.format(*sys.version_info), + 'site-packages', + ) + sitedirs.append(home_sp) lib_paths = get_path('purelib'), get_path('platlib') - for site_lib in lib_paths: - if site_lib not in sitedirs: - sitedirs.append(site_lib) + + sitedirs.extend(s for s in lib_paths if s not in sitedirs) if site.ENABLE_USER_SITE: sitedirs.append(site.USER_SITE) - try: + with contextlib.suppress(AttributeError): sitedirs.extend(site.getsitepackages()) - except AttributeError: - pass sitedirs = list(map(normalize_path, sitedirs)) return sitedirs -def expand_paths(inputs): +def expand_paths(inputs): # noqa: C901 # is too complex (11) # FIXME """Yield sys.path directories that might contain "old-style" packages""" seen = {} @@ -1460,13 +1475,18 @@ def expand_paths(inputs): # Yield existing non-dupe, non-import directory lines from it for line in lines: - if not line.startswith("import"): - line = normalize_path(line.rstrip()) - if line not in seen: - seen[line] = 1 - if not os.path.isdir(line): - continue - yield line, os.listdir(line) + if line.startswith("import"): + continue + + line = normalize_path(line.rstrip()) + if line in seen: + continue + + seen[line] = 1 + if not os.path.isdir(line): + continue + + yield line, os.listdir(line) def extract_wininst_cfg(dist_filename): @@ -1499,7 +1519,7 @@ def extract_wininst_cfg(dist_filename): # Now the config is in bytes, but for RawConfigParser, it should # be text, so decode it. config = config.decode(sys.getfilesystemencoding()) - cfg.readfp(six.StringIO(config)) + cfg.read_file(io.StringIO(config)) except configparser.Error: return None if not cfg.has_section('metadata') or not cfg.has_section('Setup'): @@ -1534,9 +1554,7 @@ def get_exe_prefixes(exe_filename): if name.endswith('-nspkg.pth'): continue if parts[0].upper() in ('PURELIB', 'PLATLIB'): - contents = z.read(name) - if not six.PY2: - contents = contents.decode() + contents = z.read(name).decode() for pth in yield_lines(contents): pth = pth.strip().replace('\\', '/') if not pth.startswith('import'): @@ -1700,7 +1718,8 @@ def auto_chmod(func, arg, exc): chmod(arg, stat.S_IWRITE) return func(arg) et, ev, _ = sys.exc_info() - six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg)))) + # TODO: This code doesn't make sense. What is it trying to do? + raise (ev[0], ev[1] + (" %s %s" % (func, arg))) def update_dist_caches(dist_path, fix_zipimporter_caches): @@ -2185,7 +2204,7 @@ class WindowsScriptWriter(ScriptWriter): @classmethod def _adjust_header(cls, type_, orig_header): """ - Make sure 'pythonw' is used for gui and and 'python' is used for + Make sure 'pythonw' is used for gui and 'python' is used for console (regardless of what sys.executable is). """ pattern = 'pythonw.exe' @@ -2255,7 +2274,10 @@ def get_win_launcher(type): """ launcher_fn = '%s.exe' % type if is_64bit(): - launcher_fn = launcher_fn.replace(".", "-64.") + if get_platform() == "win-arm64": + launcher_fn = launcher_fn.replace(".", "-arm64.") + else: + launcher_fn = launcher_fn.replace(".", "-64.") else: launcher_fn = launcher_fn.replace(".", "-32.") return resource_string('setuptools', launcher_fn) @@ -2263,10 +2285,7 @@ def get_win_launcher(type): def load_launcher_manifest(name): manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml') - if six.PY2: - return manifest % vars() - else: - return manifest.decode('utf-8') % vars() + return manifest.decode('utf-8') % vars() def rmtree(path, ignore_errors=False, onerror=auto_chmod): @@ -2279,60 +2298,6 @@ def current_umask(): return tmp -def bootstrap(): - # This function is called when setuptools*.egg is run using /bin/sh - import setuptools - - argv0 = os.path.dirname(setuptools.__path__[0]) - sys.argv[0] = argv0 - sys.argv.append(argv0) - main() - - -def main(argv=None, **kw): - from setuptools import setup - from setuptools.dist import Distribution - - class DistributionWithoutHelpCommands(Distribution): - common_usage = "" - - def _show_help(self, *args, **kw): - with _patch_usage(): - Distribution._show_help(self, *args, **kw) - - if argv is None: - argv = sys.argv[1:] - - with _patch_usage(): - setup( - script_args=['-q', 'easy_install', '-v'] + argv, - script_name=sys.argv[0] or 'easy_install', - distclass=DistributionWithoutHelpCommands, - **kw - ) - - -@contextlib.contextmanager -def _patch_usage(): - import distutils.core - USAGE = textwrap.dedent(""" - usage: %(script)s [options] requirement_or_url ... - or: %(script)s --help - """).lstrip() - - def gen_usage(script_name): - return USAGE % dict( - script=os.path.basename(script_name), - ) - - saved = distutils.core.gen_usage - distutils.core.gen_usage = gen_usage - try: - yield - finally: - distutils.core.gen_usage = saved - - class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): """ Warning for EasyInstall deprecations, bypassing suppression. diff --git a/venv/Lib/site-packages/setuptools/command/egg_info.py b/venv/Lib/site-packages/setuptools/command/egg_info.py index 0855207ceb9dc5988e198005e2bd08bbab363555..f2210292e3e41c54d6175ef600e169206eca4f02 100644 --- a/venv/Lib/site-packages/setuptools/command/egg_info.py +++ b/venv/Lib/site-packages/setuptools/command/egg_info.py @@ -8,6 +8,7 @@ from distutils.util import convert_path from distutils import log import distutils.errors import distutils.filelist +import functools import os import re import sys @@ -16,9 +17,6 @@ import warnings import time import collections -from setuptools.extern import six -from setuptools.extern.six.moves import map - from setuptools import Command from setuptools.command.sdist import sdist from setuptools.command.sdist import walk_revctrl @@ -34,7 +32,7 @@ from setuptools.extern import packaging from setuptools import SetuptoolsDeprecationWarning -def translate_pattern(glob): +def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME """ Translate a file path glob like '*.txt' in to a regular expression. This differs from fnmatch.translate which allows wildcards to match @@ -126,12 +124,17 @@ class InfoCommon: return safe_name(self.distribution.get_name()) def tagged_version(self): - version = self.distribution.get_version() - # egg_info may be called more than once for a distribution, - # in which case the version string already contains all tags. - if self.vtags and version.endswith(self.vtags): - return safe_version(version) - return safe_version(version + self.vtags) + return safe_version(self._maybe_tag(self.distribution.get_version())) + + def _maybe_tag(self, version): + """ + egg_info may be called more than once for a distribution, + in which case the version string already contains all tags. + """ + return ( + version if self.vtags and version.endswith(self.vtags) + else version + self.vtags + ) def tags(self): version = '' @@ -267,8 +270,7 @@ class egg_info(InfoCommon, Command): to the file. """ log.info("writing %s to %s", what, filename) - if not six.PY2: - data = data.encode("utf-8") + data = data.encode("utf-8") if not self.dry_run: f = open(filename, 'wb') f.write(data) @@ -331,70 +333,74 @@ class FileList(_FileList): # patterns, (dir and patterns), or (dir_pattern). (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + action_map = { + 'include': self.include, + 'exclude': self.exclude, + 'global-include': self.global_include, + 'global-exclude': self.global_exclude, + 'recursive-include': functools.partial( + self.recursive_include, dir, + ), + 'recursive-exclude': functools.partial( + self.recursive_exclude, dir, + ), + 'graft': self.graft, + 'prune': self.prune, + } + log_map = { + 'include': "warning: no files found matching '%s'", + 'exclude': ( + "warning: no previously-included files found " + "matching '%s'" + ), + 'global-include': ( + "warning: no files found matching '%s' " + "anywhere in distribution" + ), + 'global-exclude': ( + "warning: no previously-included files matching " + "'%s' found anywhere in distribution" + ), + 'recursive-include': ( + "warning: no files found matching '%s' " + "under directory '%s'" + ), + 'recursive-exclude': ( + "warning: no previously-included files matching " + "'%s' found under directory '%s'" + ), + 'graft': "warning: no directories found matching '%s'", + 'prune': "no previously-included directories found matching '%s'", + } + + try: + process_action = action_map[action] + except KeyError: + raise DistutilsInternalError( + "this cannot happen: invalid action '{action!s}'". + format(action=action), + ) + # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we # can proceed with minimal error-checking. - if action == 'include': - self.debug_print("include " + ' '.join(patterns)) - for pattern in patterns: - if not self.include(pattern): - log.warn("warning: no files found matching '%s'", pattern) - - elif action == 'exclude': - self.debug_print("exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.exclude(pattern): - log.warn(("warning: no previously-included files " - "found matching '%s'"), pattern) - - elif action == 'global-include': - self.debug_print("global-include " + ' '.join(patterns)) - for pattern in patterns: - if not self.global_include(pattern): - log.warn(("warning: no files found matching '%s' " - "anywhere in distribution"), pattern) - - elif action == 'global-exclude': - self.debug_print("global-exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.global_exclude(pattern): - log.warn(("warning: no previously-included files matching " - "'%s' found anywhere in distribution"), - pattern) - - elif action == 'recursive-include': - self.debug_print("recursive-include %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.recursive_include(dir, pattern): - log.warn(("warning: no files found matching '%s' " - "under directory '%s'"), - pattern, dir) - - elif action == 'recursive-exclude': - self.debug_print("recursive-exclude %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.recursive_exclude(dir, pattern): - log.warn(("warning: no previously-included files matching " - "'%s' found under directory '%s'"), - pattern, dir) - - elif action == 'graft': - self.debug_print("graft " + dir_pattern) - if not self.graft(dir_pattern): - log.warn("warning: no directories found matching '%s'", - dir_pattern) - - elif action == 'prune': - self.debug_print("prune " + dir_pattern) - if not self.prune(dir_pattern): - log.warn(("no previously-included directories found " - "matching '%s'"), dir_pattern) - - else: - raise DistutilsInternalError( - "this cannot happen: invalid action '%s'" % action) + + action_is_recursive = action.startswith('recursive-') + if action in {'graft', 'prune'}: + patterns = [dir_pattern] + extra_log_args = (dir, ) if action_is_recursive else () + log_tmpl = log_map[action] + + self.debug_print( + ' '.join( + [action] + + ([dir] if action_is_recursive else []) + + patterns, + ) + ) + for pattern in patterns: + if not process_action(pattern): + log.warn(log_tmpl, pattern, *extra_log_args) def _remove_files(self, predicate): """ @@ -535,6 +541,7 @@ class manifest_maker(sdist): self.add_defaults() if os.path.exists(self.template): self.read_template() + self.add_license_files() self.prune_file_list() self.filelist.sort() self.filelist.remove_duplicates() @@ -569,7 +576,6 @@ class manifest_maker(sdist): def add_defaults(self): sdist.add_defaults(self) - self.check_license() self.filelist.append(self.template) self.filelist.append(self.manifest) rcfiles = list(walk_revctrl()) @@ -586,6 +592,13 @@ class manifest_maker(sdist): ei_cmd = self.get_finalized_command('egg_info') self.filelist.graft(ei_cmd.egg_info) + def add_license_files(self): + license_files = self.distribution.metadata.license_files or [] + for lf in license_files: + log.info("adding license file '%s'", lf) + pass + self.filelist.extend(license_files) + def prune_file_list(self): build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() @@ -595,6 +608,27 @@ class manifest_maker(sdist): self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep, is_regex=1) + def _safe_data_files(self, build_py): + """ + The parent class implementation of this method + (``sdist``) will try to include data files, which + might cause recursion problems when + ``include_package_data=True``. + + Therefore, avoid triggering any attempt of + analyzing/building the manifest again. + """ + if hasattr(build_py, 'get_data_files_without_manifest'): + return build_py.get_data_files_without_manifest() + + warnings.warn( + "Custom 'build_py' does not implement " + "'get_data_files_without_manifest'.\nPlease extend command classes" + " from setuptools instead of distutils.", + SetuptoolsDeprecationWarning + ) + return build_py.get_data_files() + def write_file(filename, contents): """Create a file with the specified name and write 'contents' (a @@ -647,7 +681,7 @@ def _write_requirements(stream, reqs): def write_requirements(cmd, basename, filename): dist = cmd.distribution - data = six.StringIO() + data = io.StringIO() _write_requirements(data, dist.install_requires) extras_require = dist.extras_require or {} for extra in sorted(extras_require): @@ -687,12 +721,12 @@ def write_arg(cmd, basename, filename, force=False): def write_entries(cmd, basename, filename): ep = cmd.distribution.entry_points - if isinstance(ep, six.string_types) or ep is None: + if isinstance(ep, str) or ep is None: data = ep elif ep is not None: data = [] for section, contents in sorted(ep.items()): - if not isinstance(contents, six.string_types): + if not isinstance(contents, str): contents = EntryPoint.parse_group(section, contents) contents = '\n'.join(sorted(map(str, contents.values()))) data.append('[%s]\n%s\n\n' % (section, contents)) diff --git a/venv/Lib/site-packages/setuptools/command/install.py b/venv/Lib/site-packages/setuptools/command/install.py index 72b9a3e424707633c7e31a347170f358cfa3f87a..35e54d2043ae087db660d3158561c7f865cf1681 100644 --- a/venv/Lib/site-packages/setuptools/command/install.py +++ b/venv/Lib/site-packages/setuptools/command/install.py @@ -30,6 +30,13 @@ class install(orig.install): _nc = dict(new_commands) def initialize_options(self): + + warnings.warn( + "setup.py install is deprecated. " + "Use build and pip and other standards-based tools.", + setuptools.SetuptoolsDeprecationWarning, + ) + orig.install.initialize_options(self) self.old_and_unmanageable = None self.single_version_externally_managed = None diff --git a/venv/Lib/site-packages/setuptools/command/install_scripts.py b/venv/Lib/site-packages/setuptools/command/install_scripts.py index 8c9a15e2bbda0ecb1442d1792627b695509e3330..9cd8eb06277f449599a7b4babe74e1adab33bdc2 100644 --- a/venv/Lib/site-packages/setuptools/command/install_scripts.py +++ b/venv/Lib/site-packages/setuptools/command/install_scripts.py @@ -1,5 +1,6 @@ from distutils import log import distutils.command.install_scripts as orig +from distutils.errors import DistutilsModuleError import os import sys @@ -35,7 +36,7 @@ class install_scripts(orig.install_scripts): try: bw_cmd = self.get_finalized_command("bdist_wininst") is_wininst = getattr(bw_cmd, '_is_running', False) - except ImportError: + except (ImportError, DistutilsModuleError): is_wininst = False writer = ei.ScriptWriter if is_wininst: diff --git a/venv/Lib/site-packages/setuptools/command/py36compat.py b/venv/Lib/site-packages/setuptools/command/py36compat.py index 2886055862c65c7159287191b3eaa54f4ab3913f..343547a4d316e48144ba6bdf342dcc24cd6cb6cd 100644 --- a/venv/Lib/site-packages/setuptools/command/py36compat.py +++ b/venv/Lib/site-packages/setuptools/command/py36compat.py @@ -3,8 +3,6 @@ from glob import glob from distutils.util import convert_path from distutils.command import sdist -from setuptools.extern.six.moves import filter - class sdist_add_defaults: """ diff --git a/venv/Lib/site-packages/setuptools/command/rotate.py b/venv/Lib/site-packages/setuptools/command/rotate.py index e398834fa71486e86cca09159df2a41ec8660edd..74795ba922bb376e24858760e63dc9124ef22b9f 100644 --- a/venv/Lib/site-packages/setuptools/command/rotate.py +++ b/venv/Lib/site-packages/setuptools/command/rotate.py @@ -4,8 +4,6 @@ from distutils.errors import DistutilsOptionError import os import shutil -from setuptools.extern import six - from setuptools import Command @@ -38,7 +36,7 @@ class rotate(Command): self.keep = int(self.keep) except ValueError as e: raise DistutilsOptionError("--keep must be an integer") from e - if isinstance(self.match, six.string_types): + if isinstance(self.match, str): self.match = [ convert_path(p.strip()) for p in self.match.split(',') ] diff --git a/venv/Lib/site-packages/setuptools/command/sdist.py b/venv/Lib/site-packages/setuptools/command/sdist.py index 8c3438eaa6c995a5e52440b4692116593e6ca62b..0285b690fc7417198d21cb71f67ce22d48514e8a 100644 --- a/venv/Lib/site-packages/setuptools/command/sdist.py +++ b/venv/Lib/site-packages/setuptools/command/sdist.py @@ -5,8 +5,6 @@ import sys import io import contextlib -from setuptools.extern import six, ordered_set - from .py36compat import sdist_add_defaults import pkg_resources @@ -33,6 +31,10 @@ class sdist(sdist_add_defaults, orig.sdist): ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), ] negative_opt = {} @@ -98,34 +100,8 @@ class sdist(sdist_add_defaults, orig.sdist): if orig_val is not NoValue: setattr(os, 'link', orig_val) - def __read_template_hack(self): - # This grody hack closes the template file (MANIFEST.in) if an - # exception occurs during read_template. - # Doing so prevents an error when easy_install attempts to delete the - # file. - try: - orig.sdist.read_template(self) - except Exception: - _, _, tb = sys.exc_info() - tb.tb_next.tb_frame.f_locals['template'].close() - raise - - # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle - # has been fixed, so only override the method if we're using an earlier - # Python. - has_leaky_handle = ( - sys.version_info < (2, 7, 2) - or (3, 0) <= sys.version_info < (3, 1, 4) - or (3, 2) <= sys.version_info < (3, 2, 1) - ) - if has_leaky_handle: - read_template = __read_template_hack - def _add_defaults_optional(self): - if six.PY2: - sdist_add_defaults._add_defaults_optional(self) - else: - super()._add_defaults_optional() + super()._add_defaults_optional() if os.path.isfile('pyproject.toml'): self.filelist.append('pyproject.toml') @@ -138,12 +114,15 @@ class sdist(sdist_add_defaults, orig.sdist): def _safe_data_files(self, build_py): """ - Extracting data_files from build_py is known to cause - infinite recursion errors when `include_package_data` - is enabled, so suppress it in that case. + Since the ``sdist`` class is also used to compute the MANIFEST + (via :obj:`setuptools.command.egg_info.manifest_maker`), + there might be recursion problems when trying to obtain the list of + data_files and ``include_package_data=True`` (which in turn depends on + the files included in the MANIFEST). + + To avoid that, ``manifest_maker`` should be able to overwrite this + method and avoid recursive attempts to build/analyze the MANIFEST. """ - if self.distribution.include_package_data: - return () return build_py.data_files def _add_data_files(self, data_files): @@ -158,10 +137,7 @@ class sdist(sdist_add_defaults, orig.sdist): def _add_defaults_data_files(self): try: - if six.PY2: - sdist_add_defaults._add_defaults_data_files(self) - else: - super()._add_defaults_data_files() + super()._add_defaults_data_files() except TypeError: log.warn("data_files contains unexpected objects") @@ -207,46 +183,14 @@ class sdist(sdist_add_defaults, orig.sdist): manifest = open(self.manifest, 'rb') for line in manifest: # The manifest must contain UTF-8. See #303. - if not six.PY2: - try: - line = line.decode('UTF-8') - except UnicodeDecodeError: - log.warn("%r not UTF-8 decodable -- skipping" % line) - continue + try: + line = line.decode('UTF-8') + except UnicodeDecodeError: + log.warn("%r not UTF-8 decodable -- skipping" % line) + continue # ignore comments and blank lines line = line.strip() if line.startswith('#') or not line: continue self.filelist.append(line) manifest.close() - - def check_license(self): - """Checks if license_file' or 'license_files' is configured and adds any - valid paths to 'self.filelist'. - """ - - files = ordered_set.OrderedSet() - - opts = self.distribution.get_option_dict('metadata') - - # ignore the source of the value - _, license_file = opts.get('license_file', (None, None)) - - if license_file is None: - log.debug("'license_file' option was not specified") - else: - files.add(license_file) - - try: - files.update(self.distribution.metadata.license_files) - except TypeError: - log.warn("warning: 'license_files' option is malformed") - - for f in files: - if not os.path.exists(f): - log.warn( - "warning: Failed to find the configured license file '%s'", - f) - files.remove(f) - - self.filelist.extend(files) diff --git a/venv/Lib/site-packages/setuptools/command/setopt.py b/venv/Lib/site-packages/setuptools/command/setopt.py index 7e57cc02627fc3c3bb49613731a51c72452f96ba..6358c0451b2d0036e3821d897fb6f7ab436ee4a9 100644 --- a/venv/Lib/site-packages/setuptools/command/setopt.py +++ b/venv/Lib/site-packages/setuptools/command/setopt.py @@ -3,8 +3,7 @@ from distutils import log from distutils.errors import DistutilsOptionError import distutils import os - -from setuptools.extern.six.moves import configparser +import configparser from setuptools import Command @@ -40,6 +39,7 @@ def edit_config(filename, settings, dry_run=False): """ log.debug("Reading configuration from %s", filename) opts = configparser.RawConfigParser() + opts.optionxform = lambda x: x opts.read([filename]) for section, options in settings.items(): if options is None: diff --git a/venv/Lib/site-packages/setuptools/command/test.py b/venv/Lib/site-packages/setuptools/command/test.py index 2d83967dd9182a7cd6c56074d1386e71fd134c64..4a389e4d071d3753f14a8b74338def21f6b54299 100644 --- a/venv/Lib/site-packages/setuptools/command/test.py +++ b/venv/Lib/site-packages/setuptools/command/test.py @@ -8,20 +8,21 @@ from distutils.errors import DistutilsError, DistutilsOptionError from distutils import log from unittest import TestLoader -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter - -from pkg_resources import (resource_listdir, resource_exists, normalize_path, - working_set, _namespace_packages, evaluate_marker, - add_activation_listener, require, EntryPoint) +from pkg_resources import ( + resource_listdir, + resource_exists, + normalize_path, + working_set, + evaluate_marker, + add_activation_listener, + require, + EntryPoint, +) from setuptools import Command -from .build_py import _unique_everseen - -__metaclass__ = type +from setuptools.extern.more_itertools import unique_everseen class ScanningLoader(TestLoader): - def __init__(self): TestLoader.__init__(self) self._visited = set() @@ -78,8 +79,11 @@ class test(Command): user_options = [ ('test-module=', 'm', "Run 'test_suite' in specified module"), - ('test-suite=', 's', - "Run single test, case or suite (e.g. 'module.test_suite')"), + ( + 'test-suite=', + 's', + "Run single test, case or suite (e.g. 'module.test_suite')", + ), ('test-runner=', 'r', "Test runner to use"), ] @@ -129,31 +133,11 @@ class test(Command): @contextlib.contextmanager def project_on_sys_path(self, include_dists=[]): - with_2to3 = not six.PY2 and getattr( - self.distribution, 'use_2to3', False) - - if with_2to3: - # If we run 2to3 we can not do this inplace: - - # Ensure metadata is up-to-date - self.reinitialize_command('build_py', inplace=0) - self.run_command('build_py') - bpy_cmd = self.get_finalized_command("build_py") - build_path = normalize_path(bpy_cmd.build_lib) + self.run_command('egg_info') - # Build extensions - self.reinitialize_command('egg_info', egg_base=build_path) - self.run_command('egg_info') - - self.reinitialize_command('build_ext', inplace=0) - self.run_command('build_ext') - else: - # Without 2to3 inplace works fine: - self.run_command('egg_info') - - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=1) - self.run_command('build_ext') + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') ei_cmd = self.get_finalized_command("egg_info") @@ -188,7 +172,7 @@ class test(Command): orig_pythonpath = os.environ.get('PYTHONPATH', nothing) current_pythonpath = os.environ.get('PYTHONPATH', '') try: - prefix = os.pathsep.join(_unique_everseen(paths)) + prefix = os.pathsep.join(unique_everseen(paths)) to_join = filter(None, [prefix, current_pythonpath]) new_path = os.pathsep.join(to_join) if new_path: @@ -209,7 +193,8 @@ class test(Command): ir_d = dist.fetch_build_eggs(dist.install_requires) tr_d = dist.fetch_build_eggs(dist.tests_require or []) er_d = dist.fetch_build_eggs( - v for k, v in dist.extras_require.items() + v + for k, v in dist.extras_require.items() if k.startswith(':') and evaluate_marker(k[1:]) ) return itertools.chain(ir_d, tr_d, er_d) @@ -238,23 +223,10 @@ class test(Command): self.run_tests() def run_tests(self): - # Purge modules under test from sys.modules. The test loader will - # re-import them from the build location. Required when 2to3 is used - # with namespace packages. - if not six.PY2 and getattr(self.distribution, 'use_2to3', False): - module = self.test_suite.split('.')[0] - if module in _namespace_packages: - del_modules = [] - if module in sys.modules: - del_modules.append(module) - module += '.' - for name in sys.modules: - if name.startswith(module): - del_modules.append(name) - list(map(sys.modules.__delitem__, del_modules)) - test = unittest.main( - None, None, self._argv, + None, + None, + self._argv, testLoader=self._resolve_as_ep(self.test_loader), testRunner=self._resolve_as_ep(self.test_runner), exit=False, diff --git a/venv/Lib/site-packages/setuptools/command/upload_docs.py b/venv/Lib/site-packages/setuptools/command/upload_docs.py index 0351da77738e6857312f5726e3b0bb00032ae9cf..845bff4421f8700eb6e1bf719b0639185e6ed0b1 100644 --- a/venv/Lib/site-packages/setuptools/command/upload_docs.py +++ b/venv/Lib/site-packages/setuptools/command/upload_docs.py @@ -2,7 +2,7 @@ """upload_docs Implements a Distutils 'upload_docs' subcommand (upload documentation to -PyPI's pythonhosted.org). +sites other than PyPi such as devpi). """ from base64 import standard_b64encode @@ -15,17 +15,15 @@ import tempfile import shutil import itertools import functools - -from setuptools.extern import six -from setuptools.extern.six.moves import http_client, urllib +import http.client +import urllib.parse from pkg_resources import iter_entry_points from .upload import upload def _encode(s): - errors = 'strict' if six.PY2 else 'surrogateescape' - return s.encode('utf-8', errors) + return s.encode('utf-8', 'surrogateescape') class upload_docs(upload): @@ -33,7 +31,7 @@ class upload_docs(upload): # supported by Warehouse (and won't be). DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/' - description = 'Upload documentation to PyPI' + description = 'Upload documentation to sites other than PyPi such as devpi' user_options = [ ('repository=', 'r', @@ -61,7 +59,7 @@ class upload_docs(upload): if self.upload_dir is None: if self.has_sphinx(): build_sphinx = self.get_finalized_command('build_sphinx') - self.target_dir = build_sphinx.builder_target_dir + self.target_dir = dict(build_sphinx.builder_target_dirs)['html'] else: build = self.get_finalized_command('build') self.target_dir = os.path.join(build.build_base, 'docs') @@ -69,7 +67,7 @@ class upload_docs(upload): self.ensure_dirname('upload_dir') self.target_dir = self.upload_dir if 'pypi.python.org' in self.repository: - log.warn("Upload_docs command is deprecated. Use RTD instead.") + log.warn("Upload_docs command is deprecated for PyPi. Use RTD instead.") self.announce('Using upload directory %s' % self.target_dir) def create_zipfile(self, filename): @@ -152,9 +150,7 @@ class upload_docs(upload): } # set up the authentication credentials = _encode(self.username + ':' + self.password) - credentials = standard_b64encode(credentials) - if not six.PY2: - credentials = credentials.decode('ascii') + credentials = standard_b64encode(credentials).decode('ascii') auth = "Basic " + credentials body, ct = self._build_multipart(data) @@ -169,9 +165,9 @@ class upload_docs(upload): urllib.parse.urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': - conn = http_client.HTTPConnection(netloc) + conn = http.client.HTTPConnection(netloc) elif schema == 'https': - conn = http_client.HTTPSConnection(netloc) + conn = http.client.HTTPSConnection(netloc) else: raise AssertionError("unsupported schema " + schema) diff --git a/venv/Lib/site-packages/setuptools/config.py b/venv/Lib/site-packages/setuptools/config.py index a8f8b6b0068f453d858140b9589ef7a8df5d74bf..b4e968e5cae9c32b6aa270e9cbdce1b598efa1b6 100644 --- a/venv/Lib/site-packages/setuptools/config.py +++ b/venv/Lib/site-packages/setuptools/config.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, unicode_literals import ast import io import os @@ -10,21 +9,19 @@ import importlib from collections import defaultdict from functools import partial from functools import wraps +from glob import iglob import contextlib from distutils.errors import DistutilsOptionError, DistutilsFileError -from setuptools.extern.packaging.version import LegacyVersion, parse +from setuptools.extern.packaging.version import Version, InvalidVersion from setuptools.extern.packaging.specifiers import SpecifierSet -from setuptools.extern.six import string_types, PY3 - - -__metaclass__ = type class StaticModule: """ Attempt to load the module by the name """ + def __init__(self, name): spec = importlib.util.find_spec(name) with open(spec.origin) as strm: @@ -60,8 +57,7 @@ def patch_path(path): sys.path.remove(path) -def read_configuration( - filepath, find_others=False, ignore_option_errors=False): +def read_configuration(filepath, find_others=False, ignore_option_errors=False): """Read given configuration file and returns options from it as a dict. :param str|unicode filepath: Path to configuration file @@ -82,8 +78,7 @@ def read_configuration( filepath = os.path.abspath(filepath) if not os.path.isfile(filepath): - raise DistutilsFileError( - 'Configuration file %s does not exist.' % filepath) + raise DistutilsFileError('Configuration file %s does not exist.' % filepath) current_directory = os.getcwd() os.chdir(os.path.dirname(filepath)) @@ -98,8 +93,8 @@ def read_configuration( _Distribution.parse_config_files(dist, filenames=filenames) handlers = parse_configuration( - dist, dist.command_options, - ignore_option_errors=ignore_option_errors) + dist, dist.command_options, ignore_option_errors=ignore_option_errors + ) finally: os.chdir(current_directory) @@ -137,8 +132,7 @@ def configuration_to_dict(handlers): return config_dict -def parse_configuration( - distribution, command_options, ignore_option_errors=False): +def parse_configuration(distribution, command_options, ignore_option_errors=False): """Performs additional parsing of configuration options for a distribution. @@ -152,13 +146,15 @@ def parse_configuration( If False exceptions are propagated as expected. :rtype: list """ - options = ConfigOptionsHandler( - distribution, command_options, ignore_option_errors) + options = ConfigOptionsHandler(distribution, command_options, ignore_option_errors) options.parse() meta = ConfigMetadataHandler( - distribution.metadata, command_options, ignore_option_errors, - distribution.package_dir) + distribution.metadata, + command_options, + ignore_option_errors, + distribution.package_dir, + ) meta.parse() return meta, options @@ -200,7 +196,8 @@ class ConfigHandler: def parsers(self): """Metadata item name to parser function mapping.""" raise NotImplementedError( - '%s must provide .parsers property' % self.__class__.__name__) + '%s must provide .parsers property' % self.__class__.__name__ + ) def __setitem__(self, option_name, value): unknown = tuple() @@ -260,6 +257,34 @@ class ConfigHandler: return [chunk.strip() for chunk in value if chunk.strip()] + @classmethod + def _parse_list_glob(cls, value, separator=','): + """Equivalent to _parse_list() but expands any glob patterns using glob(). + + However, unlike with glob() calls, the results remain relative paths. + + :param value: + :param separator: List items separator character. + :rtype: list + """ + glob_characters = ('*', '?', '[', ']', '{', '}') + values = cls._parse_list(value, separator=separator) + expanded_values = [] + for value in values: + + # Has globby characters? + if any(char in value for char in glob_characters): + # then expand the glob pattern while keeping paths *relative*: + expanded_values.extend(sorted( + os.path.relpath(path, os.getcwd()) + for path in iglob(os.path.abspath(value)))) + + else: + # take the value as-is: + expanded_values.append(value) + + return expanded_values + @classmethod def _parse_dict(cls, value): """Represents value as a dict. @@ -273,7 +298,8 @@ class ConfigHandler: key, sep, val = line.partition(separator) if sep != separator: raise DistutilsOptionError( - 'Unable to parse option value to dict: %s' % value) + 'Unable to parse option value to dict: %s' % value + ) result[key.strip()] = val.strip() return result @@ -299,13 +325,16 @@ class ConfigHandler: :param key: :rtype: callable """ + def parser(value): exclude_directive = 'file:' if value.startswith(exclude_directive): raise ValueError( 'Only strings are accepted for the {0} field, ' - 'files are not accepted'.format(key)) + 'files are not accepted'.format(key) + ) return value + return parser @classmethod @@ -324,26 +353,24 @@ class ConfigHandler: """ include_directive = 'file:' - if not isinstance(value, string_types): + if not isinstance(value, str): return value if not value.startswith(include_directive): return value - spec = value[len(include_directive):] + spec = value[len(include_directive) :] filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) return '\n'.join( cls._read_file(path) for path in filepaths - if (cls._assert_local(path) or True) - and os.path.isfile(path) + if (cls._assert_local(path) or True) and os.path.isfile(path) ) @staticmethod def _assert_local(filepath): if not filepath.startswith(os.getcwd()): - raise DistutilsOptionError( - '`file:` directive can not access %s' % filepath) + raise DistutilsOptionError('`file:` directive can not access %s' % filepath) @staticmethod def _read_file(filepath): @@ -405,6 +432,7 @@ class ConfigHandler: :param parse_methods: :rtype: callable """ + def parse(value): parsed = value @@ -458,22 +486,25 @@ class ConfigHandler: self, # Dots in section names are translated into dunderscores. ('parse_section%s' % method_postfix).replace('.', '__'), - None) + None, + ) if section_parser_method is None: raise DistutilsOptionError( - 'Unsupported distribution option section: [%s.%s]' % ( - self.section_prefix, section_name)) + 'Unsupported distribution option section: [%s.%s]' + % (self.section_prefix, section_name) + ) section_parser_method(section_options) def _deprecated_config_handler(self, func, msg, warning_class): - """ this function will wrap around parameters that are deprecated + """this function will wrap around parameters that are deprecated :param msg: deprecation message :param warning_class: class of warning exception to be raised :param func: function to be wrapped around """ + @wraps(func) def config_handler(*args, **kwargs): warnings.warn(msg, warning_class) @@ -499,10 +530,12 @@ class ConfigMetadataHandler(ConfigHandler): """ - def __init__(self, target_obj, options, ignore_option_errors=False, - package_dir=None): - super(ConfigMetadataHandler, self).__init__(target_obj, options, - ignore_option_errors) + def __init__( + self, target_obj, options, ignore_option_errors=False, package_dir=None + ): + super(ConfigMetadataHandler, self).__init__( + target_obj, options, ignore_option_errors + ) self.package_dir = package_dir @property @@ -521,10 +554,17 @@ class ConfigMetadataHandler(ConfigHandler): parse_list, "The requires parameter is deprecated, please use " "install_requires for runtime dependencies.", - DeprecationWarning), + DeprecationWarning, + ), 'obsoletes': parse_list, 'classifiers': self._get_parser_compound(parse_file, parse_list), 'license': exclude_files_parser('license'), + 'license_file': self._deprecated_config_handler( + exclude_files_parser('license_file'), + "The license_file parameter is deprecated, " + "use license_files instead.", + DeprecationWarning, + ), 'license_files': parse_list, 'description': parse_file, 'long_description': parse_file, @@ -545,7 +585,9 @@ class ConfigMetadataHandler(ConfigHandler): version = version.strip() # Be strict about versions loaded from file because it's easy to # accidentally include newlines and other unintended content - if isinstance(parse(version), LegacyVersion): + try: + Version(version) + except InvalidVersion: tmpl = ( 'Version loaded from {value} does not ' 'comply with PEP 440: {version}' @@ -559,7 +601,7 @@ class ConfigMetadataHandler(ConfigHandler): if callable(version): version = version() - if not isinstance(version, string_types): + if not isinstance(version, str): if hasattr(version, '__iter__'): version = '.'.join(map(str, version)) else: @@ -579,15 +621,12 @@ class ConfigOptionsHandler(ConfigHandler): parse_list_semicolon = partial(self._parse_list, separator=';') parse_bool = self._parse_bool parse_dict = self._parse_dict + parse_cmdclass = self._parse_cmdclass return { 'zip_safe': parse_bool, - 'use_2to3': parse_bool, 'include_package_data': parse_bool, 'package_dir': parse_dict, - 'use_2to3_fixers': parse_list, - 'use_2to3_exclude_fixers': parse_list, - 'convert_2to3_doctests': parse_list, 'scripts': parse_list, 'eager_resources': parse_list, 'dependency_links': parse_list, @@ -599,8 +638,21 @@ class ConfigOptionsHandler(ConfigHandler): 'entry_points': self._parse_file, 'py_modules': parse_list, 'python_requires': SpecifierSet, + 'cmdclass': parse_cmdclass, } + def _parse_cmdclass(self, value): + def resolve_class(qualified_class_name): + idx = qualified_class_name.rfind('.') + class_name = qualified_class_name[idx + 1 :] + pkg_name = qualified_class_name[:idx] + + module = __import__(pkg_name) + + return getattr(module, class_name) + + return {k: resolve_class(v) for k, v in self._parse_dict(value).items()} + def _parse_packages(self, value): """Parses `packages` option value. @@ -614,13 +666,11 @@ class ConfigOptionsHandler(ConfigHandler): return self._parse_list(value) findns = trimmed_value == find_directives[1] - if findns and not PY3: - raise DistutilsOptionError( - 'find_namespace: directive is unsupported on Python < 3.3') # Read function arguments from a dedicated section. find_kwargs = self.parse_section_packages__find( - self.sections.get('packages.find', {})) + self.sections.get('packages.find', {}) + ) if findns: from setuptools import find_namespace_packages as find_packages @@ -636,13 +686,13 @@ class ConfigOptionsHandler(ConfigHandler): :param dict section_options: """ - section_data = self._parse_section_to_dict( - section_options, self._parse_list) + section_data = self._parse_section_to_dict(section_options, self._parse_list) valid_keys = ['where', 'include', 'exclude'] find_kwargs = dict( - [(k, v) for k, v in section_data.items() if k in valid_keys and v]) + [(k, v) for k, v in section_data.items() if k in valid_keys and v] + ) where = find_kwargs.get('where') if where is not None: @@ -680,8 +730,7 @@ class ConfigOptionsHandler(ConfigHandler): :param dict section_options: """ - self['exclude_package_data'] = self._parse_package_data( - section_options) + self['exclude_package_data'] = self._parse_package_data(section_options) def parse_section_extras_require(self, section_options): """Parses `extras_require` configuration file section. @@ -690,12 +739,13 @@ class ConfigOptionsHandler(ConfigHandler): """ parse_list = partial(self._parse_list, separator=';') self['extras_require'] = self._parse_section_to_dict( - section_options, parse_list) + section_options, parse_list + ) def parse_section_data_files(self, section_options): """Parses `data_files` configuration file section. :param dict section_options: """ - parsed = self._parse_section_to_dict(section_options, self._parse_list) + parsed = self._parse_section_to_dict(section_options, self._parse_list_glob) self['data_files'] = [(k, v) for k, v in parsed.items()] diff --git a/venv/Lib/site-packages/setuptools/depends.py b/venv/Lib/site-packages/setuptools/depends.py index a37675cbd9bc9583fd01cc158198e2f4deda321b..adffd12db8c8e0477ee6532cd3b84f2e0cde9632 100644 --- a/venv/Lib/site-packages/setuptools/depends.py +++ b/venv/Lib/site-packages/setuptools/depends.py @@ -1,12 +1,12 @@ import sys import marshal import contextlib -from distutils.version import StrictVersion +import dis -from .py33compat import Bytecode +from setuptools.extern.packaging import version -from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE -from . import py27compat +from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE +from . import _imp __all__ = [ @@ -22,7 +22,7 @@ class Require: attribute=None, format=None): if format is None and requested_version is not None: - format = StrictVersion + format = version.Version if format is not None: requested_version = format(requested_version) @@ -41,7 +41,7 @@ class Require: def version_ok(self, version): """Is 'version' sufficiently up-to-date?""" return self.attribute is None or self.format is None or \ - str(version) != "unknown" and version >= self.requested_version + str(version) != "unknown" and self.format(version) >= self.requested_version def get_version(self, paths=None, default="unknown"): """Get version number of installed module, 'None', or 'default' @@ -79,7 +79,7 @@ class Require: version = self.get_version(paths) if version is None: return False - return self.version_ok(version) + return self.version_ok(str(version)) def maybe_close(f): @@ -111,12 +111,12 @@ def get_module_constant(module, symbol, default=-1, paths=None): f.read(8) # skip magic & date code = marshal.load(f) elif kind == PY_FROZEN: - code = py27compat.get_frozen_object(module, paths) + code = _imp.get_frozen_object(module, paths) elif kind == PY_SOURCE: code = compile(f.read(), path, 'exec') else: # Not something we can parse; we'll have to import it. :( - imported = py27compat.get_module(module, paths, info) + imported = _imp.get_module(module, paths, info) return getattr(imported, symbol, None) return extract_constant(code, symbol, default) @@ -146,7 +146,7 @@ def extract_constant(code, symbol, default=-1): const = default - for byte_code in Bytecode(code): + for byte_code in dis.Bytecode(code): op = byte_code.opcode arg = byte_code.arg diff --git a/venv/Lib/site-packages/setuptools/dist.py b/venv/Lib/site-packages/setuptools/dist.py index e813b11ca762376d4094d381d53b063e785a53cc..37a10d1dcd92e9ab8c1409173332f9b76d82c9ae 100644 --- a/venv/Lib/site-packages/setuptools/dist.py +++ b/venv/Lib/site-packages/setuptools/dist.py @@ -11,30 +11,37 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist +import distutils.command from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt +from glob import iglob import itertools +import textwrap +from typing import List, Optional, TYPE_CHECKING from collections import defaultdict from email import message_from_file from distutils.errors import DistutilsOptionError, DistutilsSetupError from distutils.util import rfc822_escape -from distutils.version import StrictVersion -from setuptools.extern import six from setuptools.extern import packaging from setuptools.extern import ordered_set -from setuptools.extern.six.moves import map, filter, filterfalse +from setuptools.extern.more_itertools import unique_everseen from . import SetuptoolsDeprecationWarning import setuptools +import setuptools.command from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration import pkg_resources +from setuptools.extern.packaging import version + +if TYPE_CHECKING: + from email.message import Message __import__('setuptools.extern.packaging.specifiers') __import__('setuptools.extern.packaging.version') @@ -47,133 +54,150 @@ def _get_unpatched(cls): def get_metadata_version(self): mv = getattr(self, 'metadata_version', None) - if mv is None: - if self.long_description_content_type or self.provides_extras: - mv = StrictVersion('2.1') - elif (self.maintainer is not None or - self.maintainer_email is not None or - getattr(self, 'python_requires', None) is not None or - self.project_urls): - mv = StrictVersion('1.2') - elif (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - mv = StrictVersion('1.1') - else: - mv = StrictVersion('1.0') - + mv = version.Version('2.1') self.metadata_version = mv - return mv -def read_pkg_file(self, file): - """Reads the metadata values from a file object.""" - msg = message_from_file(file) +def rfc822_unescape(content: str) -> str: + """Reverse RFC-822 escaping by removing leading whitespaces from content.""" + lines = content.splitlines() + if len(lines) == 1: + return lines[0].lstrip() + return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:])))) + - def _read_field(name): - value = msg[name] - if value == 'UNKNOWN': - return None +def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]: + """Read Message header field.""" + value = msg[field] + if value == 'UNKNOWN': + return None + return value + + +def _read_field_unescaped_from_msg(msg: "Message", field: str) -> Optional[str]: + """Read Message header field and apply rfc822_unescape.""" + value = _read_field_from_msg(msg, field) + if value is None: return value + return rfc822_unescape(value) + - def _read_list(name): - values = msg.get_all(name, None) - if values == []: - return None - return values +def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]: + """Read Message header field and return all results as list.""" + values = msg.get_all(field, None) + if values == []: + return None + return values - self.metadata_version = StrictVersion(msg['metadata-version']) - self.name = _read_field('name') - self.version = _read_field('version') - self.description = _read_field('summary') + +def _read_payload_from_msg(msg: "Message") -> Optional[str]: + value = msg.get_payload().strip() + if value == 'UNKNOWN': + return None + return value + + +def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + self.metadata_version = version.Version(msg['metadata-version']) + self.name = _read_field_from_msg(msg, 'name') + self.version = _read_field_from_msg(msg, 'version') + self.description = _read_field_from_msg(msg, 'summary') # we are filling author only. - self.author = _read_field('author') + self.author = _read_field_from_msg(msg, 'author') self.maintainer = None - self.author_email = _read_field('author-email') + self.author_email = _read_field_from_msg(msg, 'author-email') self.maintainer_email = None - self.url = _read_field('home-page') - self.license = _read_field('license') + self.url = _read_field_from_msg(msg, 'home-page') + self.license = _read_field_unescaped_from_msg(msg, 'license') if 'download-url' in msg: - self.download_url = _read_field('download-url') + self.download_url = _read_field_from_msg(msg, 'download-url') else: self.download_url = None - self.long_description = _read_field('description') - self.description = _read_field('summary') + self.long_description = _read_field_unescaped_from_msg(msg, 'description') + if ( + self.long_description is None and + self.metadata_version >= version.Version('2.1') + ): + self.long_description = _read_payload_from_msg(msg) + self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: - self.keywords = _read_field('keywords').split(',') + self.keywords = _read_field_from_msg(msg, 'keywords').split(',') - self.platforms = _read_list('platform') - self.classifiers = _read_list('classifier') + self.platforms = _read_list_from_msg(msg, 'platform') + self.classifiers = _read_list_from_msg(msg, 'classifier') # PEP 314 - these fields only exist in 1.1 - if self.metadata_version == StrictVersion('1.1'): - self.requires = _read_list('requires') - self.provides = _read_list('provides') - self.obsoletes = _read_list('obsoletes') + if self.metadata_version == version.Version('1.1'): + self.requires = _read_list_from_msg(msg, 'requires') + self.provides = _read_list_from_msg(msg, 'provides') + self.obsoletes = _read_list_from_msg(msg, 'obsoletes') else: self.requires = None self.provides = None self.obsoletes = None + self.license_files = _read_list_from_msg(msg, 'license-file') -# Based on Python 3.5 version -def write_pkg_file(self, file): - """Write the PKG-INFO format data to a file object. + +def single_line(val): """ + Quick and dirty validation for Summary pypa/setuptools#1390. + """ + if '\n' in val: + # TODO: Replace with `raise ValueError("newlines not allowed")` + # after reviewing #2893. + warnings.warn("newlines not allowed and will break in the future") + val = val.strip().split('\n')[0] + return val + + +# Based on Python 3.5 version +def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME + """Write the PKG-INFO format data to a file object.""" version = self.get_metadata_version() - if six.PY2: - def write_field(key, value): - file.write("%s: %s\n" % (key, self._encode_field(value))) - else: - def write_field(key, value): - file.write("%s: %s\n" % (key, value)) + def write_field(key, value): + file.write("%s: %s\n" % (key, value)) write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) write_field('Version', self.get_version()) - write_field('Summary', self.get_description()) + write_field('Summary', single_line(self.get_description())) write_field('Home-page', self.get_url()) - if version < StrictVersion('1.2'): - write_field('Author', self.get_contact()) - write_field('Author-email', self.get_contact_email()) - else: - optional_fields = ( - ('Author', 'author'), - ('Author-email', 'author_email'), - ('Maintainer', 'maintainer'), - ('Maintainer-email', 'maintainer_email'), - ) - - for field, attr in optional_fields: - attr_val = getattr(self, attr) + optional_fields = ( + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) - if attr_val is not None: - write_field(field, attr_val) + for field, attr in optional_fields: + attr_val = getattr(self, attr, None) + if attr_val is not None: + write_field(field, attr_val) - write_field('License', self.get_license()) + license = rfc822_escape(self.get_license()) + write_field('License', license) if self.download_url: write_field('Download-URL', self.download_url) for project_url in self.project_urls.items(): write_field('Project-URL', '%s, %s' % project_url) - long_desc = rfc822_escape(self.get_long_description()) - write_field('Description', long_desc) - keywords = ','.join(self.get_keywords()) if keywords: write_field('Keywords', keywords) - if version >= StrictVersion('1.2'): - for platform in self.get_platforms(): - write_field('Platform', platform) - else: - self._write_list(file, 'Platform', self.get_platforms()) + for platform in self.get_platforms(): + write_field('Platform', platform) self._write_list(file, 'Classifier', self.get_classifiers()) @@ -188,14 +212,15 @@ def write_pkg_file(self, file): # PEP 566 if self.long_description_content_type: - write_field( - 'Description-Content-Type', - self.long_description_content_type - ) + write_field('Description-Content-Type', self.long_description_content_type) if self.provides_extras: for extra in self.provides_extras: write_field('Provides-Extra', extra) + self._write_list(file, 'License-File', self.license_files or []) + + file.write("\n%s\n\n" % self.get_long_description()) + sequence = tuple, list @@ -206,8 +231,7 @@ def check_importable(dist, attr, value): assert not ep.extras except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( - "%r must be importable 'module:attrs' string (got %r)" - % (attr, value) + "%r must be importable 'module:attrs' string (got %r)" % (attr, value) ) from e @@ -232,14 +256,16 @@ def check_nsp(dist, attr, value): for nsp in ns_packages: if not dist.has_contents_for(nsp): raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp ) parent, sep, child = nsp.rpartition('.') if parent and parent not in ns_packages: distutils.log.warn( "WARNING: %r is declared as a package namespace, but %r" - " is not: please correct this in setup.py", nsp, parent + " is not: please correct this in setup.py", + nsp, + parent, ) @@ -269,6 +295,13 @@ def assert_bool(dist, attr, value): raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) +def invalid_unless_false(dist, attr, value): + if not value: + warnings.warn(f"{attr} is ignored.", DistDeprecationWarning) + return + raise DistutilsSetupError(f"{attr} is invalid.") + + def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: @@ -280,23 +313,18 @@ def check_requirements(dist, attr, value): "{attr!r} must be a string or list of strings " "containing valid project/version requirement specifiers; {error}" ) - raise DistutilsSetupError( - tmpl.format(attr=attr, error=error) - ) from error + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error def check_specifier(dist, attr, value): """Verify that value is a valid version specifier""" try: packaging.specifiers.SpecifierSet(value) - except packaging.specifiers.InvalidSpecifier as error: + except (packaging.specifiers.InvalidSpecifier, AttributeError) as error: tmpl = ( - "{attr!r} must be a string " - "containing valid version specifiers; {error}" + "{attr!r} must be a string " "containing valid version specifiers; {error}" ) - raise DistutilsSetupError( - tmpl.format(attr=attr, error=error) - ) from error + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error def check_entry_points(dist, attr, value): @@ -308,7 +336,7 @@ def check_entry_points(dist, attr, value): def check_test_suite(dist, attr, value): - if not isinstance(value, six.string_types): + if not isinstance(value, str): raise DistutilsSetupError("test_suite must be a string") @@ -317,12 +345,12 @@ def check_package_data(dist, attr, value): if not isinstance(value, dict): raise DistutilsSetupError( "{!r} must be a dictionary mapping package names to lists of " - "string wildcard patterns".format(attr)) + "string wildcard patterns".format(attr) + ) for k, v in value.items(): - if not isinstance(k, six.string_types): + if not isinstance(k, str): raise DistutilsSetupError( - "keys of {!r} dict must be strings (got {!r})" - .format(attr, k) + "keys of {!r} dict must be strings (got {!r})".format(attr, k) ) assert_string_list(dist, 'values of {!r} dict'.format(attr), v) @@ -332,7 +360,8 @@ def check_packages(dist, attr, value): if not re.match(r'\w+(\.\w+)*', pkgname): distutils.log.warn( "WARNING: %r not a valid package name; please use only " - ".-separated package names in setup.py", pkgname + ".-separated package names in setup.py", + pkgname, ) @@ -392,10 +421,11 @@ class Distribution(_Distribution): """ _DISTUTILS_UNSUPPORTED_METADATA = { - 'long_description_content_type': None, + 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_files': ordered_set.OrderedSet, + 'license_file': lambda: None, + 'license_files': lambda: None, } _patched_dist = None @@ -426,27 +456,32 @@ class Distribution(_Distribution): self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) - _Distribution.__init__(self, { - k: v for k, v in attrs.items() - if k not in self._DISTUTILS_UNSUPPORTED_METADATA - }) - - # Fill-in missing metadata fields not supported by distutils. - # Note some fields may have been set by other tools (e.g. pbr) - # above; they are taken preferrentially to setup() arguments - for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): - for source in self.metadata.__dict__, attrs: - if option in source: - value = source[option] - break - else: - value = default() if default else None - setattr(self.metadata, option, value) + _Distribution.__init__( + self, + { + k: v + for k, v in attrs.items() + if k not in self._DISTUTILS_UNSUPPORTED_METADATA + }, + ) + + self._set_metadata_defaults(attrs) self.metadata.version = self._normalize_version( - self._validate_version(self.metadata.version)) + self._validate_version(self.metadata.version) + ) self._finalize_requires() + def _set_metadata_defaults(self, attrs): + """ + Fill-in missing metadata fields not supported by distutils. + Some fields may have been set by other tools (e.g. pbr). + Those fields (vars(self.metadata)) take precedence to + supplied attrs. + """ + for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): + vars(self.metadata).setdefault(option, attrs.get(option, default())) + @staticmethod def _normalize_version(version): if isinstance(version, setuptools.sic) or version is None: @@ -537,7 +572,7 @@ class Distribution(_Distribution): spec_inst_reqs = getattr(self, 'install_requires', None) or () inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) simple_reqs = filter(is_simple_req, inst_reqs) - complex_reqs = filterfalse(is_simple_req, inst_reqs) + complex_reqs = itertools.filterfalse(is_simple_req, inst_reqs) self.install_requires = list(map(str, simple_reqs)) for r in complex_reqs: @@ -554,23 +589,69 @@ class Distribution(_Distribution): req.marker = None return req - def _parse_config_files(self, filenames=None): + def _finalize_license_files(self): + """Compute names of all license files which should be included.""" + license_files: Optional[List[str]] = self.metadata.license_files + patterns: List[str] = license_files if license_files else [] + + license_file: Optional[str] = self.metadata.license_file + if license_file and license_file not in patterns: + patterns.append(license_file) + + if license_files is None and license_file is None: + # Default patterns match the ones wheel uses + # See https://wheel.readthedocs.io/en/stable/user_guide.html + # -> 'Including license files in the generated wheel file' + patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') + + self.metadata.license_files = list( + unique_everseen(self._expand_patterns(patterns)) + ) + + @staticmethod + def _expand_patterns(patterns): + """ + >>> list(Distribution._expand_patterns(['LICENSE'])) + ['LICENSE'] + >>> list(Distribution._expand_patterns(['setup.cfg', 'LIC*'])) + ['setup.cfg', 'LICENSE'] + """ + return ( + path + for pattern in patterns + for path in sorted(iglob(pattern)) + if not path.endswith('~') and os.path.isfile(path) + ) + + # FIXME: 'Distribution._parse_config_files' is too complex (14) + def _parse_config_files(self, filenames=None): # noqa: C901 """ Adapted from distutils.dist.Distribution.parse_config_files, this method provides the same functionality in subtly-improved ways. """ - from setuptools.extern.six.moves.configparser import ConfigParser + from configparser import ConfigParser # Ignore install directory options if we have a venv - if not six.PY2 and sys.prefix != sys.base_prefix: - ignore_options = [ - 'install-base', 'install-platbase', 'install-lib', - 'install-platlib', 'install-purelib', 'install-headers', - 'install-scripts', 'install-data', 'prefix', 'exec-prefix', - 'home', 'user', 'root'] - else: - ignore_options = [] + ignore_options = ( + [] + if sys.prefix == sys.base_prefix + else [ + 'install-base', + 'install-platbase', + 'install-lib', + 'install-platlib', + 'install-purelib', + 'install-headers', + 'install-scripts', + 'install-data', + 'prefix', + 'exec-prefix', + 'home', + 'user', + 'root', + ] + ) ignore_options = frozenset(ignore_options) @@ -581,62 +662,93 @@ class Distribution(_Distribution): self.announce("Distribution.parse_config_files():") parser = ConfigParser() + parser.optionxform = str for filename in filenames: with io.open(filename, encoding='utf-8') as reader: if DEBUG: self.announce(" reading {filename}".format(**locals())) - (parser.readfp if six.PY2 else parser.read_file)(reader) + parser.read_file(reader) for section in parser.sections(): options = parser.options(section) opt_dict = self.get_option_dict(section) for opt in options: - if opt != '__name__' and opt not in ignore_options: - val = self._try_str(parser.get(section, opt)) - opt = opt.replace('-', '_') - opt_dict[opt] = (filename, val) + if opt == '__name__' or opt in ignore_options: + continue + + val = parser.get(section, opt) + opt = self.warn_dash_deprecation(opt, section) + opt = self.make_option_lowercase(opt, section) + opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain # the original filenames that options come from) parser.__init__() + if 'global' not in self.command_options: + return + # If there was a "global" section in the config file, use it # to set Distribution options. - if 'global' in self.command_options: - for (opt, (src, val)) in self.command_options['global'].items(): - alias = self.negative_opt.get(opt) - try: - if alias: - setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! - setattr(self, opt, strtobool(val)) - else: - setattr(self, opt, val) - except ValueError as e: - raise DistutilsOptionError(e) from e + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + if alias: + val = not strtobool(val) + elif opt in ('verbose', 'dry_run'): # ugh! + val = strtobool(val) - @staticmethod - def _try_str(val): - """ - On Python 2, much of distutils relies on string values being of - type 'str' (bytes) and not unicode text. If the value can be safely - encoded to bytes using the default encoding, prefer that. + try: + setattr(self, alias or opt, val) + except ValueError as e: + raise DistutilsOptionError(e) from e - Why the default encoding? Because that value can be implicitly - decoded back to text if needed. + def warn_dash_deprecation(self, opt, section): + if section in ( + 'options.extras_require', + 'options.data_files', + ): + return opt + + underscore_opt = opt.replace('-', '_') + commands = distutils.command.__all__ + self._setuptools_commands() + if ( + not section.startswith('options') + and section != 'metadata' + and section not in commands + ): + return underscore_opt + + if '-' in opt: + warnings.warn( + "Usage of dash-separated '%s' will not be supported in future " + "versions. Please use the underscore name '%s' instead" + % (opt, underscore_opt) + ) + return underscore_opt - Ref #1653 - """ - if not six.PY2: - return val + def _setuptools_commands(self): try: - return val.encode() - except UnicodeEncodeError: - pass - return val + dist = pkg_resources.get_distribution('setuptools') + return list(dist.get_entry_map('distutils.commands')) + except pkg_resources.DistributionNotFound: + # during bootstrapping, distribution doesn't exist + return [] + + def make_option_lowercase(self, opt, section): + if section != 'metadata' or opt.islower(): + return opt + + lowercase_opt = opt.lower() + warnings.warn( + "Usage of uppercase key '%s' in '%s' will be deprecated in future " + "versions. Please use lowercase '%s' instead" + % (opt, section, lowercase_opt) + ) + return lowercase_opt - def _set_command_options(self, command_obj, option_dict=None): + # FIXME: 'Distribution._set_command_options' is too complex (14) + def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 """ Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to @@ -656,11 +768,9 @@ class Distribution(_Distribution): self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): if DEBUG: - self.announce(" %s = %s (from %s)" % (option, value, - source)) + self.announce(" %s = %s (from %s)" % (option, value, source)) try: - bool_opts = [translate_longopt(o) - for o in command_obj.boolean_options] + bool_opts = [translate_longopt(o) for o in command_obj.boolean_options] except AttributeError: bool_opts = [] try: @@ -669,7 +779,7 @@ class Distribution(_Distribution): neg_opt = {} try: - is_string = isinstance(value, six.string_types) + is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: @@ -679,7 +789,8 @@ class Distribution(_Distribution): else: raise DistutilsOptionError( "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + % (source, command_name, option) + ) except ValueError as e: raise DistutilsOptionError(e) from e @@ -690,9 +801,11 @@ class Distribution(_Distribution): """ self._parse_config_files(filenames=filenames) - parse_configuration(self, self.command_options, - ignore_option_errors=ignore_option_errors) + parse_configuration( + self, self.command_options, ignore_option_errors=ignore_option_errors + ) self._finalize_requires() + self._finalize_license_files() def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" @@ -716,10 +829,27 @@ class Distribution(_Distribution): def by_order(hook): return getattr(hook, 'order', 0) - eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) - for ep in sorted(eps, key=by_order): + + defined = pkg_resources.iter_entry_points(group) + filtered = itertools.filterfalse(self._removed, defined) + loaded = map(lambda e: e.load(), filtered) + for ep in sorted(loaded, key=by_order): ep(self) + @staticmethod + def _removed(ep): + """ + When removing an entry point, if metadata is loaded + from an older version of Setuptools, that removed + entry point will attempt to be loaded and will fail. + See #2765 for more details. + """ + removed = { + # removed 2021-09-05 + '2to3_doctests', + } + return ep.name in removed + def _finalize_setup_keywords(self): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self, ep.name, None) @@ -727,16 +857,6 @@ class Distribution(_Distribution): ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) - def _finalize_2to3_doctests(self): - if getattr(self, 'convert_2to3_doctests', None): - # XXX may convert to set here when we can rely on set being builtin - self.convert_2to3_doctests = [ - os.path.abspath(p) - for p in self.convert_2to3_doctests - ] - else: - self.convert_2to3_doctests = [] - def get_egg_cache_dir(self): egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): @@ -744,10 +864,14 @@ class Distribution(_Distribution): windows_support.hide_file(egg_cache_dir) readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') with open(readme_txt_filename, 'w') as f: - f.write('This directory contains eggs that were downloaded ' - 'by setuptools to build, test, and run plug-ins.\n\n') - f.write('This directory caches those eggs to prevent ' - 'repeated downloads.\n\n') + f.write( + 'This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n' + ) + f.write( + 'This directory caches those eggs to prevent ' + 'repeated downloads.\n\n' + ) f.write('However, it is safe to delete this directory.\n\n') return egg_cache_dir @@ -755,6 +879,7 @@ class Distribution(_Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" from setuptools.installer import fetch_build_egg + return fetch_build_egg(self, req) def get_command_class(self, command): @@ -814,19 +939,18 @@ class Distribution(_Distribution): pfx = package + '.' if self.packages: self.packages = [ - p for p in self.packages - if p != package and not p.startswith(pfx) + p for p in self.packages if p != package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ - p for p in self.py_modules - if p != package and not p.startswith(pfx) + p for p in self.py_modules if p != package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ - p for p in self.ext_modules + p + for p in self.ext_modules if p.name != package and not p.name.startswith(pfx) ] @@ -848,9 +972,7 @@ class Distribution(_Distribution): try: old = getattr(self, name) except AttributeError as e: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) from e + raise DistutilsSetupError("%s: No such distribution setting" % name) from e if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name + ": this setting cannot be changed via include/exclude" @@ -862,15 +984,11 @@ class Distribution(_Distribution): """Handle 'include()' for list/tuple attrs without a special handler""" if not isinstance(value, sequence): - raise DistutilsSetupError( - "%s: setting must be a list (%r)" % (name, value) - ) + raise DistutilsSetupError("%s: setting must be a list (%r)" % (name, value)) try: old = getattr(self, name) except AttributeError as e: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) from e + raise DistutilsSetupError("%s: No such distribution setting" % name) from e if old is None: setattr(self, name, value) elif not isinstance(old, sequence): @@ -923,6 +1041,7 @@ class Distribution(_Distribution): src, alias = aliases[command] del aliases[command] # ensure each alias can expand only once! import shlex + args[:1] = shlex.split(alias, True) command = args[0] @@ -1003,7 +1122,7 @@ class Distribution(_Distribution): """ import sys - if six.PY2 or self.help_commands: + if self.help_commands: return _Distribution.handle_display_options(self, option_order) # Stdout may be StringIO (e.g. in tests) @@ -1022,12 +1141,14 @@ class Distribution(_Distribution): line_buffering = sys.stdout.line_buffering sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) + sys.stdout.detach(), 'utf-8', errors, newline, line_buffering + ) try: return _Distribution.handle_display_options(self, option_order) finally: sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), encoding, errors, newline, line_buffering) + sys.stdout.detach(), encoding, errors, newline, line_buffering + ) class DistDeprecationWarning(SetuptoolsDeprecationWarning): diff --git a/venv/Lib/site-packages/setuptools/distutils_patch.py b/venv/Lib/site-packages/setuptools/distutils_patch.py deleted file mode 100644 index 33f1e7f961485586e3605a90d2dabcb173f6f8c6..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/distutils_patch.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Ensure that the local copy of distutils is preferred over stdlib. - -See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 -for more motivation. -""" - -import sys -import re -import os -import importlib -import warnings - - -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - warnings.warn( - "Distutils was imported before Setuptools. This usage is discouraged " - "and may exhibit undesirable behaviors or errors. Please use " - "Setuptools' objects directly or at least import Setuptools first.") - - -def clear_distutils(): - if 'distutils' not in sys.modules: - return - warnings.warn("Setuptools is replacing distutils.") - mods = [name for name in sys.modules if re.match(r'distutils\b', name)] - for name in mods: - del sys.modules[name] - - -def enabled(): - """ - Allow selection of distutils by environment variable. - """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') - return which == 'local' - - -def ensure_local_distutils(): - clear_distutils() - distutils = importlib.import_module('setuptools._distutils') - distutils.__name__ = 'distutils' - sys.modules['distutils'] = distutils - - # sanity check that submodules load as expected - core = importlib.import_module('distutils.core') - assert '_distutils' in core.__file__, core.__file__ - - -warn_distutils_present() -if enabled(): - ensure_local_distutils() diff --git a/venv/Lib/site-packages/setuptools/errors.py b/venv/Lib/site-packages/setuptools/errors.py index 2701747f56cc77845159f2c5fee2d0ce114259af..f4d35a630a016b74be9b18a63d89a5291d2b5f65 100644 --- a/venv/Lib/site-packages/setuptools/errors.py +++ b/venv/Lib/site-packages/setuptools/errors.py @@ -3,6 +3,7 @@ Provides exceptions used by setuptools modules. """ +from distutils import errors as _distutils_errors from distutils.errors import DistutilsError @@ -14,3 +15,26 @@ class RemovedCommandError(DistutilsError, RuntimeError): error is raised if a command exists in ``distutils`` but has been actively removed in ``setuptools``. """ + + +# Re-export errors from distutils to facilitate the migration to PEP632 + +ByteCompileError = _distutils_errors.DistutilsByteCompileError +CCompilerError = _distutils_errors.CCompilerError +ClassError = _distutils_errors.DistutilsClassError +CompileError = _distutils_errors.CompileError +ExecError = _distutils_errors.DistutilsExecError +FileError = _distutils_errors.DistutilsFileError +InternalError = _distutils_errors.DistutilsInternalError +LibError = _distutils_errors.LibError +LinkError = _distutils_errors.LinkError +ModuleError = _distutils_errors.DistutilsModuleError +OptionError = _distutils_errors.DistutilsOptionError +PlatformError = _distutils_errors.DistutilsPlatformError +PreprocessError = _distutils_errors.PreprocessError +SetupError = _distutils_errors.DistutilsSetupError +TemplateError = _distutils_errors.DistutilsTemplateError +UnknownFileError = _distutils_errors.UnknownFileError + +# The root error class in the hierarchy +BaseError = _distutils_errors.DistutilsError diff --git a/venv/Lib/site-packages/setuptools/extension.py b/venv/Lib/site-packages/setuptools/extension.py index 29468894f828128f4c36660167dd1f9e68e584be..1820722a494b1744a406e364bc3dc3aefc7dd059 100644 --- a/venv/Lib/site-packages/setuptools/extension.py +++ b/venv/Lib/site-packages/setuptools/extension.py @@ -4,8 +4,6 @@ import distutils.core import distutils.errors import distutils.extension -from setuptools.extern.six.moves import map - from .monkey import get_unpatched diff --git a/venv/Lib/site-packages/setuptools/extern/__init__.py b/venv/Lib/site-packages/setuptools/extern/__init__.py index 4e79aa17ec158711c143c24a9a5cc11e8b95ceee..baca1afabe94f3cf7a9309d8f11258a94fb19f06 100644 --- a/venv/Lib/site-packages/setuptools/extern/__init__.py +++ b/venv/Lib/site-packages/setuptools/extern/__init__.py @@ -1,3 +1,4 @@ +import importlib.util import sys @@ -20,17 +21,10 @@ class VendorImporter: yield self.vendor_pkg + '.' yield '' - def find_module(self, fullname, path=None): - """ - Return self when fullname starts with root_name and the - target module is one vendored through this importer. - """ + def _module_matches_namespace(self, fullname): + """Figure out if the target module is vendored.""" root, base, target = fullname.partition(self.root_name + '.') - if root: - return - if not any(map(target.startswith, self.vendored_names)): - return - return self + return not root and any(map(target.startswith, self.vendored_names)) def load_module(self, fullname): """ @@ -54,6 +48,19 @@ class VendorImporter: "distribution.".format(**locals()) ) + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + + def find_spec(self, fullname, path=None, target=None): + """Return a module spec for vendored names.""" + return ( + importlib.util.spec_from_loader(fullname, self) + if self._module_matches_namespace(fullname) else None + ) + def install(self): """ Install this importer into sys.meta_path if not already present. @@ -62,5 +69,5 @@ class VendorImporter: sys.meta_path.append(self) -names = 'six', 'packaging', 'pyparsing', 'ordered_set', +names = 'packaging', 'pyparsing', 'ordered_set', 'more_itertools', VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/venv/Lib/site-packages/setuptools/glob.py b/venv/Lib/site-packages/setuptools/glob.py index 9d7cbc5da68da8605d271b9314befb206b87bca6..87062b8187fa4f74a8c4edbaa60bd9a8b2d506a4 100644 --- a/venv/Lib/site-packages/setuptools/glob.py +++ b/venv/Lib/site-packages/setuptools/glob.py @@ -47,6 +47,8 @@ def iglob(pathname, recursive=False): def _iglob(pathname, recursive): dirname, basename = os.path.split(pathname) + glob_in_dir = glob2 if recursive and _isrecursive(basename) else glob1 + if not has_magic(pathname): if basename: if os.path.lexists(pathname): @@ -56,13 +58,9 @@ def _iglob(pathname, recursive): if os.path.isdir(dirname): yield pathname return + if not dirname: - if recursive and _isrecursive(basename): - for x in glob2(dirname, basename): - yield x - else: - for x in glob1(dirname, basename): - yield x + yield from glob_in_dir(dirname, basename) return # `os.path.split()` returns the argument itself as a dirname if it is a # drive or UNC path. Prevent an infinite recursion if a drive or UNC path @@ -71,12 +69,7 @@ def _iglob(pathname, recursive): dirs = _iglob(dirname, recursive) else: dirs = [dirname] - if has_magic(basename): - if recursive and _isrecursive(basename): - glob_in_dir = glob2 - else: - glob_in_dir = glob1 - else: + if not has_magic(basename): glob_in_dir = glob0 for dirname in dirs: for name in glob_in_dir(dirname, basename): diff --git a/venv/Lib/site-packages/setuptools/installer.py b/venv/Lib/site-packages/setuptools/installer.py index e5acec2726e15d7d7de652055d3bee7a9544e96a..b7096df14b4a15980ad138a3990d3e25aeb3bfe1 100644 --- a/venv/Lib/site-packages/setuptools/installer.py +++ b/venv/Lib/site-packages/setuptools/installer.py @@ -2,73 +2,34 @@ import glob import os import subprocess import sys +import tempfile +import warnings from distutils import log from distutils.errors import DistutilsError import pkg_resources -from setuptools.command.easy_install import easy_install -from setuptools.extern import six from setuptools.wheel import Wheel - -from .py31compat import TemporaryDirectory +from ._deprecation_warning import SetuptoolsDeprecationWarning def _fixup_find_links(find_links): """Ensure find-links option end-up being a list of strings.""" - if isinstance(find_links, six.string_types): + if isinstance(find_links, str): return find_links.split() assert isinstance(find_links, (tuple, list)) return find_links -def _legacy_fetch_build_egg(dist, req): - """Fetch an egg needed for building. - - Legacy path using EasyInstall. - """ - tmp_dist = dist.__class__({'script_args': ['easy_install']}) - opts = tmp_dist.get_option_dict('easy_install') - opts.clear() - opts.update( - (k, v) - for k, v in dist.get_option_dict('easy_install').items() - if k in ( - # don't use any other settings - 'find_links', 'site_dirs', 'index_url', - 'optimize', 'site_dirs', 'allow_hosts', - )) - if dist.dependency_links: - links = dist.dependency_links[:] - if 'find_links' in opts: - links = _fixup_find_links(opts['find_links'][1]) + links - opts['find_links'] = ('setup', links) - install_dir = dist.get_egg_cache_dir() - cmd = easy_install( - tmp_dist, args=["x"], install_dir=install_dir, - exclude_scripts=True, - always_copy=False, build_directory=None, editable=False, - upgrade=False, multi_version=True, no_report=True, user=False - ) - cmd.ensure_finalized() - return cmd.easy_install(req) - - -def fetch_build_egg(dist, req): +def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME """Fetch an egg needed for building. Use pip/wheel to fetch/build a wheel.""" - # Check pip is available. - try: - pkg_resources.get_distribution('pip') - except pkg_resources.DistributionNotFound: - dist.announce( - 'WARNING: The pip package is not available, falling back ' - 'to EasyInstall for handling setup_requires/test_requires; ' - 'this is deprecated and will be removed in a future version.', - log.WARN - ) - return _legacy_fetch_build_egg(dist, req) - # Warn if wheel is not. + warnings.warn( + "setuptools.installer is deprecated. Requirements should " + "be satisfied by a PEP 517 installer.", + SetuptoolsDeprecationWarning, + ) + # Warn if wheel is not available try: pkg_resources.get_distribution('wheel') except pkg_resources.DistributionNotFound: @@ -82,20 +43,17 @@ def fetch_build_egg(dist, req): if 'allow_hosts' in opts: raise DistutilsError('the `allow-hosts` option is not supported ' 'when using pip to install requirements.') - if 'PIP_QUIET' in os.environ or 'PIP_VERBOSE' in os.environ: - quiet = False - else: - quiet = True + quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ if 'PIP_INDEX_URL' in os.environ: index_url = None elif 'index_url' in opts: index_url = opts['index_url'][1] else: index_url = None - if 'find_links' in opts: - find_links = _fixup_find_links(opts['find_links'][1])[:] - else: - find_links = [] + find_links = ( + _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts + else [] + ) if dist.dependency_links: find_links.extend(dist.dependency_links) eggs_dir = os.path.realpath(dist.get_egg_cache_dir()) @@ -103,7 +61,7 @@ def fetch_build_egg(dist, req): for egg_dist in pkg_resources.find_distributions(eggs_dir): if egg_dist in req and environment.can_add(egg_dist): return egg_dist - with TemporaryDirectory() as tmpdir: + with tempfile.TemporaryDirectory() as tmpdir: cmd = [ sys.executable, '-m', 'pip', '--disable-pip-version-check', @@ -114,16 +72,12 @@ def fetch_build_egg(dist, req): cmd.append('--quiet') if index_url is not None: cmd.extend(('--index-url', index_url)) - if find_links is not None: - for link in find_links: - cmd.extend(('--find-links', link)) + for link in find_links or []: + cmd.extend(('--find-links', link)) # If requirement is a PEP 508 direct URL, directly pass # the URL to pip, as `req @ url` does not work on the # command line. - if req.url: - cmd.append(req.url) - else: - cmd.append(str(req)) + cmd.append(req.url or str(req)) try: subprocess.check_call(cmd) except subprocess.CalledProcessError as e: diff --git a/venv/Lib/site-packages/setuptools/lib2to3_ex.py b/venv/Lib/site-packages/setuptools/lib2to3_ex.py deleted file mode 100644 index 017f7285b78d490ef48741e9bc70bd2611c9b16c..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/lib2to3_ex.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Customized Mixin2to3 support: - - - adds support for converting doctests - - -This module raises an ImportError on Python 2. -""" - -import warnings -from distutils.util import Mixin2to3 as _Mixin2to3 -from distutils import log -from lib2to3.refactor import RefactoringTool, get_fixers_from_package - -import setuptools -from ._deprecation_warning import SetuptoolsDeprecationWarning - - -class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - -class Mixin2to3(_Mixin2to3): - def run_2to3(self, files, doctests=False): - # See of the distribution option has been set, otherwise check the - # setuptools default. - if self.distribution.use_2to3 is not True: - return - if not files: - return - - warnings.warn( - "2to3 support is deprecated. If the project still " - "requires Python 2 support, please migrate to " - "a single-codebase solution or employ an " - "independent conversion process.", - SetuptoolsDeprecationWarning) - log.info("Fixing " + " ".join(files)) - self.__build_fixer_names() - self.__exclude_fixers() - if doctests: - if setuptools.run_2to3_on_doctests: - r = DistutilsRefactoringTool(self.fixer_names) - r.refactor(files, write=True, doctests_only=True) - else: - _Mixin2to3.run_2to3(self, files) - - def __build_fixer_names(self): - if self.fixer_names: - return - self.fixer_names = [] - for p in setuptools.lib2to3_fixer_packages: - self.fixer_names.extend(get_fixers_from_package(p)) - if self.distribution.use_2to3_fixers is not None: - for p in self.distribution.use_2to3_fixers: - self.fixer_names.extend(get_fixers_from_package(p)) - - def __exclude_fixers(self): - excluded_fixers = getattr(self, 'exclude_fixers', []) - if self.distribution.use_2to3_exclude_fixers is not None: - excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers) - for fixer_name in excluded_fixers: - if fixer_name in self.fixer_names: - self.fixer_names.remove(fixer_name) diff --git a/venv/Lib/site-packages/setuptools/monkey.py b/venv/Lib/site-packages/setuptools/monkey.py index 3c77f8cf27f0ab1e71d64cfc114ef9d1bf72295c..fb36dc1a97a9f1f2a52c25fb6b872a7afa640be7 100644 --- a/venv/Lib/site-packages/setuptools/monkey.py +++ b/venv/Lib/site-packages/setuptools/monkey.py @@ -10,8 +10,6 @@ import functools from importlib import import_module import inspect -from setuptools.extern import six - import setuptools __all__ = [] @@ -37,7 +35,7 @@ def _get_mro(cls): def get_unpatched(item): lookup = ( - get_unpatched_class if isinstance(item, six.class_types) else + get_unpatched_class if isinstance(item, type) else get_unpatched_function if isinstance(item, types.FunctionType) else lambda item: None ) @@ -138,7 +136,7 @@ def patch_for_msvc_specialized_compiler(): msvc = import_module('setuptools.msvc') if platform.system() != 'Windows': - # Compilers only availables on Microsoft Windows + # Compilers only available on Microsoft Windows return def patch_params(mod_name, func_name): diff --git a/venv/Lib/site-packages/setuptools/msvc.py b/venv/Lib/site-packages/setuptools/msvc.py index 72383eb849142cf05018e450f537a183ba61151a..281ea1c2af6b0eb5f02ecc6d115f2d6884be74b5 100644 --- a/venv/Lib/site-packages/setuptools/msvc.py +++ b/venv/Lib/site-packages/setuptools/msvc.py @@ -24,18 +24,18 @@ from io import open from os import listdir, pathsep from os.path import join, isfile, isdir, dirname import sys +import contextlib import platform import itertools import subprocess import distutils.errors from setuptools.extern.packaging.version import LegacyVersion - -from setuptools.extern.six.moves import filterfalse +from setuptools.extern.more_itertools import unique_everseen from .monkey import get_unpatched if platform.system() == 'Windows': - from setuptools.extern.six.moves import winreg + import winreg from os import environ else: # Mock winreg and environ so the module can be imported on this platform. @@ -194,7 +194,9 @@ def _msvc14_find_vc2017(): join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-latest", "-prerelease", + "-requiresAny", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-requires", "Microsoft.VisualStudio.Workload.WDExpress", "-property", "installationPath", "-products", "*", ]).decode(encoding="mbcs", errors="strict").strip() @@ -340,7 +342,7 @@ def _augment_exception(exc, version, arch=''): if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' + tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.' message = tmpl.format(**locals()) msdownload = 'www.microsoft.com/download/details.aspx?id=%d' if version == 9.0: @@ -360,8 +362,9 @@ def _augment_exception(exc, version, arch=''): message += msdownload % 8279 elif version >= 14.0: # For VC++ 14.X Redirect user to latest Visual C++ Build Tools - message += (' Get it with "Build Tools for Visual Studio": ' - r'https://visualstudio.microsoft.com/downloads/') + message += (' Get it with "Microsoft C++ Build Tools": ' + r'https://visualstudio.microsoft.com' + r'/visual-cpp-build-tools/') exc.args = (message, ) @@ -725,28 +728,23 @@ class SystemInfo: ms = self.ri.microsoft vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) vs_vers = [] - for hkey in self.ri.HKEYS: - for key in vckeys: - try: - bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) - except (OSError, IOError): - continue - with bkey: - subkeys, values, _ = winreg.QueryInfoKey(bkey) - for i in range(values): - try: - ver = float(winreg.EnumValue(bkey, i)[0]) - if ver not in vs_vers: - vs_vers.append(ver) - except ValueError: - pass - for i in range(subkeys): - try: - ver = float(winreg.EnumKey(bkey, i)) - if ver not in vs_vers: - vs_vers.append(ver) - except ValueError: - pass + for hkey, key in itertools.product(self.ri.HKEYS, vckeys): + try: + bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) + except (OSError, IOError): + continue + with bkey: + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + with contextlib.suppress(ValueError): + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vs_vers: + vs_vers.append(ver) + for i in range(subkeys): + with contextlib.suppress(ValueError): + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vs_vers: + vs_vers.append(ver) return sorted(vs_vers) def find_programdata_vs_vers(self): @@ -926,8 +924,8 @@ class SystemInfo: """ return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib')) - @property - def WindowsSdkDir(self): + @property # noqa: C901 + def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME """ Microsoft Windows SDK directory. @@ -1803,29 +1801,5 @@ class EnvironmentInfo: if not extant_paths: msg = "%s environment variable is empty" % name.upper() raise distutils.errors.DistutilsPlatformError(msg) - unique_paths = self._unique_everseen(extant_paths) + unique_paths = unique_everseen(extant_paths) return pathsep.join(unique_paths) - - # from Python docs - @staticmethod - def _unique_everseen(iterable, key=None): - """ - List unique elements, preserving order. - Remember all elements ever seen. - - _unique_everseen('AAAABBBCCDAABBB') --> A B C D - - _unique_everseen('ABBCcAD', str.lower) --> A B C D - """ - seen = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element diff --git a/venv/Lib/site-packages/setuptools/namespaces.py b/venv/Lib/site-packages/setuptools/namespaces.py index 5f403c96d7b3df72f0d77d15c3e230f72bc96c24..44939e1c6d40539eb8173bf1527db926c5a54658 100644 --- a/venv/Lib/site-packages/setuptools/namespaces.py +++ b/venv/Lib/site-packages/setuptools/namespaces.py @@ -2,8 +2,6 @@ import os from distutils import log import itertools -from setuptools.extern.six.moves import map - flatten = itertools.chain.from_iterable @@ -72,8 +70,6 @@ class Installer: return "sys._getframe(1).f_locals['sitedir']" def _gen_nspkg_line(self, pkg): - # ensure pkg is not a unicode string under Python 2.7 - pkg = str(pkg) pth = tuple(pkg.split('.')) root = self._get_root() tmpl_lines = self._nspkg_tmpl diff --git a/venv/Lib/site-packages/setuptools/package_index.py b/venv/Lib/site-packages/setuptools/package_index.py index 1702c7c693d84471a5c9ca6b7370f278c8d54def..270e7f3c91bb1a4e21502d9450471d658eb57c49 100644 --- a/venv/Lib/site-packages/setuptools/package_index.py +++ b/venv/Lib/site-packages/setuptools/package_index.py @@ -2,32 +2,33 @@ import sys import os import re +import io import shutil import socket import base64 import hashlib import itertools import warnings +import configparser +import html +import http.client +import urllib.parse +import urllib.request +import urllib.error from functools import wraps -from setuptools.extern import six -from setuptools.extern.six.moves import urllib, http_client, configparser, map - import setuptools from pkg_resources import ( CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, Environment, find_distributions, safe_name, safe_version, - to_filename, Requirement, DEVELOP_DIST, EGG_DIST, + to_filename, Requirement, DEVELOP_DIST, EGG_DIST, parse_version, ) -from setuptools import ssl_support from distutils import log from distutils.errors import DistutilsError from fnmatch import translate -from setuptools.py27compat import get_all_headers -from setuptools.py33compat import unescape from setuptools.wheel import Wheel +from setuptools.extern.more_itertools import unique_everseen -__metaclass__ = type EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) @@ -161,7 +162,7 @@ def interpret_distro_name( # Generate alternative interpretations of a source distro name # Because some packages are ambiguous as to name/versions split # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc. - # So, we generate each possible interepretation (e.g. "adns, python-1.1.0" + # So, we generate each possible interpretation (e.g. "adns, python-1.1.0" # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice, # the spurious interpretations should be ignored, because in the event # there's also an "adns" package, the spurious "python-1.1.0" version will @@ -183,25 +184,6 @@ def interpret_distro_name( ) -# From Python 2.7 docs -def unique_everseen(iterable, key=None): - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen = set() - seen_add = seen.add - if key is None: - for element in six.moves.filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element - - def unique_values(func): """ Wrap a function returning an iterable such that the resulting iterable @@ -310,17 +292,18 @@ class PackageIndex(Environment): self.package_pages = {} self.allows = re.compile('|'.join(map(translate, hosts))).match self.to_scan = [] - use_ssl = ( - verify_ssl - and ssl_support.is_available - and (ca_bundle or ssl_support.find_ca_bundle()) - ) - if use_ssl: - self.opener = ssl_support.opener_for(ca_bundle) - else: - self.opener = urllib.request.urlopen + self.opener = urllib.request.urlopen - def process_url(self, url, retrieve=False): + def add(self, dist): + # ignore invalid versions + try: + parse_version(dist.version) + except Exception: + return + return super().add(dist) + + # FIXME: 'PackageIndex.process_url' is too complex (14) + def process_url(self, url, retrieve=False): # noqa: C901 """Evaluate a URL as a possible download, and maybe retrieve it""" if url in self.scanned_urls and not retrieve: return @@ -428,49 +411,53 @@ class PackageIndex(Environment): dist.precedence = SOURCE_DIST self.add(dist) + def _scan(self, link): + # Process a URL to see if it's for a package page + NO_MATCH_SENTINEL = None, None + if not link.startswith(self.index_url): + return NO_MATCH_SENTINEL + + parts = list(map( + urllib.parse.unquote, link[len(self.index_url):].split('/') + )) + if len(parts) != 2 or '#' in parts[1]: + return NO_MATCH_SENTINEL + + # it's a package page, sanitize and index it + pkg = safe_name(parts[0]) + ver = safe_version(parts[1]) + self.package_pages.setdefault(pkg.lower(), {})[link] = True + return to_filename(pkg), to_filename(ver) + def process_index(self, url, page): """Process the contents of a PyPI page""" - def scan(link): - # Process a URL to see if it's for a package page - if link.startswith(self.index_url): - parts = list(map( - urllib.parse.unquote, link[len(self.index_url):].split('/') - )) - if len(parts) == 2 and '#' not in parts[1]: - # it's a package page, sanitize and index it - pkg = safe_name(parts[0]) - ver = safe_version(parts[1]) - self.package_pages.setdefault(pkg.lower(), {})[link] = True - return to_filename(pkg), to_filename(ver) - return None, None - # process an index page into the package-page index for match in HREF.finditer(page): try: - scan(urllib.parse.urljoin(url, htmldecode(match.group(1)))) + self._scan(urllib.parse.urljoin(url, htmldecode(match.group(1)))) except ValueError: pass - pkg, ver = scan(url) # ensure this page is in the page index - if pkg: - # process individual package page - for new_url in find_external_links(url, page): - # Process the found URL - base, frag = egg_info_for_url(new_url) - if base.endswith('.py') and not frag: - if ver: - new_url += '#egg=%s-%s' % (pkg, ver) - else: - self.need_version_info(url) - self.scan_url(new_url) - - return PYPI_MD5.sub( - lambda m: '%s' % m.group(1, 3, 2), page - ) - else: + pkg, ver = self._scan(url) # ensure this page is in the page index + if not pkg: return "" # no sense double-scanning non-package pages + # process individual package page + for new_url in find_external_links(url, page): + # Process the found URL + base, frag = egg_info_for_url(new_url) + if base.endswith('.py') and not frag: + if ver: + new_url += '#egg=%s-%s' % (pkg, ver) + else: + self.need_version_info(url) + self.scan_url(new_url) + + return PYPI_MD5.sub( + lambda m: '%s' % m.group(1, 3, 2), page + ) + def need_version_info(self, url): self.scan_all( "Page at %s links to .py file(s) without version info; an index " @@ -591,7 +578,7 @@ class PackageIndex(Environment): spec = parse_requirement_arg(spec) return getattr(self.fetch_distribution(spec, tmpdir), 'location', None) - def fetch_distribution( + def fetch_distribution( # noqa: C901 # is too complex (14) # FIXME self, requirement, tmpdir, force_scan=False, source=False, develop_ok=False, local_index=None): """Obtain a distribution suitable for fulfilling `requirement` @@ -740,7 +727,7 @@ class PackageIndex(Environment): size = -1 if "content-length" in headers: # Some servers return multiple Content-Length headers :( - sizes = get_all_headers(headers, 'Content-Length') + sizes = headers.get_all('Content-Length') size = max(map(int, sizes)) self.reporthook(url, filename, blocknum, bs, size) with open(filename, 'wb') as tfp: @@ -762,12 +749,13 @@ class PackageIndex(Environment): def reporthook(self, url, filename, blocknum, blksize, size): pass # no-op - def open_url(self, url, warning=None): + # FIXME: + def open_url(self, url, warning=None): # noqa: C901 # is too complex (12) if url.startswith('file:'): return local_open(url) try: return open_with_auth(url, self.opener) - except (ValueError, http_client.InvalidURL) as v: + except (ValueError, http.client.InvalidURL) as v: msg = ' '.join([str(arg) for arg in v.args]) if warning: self.warn(warning, msg) @@ -781,7 +769,7 @@ class PackageIndex(Environment): else: raise DistutilsError("Download error for %s: %s" % (url, v.reason)) from v - except http_client.BadStatusLine as v: + except http.client.BadStatusLine as v: if warning: self.warn(warning, v.line) else: @@ -790,7 +778,7 @@ class PackageIndex(Environment): 'down, %s' % (url, v.line) ) from v - except (http_client.HTTPException, socket.error) as v: + except (http.client.HTTPException, socket.error) as v: if warning: self.warn(warning, v) else: @@ -940,7 +928,7 @@ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub def decode_entity(match): what = match.group(0) - return unescape(what) + return html.unescape(what) def htmldecode(text): @@ -972,8 +960,7 @@ def socket_timeout(timeout=15): def _encode_auth(auth): """ - A function compatible with Python 2.3-3.3 that will encode - auth from a URL suitable for an HTTP header. + Encode auth from a URL suitable for an HTTP header. >>> str(_encode_auth('username%3Apassword')) 'dXNlcm5hbWU6cGFzc3dvcmQ=' @@ -1056,7 +1043,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): # Double scheme does not raise on macOS as revealed by a # failing test. We would expect "nonnumeric port". Refs #20. if netloc.endswith(':'): - raise http_client.InvalidURL("nonnumeric port: ''") + raise http.client.InvalidURL("nonnumeric port: ''") if scheme in ('http', 'https'): auth, address = _splituser(netloc) @@ -1136,5 +1123,5 @@ def local_open(url): status, message, body = 404, "Path not found", "Not found" headers = {'content-type': 'text/html'} - body_stream = six.StringIO(body) + body_stream = io.StringIO(body) return urllib.error.HTTPError(url, status, message, headers, body_stream) diff --git a/venv/Lib/site-packages/setuptools/py27compat.py b/venv/Lib/site-packages/setuptools/py27compat.py deleted file mode 100644 index ba39af52b69bc561e2723957b927dae5a3669858..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/py27compat.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Compatibility Support for Python 2.7 and earlier -""" - -import sys -import platform - -from setuptools.extern import six - - -def get_all_headers(message, key): - """ - Given an HTTPMessage, return all headers matching a given key. - """ - return message.get_all(key) - - -if six.PY2: - def get_all_headers(message, key): # noqa - return message.getheaders(key) - - -linux_py2_ascii = ( - platform.system() == 'Linux' and - six.PY2 -) - -rmtree_safe = str if linux_py2_ascii else lambda x: x -"""Workaround for http://bugs.python.org/issue24672""" - - -try: - from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE - from ._imp import get_frozen_object, get_module -except ImportError: - import imp - from imp import PY_COMPILED, PY_FROZEN, PY_SOURCE # noqa - - def find_module(module, paths=None): - """Just like 'imp.find_module()', but with package support""" - parts = module.split('.') - while parts: - part = parts.pop(0) - f, path, (suffix, mode, kind) = info = imp.find_module(part, paths) - - if kind == imp.PKG_DIRECTORY: - parts = parts or ['__init__'] - paths = [path] - - elif parts: - raise ImportError("Can't find %r in %s" % (parts, module)) - - return info - - def get_frozen_object(module, paths): - return imp.get_frozen_object(module) - - def get_module(module, paths, info): - imp.load_module(module, *info) - return sys.modules[module] diff --git a/venv/Lib/site-packages/setuptools/py31compat.py b/venv/Lib/site-packages/setuptools/py31compat.py deleted file mode 100644 index e1da7ee2a2c56e46e09665d98ba1bc5bfedd2c3e..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/py31compat.py +++ /dev/null @@ -1,32 +0,0 @@ -__all__ = [] - -__metaclass__ = type - - -try: - # Python >=3.2 - from tempfile import TemporaryDirectory -except ImportError: - import shutil - import tempfile - - class TemporaryDirectory: - """ - Very simple temporary directory context manager. - Will try to delete afterward, but will also ignore OS and similar - errors on deletion. - """ - - def __init__(self, **kwargs): - self.name = None # Handle mkdtemp raising an exception - self.name = tempfile.mkdtemp(**kwargs) - - def __enter__(self): - return self.name - - def __exit__(self, exctype, excvalue, exctrace): - try: - shutil.rmtree(self.name, True) - except OSError: # removal errors are not the only possible - pass - self.name = None diff --git a/venv/Lib/site-packages/setuptools/py33compat.py b/venv/Lib/site-packages/setuptools/py33compat.py deleted file mode 100644 index cb69443638354b46b43da5bbf187b4f7cba301f1..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/py33compat.py +++ /dev/null @@ -1,59 +0,0 @@ -import dis -import array -import collections - -try: - import html -except ImportError: - html = None - -from setuptools.extern import six -from setuptools.extern.six.moves import html_parser - -__metaclass__ = type - -OpArg = collections.namedtuple('OpArg', 'opcode arg') - - -class Bytecode_compat: - def __init__(self, code): - self.code = code - - def __iter__(self): - """Yield '(op,arg)' pair for each operation in code object 'code'""" - - bytes = array.array('b', self.code.co_code) - eof = len(self.code.co_code) - - ptr = 0 - extended_arg = 0 - - while ptr < eof: - - op = bytes[ptr] - - if op >= dis.HAVE_ARGUMENT: - - arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg - ptr += 3 - - if op == dis.EXTENDED_ARG: - long_type = six.integer_types[-1] - extended_arg = arg * long_type(65536) - continue - - else: - arg = None - ptr += 1 - - yield OpArg(op, arg) - - -Bytecode = getattr(dis, 'Bytecode', Bytecode_compat) - - -unescape = getattr(html, 'unescape', None) -if unescape is None: - # HTMLParser.unescape is deprecated since Python 3.4, and will be removed - # from 3.9. - unescape = html_parser.HTMLParser().unescape diff --git a/venv/Lib/site-packages/setuptools/sandbox.py b/venv/Lib/site-packages/setuptools/sandbox.py index 93ae8eb4d94f01ce02792ac0461878f2593b36d5..034fc80d20ea4a59d77af6f808dbcfc3b87612c3 100644 --- a/venv/Lib/site-packages/setuptools/sandbox.py +++ b/venv/Lib/site-packages/setuptools/sandbox.py @@ -8,9 +8,7 @@ import re import contextlib import pickle import textwrap - -from setuptools.extern import six -from setuptools.extern.six.moves import builtins, map +import builtins import pkg_resources from distutils.errors import DistutilsError @@ -28,7 +26,10 @@ _open = open __all__ = [ - "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", + "AbstractSandbox", + "DirectorySandbox", + "SandboxViolation", + "run_setup", ] @@ -108,6 +109,7 @@ class UnpickleableException(Exception): except Exception: # get UnpickleableException inside the sandbox from setuptools.sandbox import UnpickleableException as cls + return cls.dump(cls, cls(repr(exc))) @@ -138,7 +140,7 @@ class ExceptionSaver: return type, exc = map(pickle.loads, self._saved) - six.reraise(type, exc, self._tb) + raise exc.with_traceback(self._tb) @contextlib.contextmanager @@ -156,7 +158,8 @@ def save_modules(): sys.modules.update(saved) # remove any modules imported since del_modules = ( - mod_name for mod_name in sys.modules + mod_name + for mod_name in sys.modules if mod_name not in saved # exclude any encodings modules. See #285 and not mod_name.startswith('encodings.') @@ -185,8 +188,8 @@ def setup_context(setup_dir): temp_dir = os.path.join(setup_dir, 'temp') with save_pkg_resources_state(): with save_modules(): - hide_setuptools() with save_path(): + hide_setuptools() with save_argv(): with override_temp(temp_dir): with pushd(setup_dir): @@ -195,6 +198,15 @@ def setup_context(setup_dir): yield +_MODULES_TO_HIDE = { + 'setuptools', + 'distutils', + 'pkg_resources', + 'Cython', + '_distutils_hack', +} + + def _needs_hiding(mod_name): """ >>> _needs_hiding('setuptools') @@ -212,8 +224,8 @@ def _needs_hiding(mod_name): >>> _needs_hiding('Cython') True """ - pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)') - return bool(pattern.match(mod_name)) + base_module = mod_name.split('.', 1)[0] + return base_module in _MODULES_TO_HIDE def hide_setuptools(): @@ -223,6 +235,10 @@ def hide_setuptools(): necessary to avoid issues such as #315 where setuptools upgrading itself would fail to find a function declared in the metadata. """ + _distutils_hack = sys.modules.get('_distutils_hack', None) + if _distutils_hack is not None: + _distutils_hack.remove_shim() + modules = filter(_needs_hiding, sys.modules) _clear_modules(modules) @@ -238,15 +254,8 @@ def run_setup(setup_script, args): working_set.__init__() working_set.callbacks.append(lambda dist: dist.activate()) - # __file__ should be a byte string on Python 2 (#712) - dunder_file = ( - setup_script - if isinstance(setup_script, str) else - setup_script.encode(sys.getfilesystemencoding()) - ) - with DirectorySandbox(setup_dir): - ns = dict(__file__=dunder_file, __name__='__main__') + ns = dict(__file__=setup_script, __name__='__main__') _execfile(setup_script, ns) except SystemExit as v: if v.args and v.args[0]: @@ -261,7 +270,8 @@ class AbstractSandbox: def __init__(self): self._attrs = [ - name for name in dir(_os) + name + for name in dir(_os) if not name.startswith('_') and hasattr(self, name) ] @@ -316,9 +326,25 @@ class AbstractSandbox: _file = _mk_single_path_wrapper('file', _file) _open = _mk_single_path_wrapper('open', _open) for name in [ - "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", - "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", - "startfile", "mkfifo", "mknod", "pathconf", "access" + "stat", + "listdir", + "chdir", + "open", + "chmod", + "chown", + "mkdir", + "remove", + "unlink", + "rmdir", + "utime", + "lchown", + "chroot", + "lstat", + "startfile", + "mkfifo", + "mknod", + "pathconf", + "access", ]: if hasattr(_os, name): locals()[name] = _mk_single_path_wrapper(name) @@ -369,7 +395,7 @@ class AbstractSandbox: """Called for path pairs like rename, link, and symlink operations""" return ( self._remap_input(operation + '-from', src, *args, **kw), - self._remap_input(operation + '-to', dst, *args, **kw) + self._remap_input(operation + '-to', dst, *args, **kw), ) @@ -382,28 +408,38 @@ else: class DirectorySandbox(AbstractSandbox): """Restrict operations to a single subdirectory - pseudo-chroot""" - write_ops = dict.fromkeys([ - "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", - "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", - ]) + write_ops = dict.fromkeys( + [ + "open", + "chmod", + "chown", + "mkdir", + "remove", + "unlink", + "rmdir", + "utime", + "lchown", + "chroot", + "mkfifo", + "mknod", + "tempnam", + ] + ) - _exception_patterns = [ - # Allow lib2to3 to attempt to save a pickled grammar object (#121) - r'.*lib2to3.*\.pickle$', - ] + _exception_patterns = [] "exempt writing to paths that match the pattern" def __init__(self, sandbox, exceptions=_EXCEPTIONS): self._sandbox = os.path.normcase(os.path.realpath(sandbox)) self._prefix = os.path.join(self._sandbox, '') self._exceptions = [ - os.path.normcase(os.path.realpath(path)) - for path in exceptions + os.path.normcase(os.path.realpath(path)) for path in exceptions ] AbstractSandbox.__init__(self) def _violation(self, operation, *args, **kw): from setuptools.sandbox import SandboxViolation + raise SandboxViolation(operation, args, kw) if _file: @@ -436,12 +472,10 @@ class DirectorySandbox(AbstractSandbox): def _exempted(self, filepath): start_matches = ( - filepath.startswith(exception) - for exception in self._exceptions + filepath.startswith(exception) for exception in self._exceptions ) pattern_matches = ( - re.match(pattern, filepath) - for pattern in self._exception_patterns + re.match(pattern, filepath) for pattern in self._exception_patterns ) candidates = itertools.chain(start_matches, pattern_matches) return any(candidates) @@ -466,16 +500,19 @@ class DirectorySandbox(AbstractSandbox): WRITE_FLAGS = functools.reduce( - operator.or_, [ - getattr(_os, a, 0) for a in - "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] + operator.or_, + [ + getattr(_os, a, 0) + for a in "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split() + ], ) class SandboxViolation(DistutilsError): """A setup script attempted to modify the filesystem outside the sandbox""" - tmpl = textwrap.dedent(""" + tmpl = textwrap.dedent( + """ SandboxViolation: {cmd}{args!r} {kwargs} The package setup script has attempted to modify files on your system @@ -485,7 +522,8 @@ class SandboxViolation(DistutilsError): support alternate installation locations even if you run its setup script by hand. Please inform the package's author and the EasyInstall maintainers to find out if a fix or workaround is available. - """).lstrip() + """ + ).lstrip() def __str__(self): cmd, args, kwargs = self.args diff --git a/venv/Lib/site-packages/setuptools/ssl_support.py b/venv/Lib/site-packages/setuptools/ssl_support.py deleted file mode 100644 index 17c14c4694cd714a385e30503956d4e14ad6dabe..0000000000000000000000000000000000000000 --- a/venv/Lib/site-packages/setuptools/ssl_support.py +++ /dev/null @@ -1,265 +0,0 @@ -import os -import socket -import atexit -import re -import functools - -from setuptools.extern.six.moves import urllib, http_client, map, filter - -from pkg_resources import ResolutionError, ExtractionError - -try: - import ssl -except ImportError: - ssl = None - -__all__ = [ - 'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths', - 'opener_for' -] - -cert_paths = """ -/etc/pki/tls/certs/ca-bundle.crt -/etc/ssl/certs/ca-certificates.crt -/usr/share/ssl/certs/ca-bundle.crt -/usr/local/share/certs/ca-root.crt -/etc/ssl/cert.pem -/System/Library/OpenSSL/certs/cert.pem -/usr/local/share/certs/ca-root-nss.crt -/etc/ssl/ca-bundle.pem -""".strip().split() - -try: - HTTPSHandler = urllib.request.HTTPSHandler - HTTPSConnection = http_client.HTTPSConnection -except AttributeError: - HTTPSHandler = HTTPSConnection = object - -is_available = ssl is not None and object not in ( - HTTPSHandler, HTTPSConnection) - - -try: - from ssl import CertificateError, match_hostname -except ImportError: - try: - from backports.ssl_match_hostname import CertificateError - from backports.ssl_match_hostname import match_hostname - except ImportError: - CertificateError = None - match_hostname = None - -if not CertificateError: - - class CertificateError(ValueError): - pass - - -if not match_hostname: - - def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - https://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - # Ported from python3-syntax: - # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r'.') - leftmost = parts[0] - remainder = parts[1:] - - wildcards = leftmost.count('*') - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a - # presented identifier in which the wildcard - # character comprises a label other than the - # left-most label. - if leftmost == '*': - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append('[^.]+') - elif leftmost.startswith('xn--') or hostname.startswith('xn--'): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) - return pat.match(hostname) - - def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError("empty or no certificate") - dnsnames = [] - san = cert.get('subjectAltName', ()) - for key, value in san: - if key == 'DNS': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get('subject', ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == 'commonName': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError( - "hostname %r doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) - elif len(dnsnames) == 1: - raise CertificateError( - "hostname %r doesn't match %r" - % (hostname, dnsnames[0])) - else: - raise CertificateError( - "no appropriate commonName or " - "subjectAltName fields were found") - - -class VerifyingHTTPSHandler(HTTPSHandler): - """Simple verifying handler: no auth, subclasses, timeouts, etc.""" - - def __init__(self, ca_bundle): - self.ca_bundle = ca_bundle - HTTPSHandler.__init__(self) - - def https_open(self, req): - return self.do_open( - lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), - req - ) - - -class VerifyingHTTPSConn(HTTPSConnection): - """Simple verifying connection: no auth, subclasses, timeouts, etc.""" - - def __init__(self, host, ca_bundle, **kw): - HTTPSConnection.__init__(self, host, **kw) - self.ca_bundle = ca_bundle - - def connect(self): - sock = socket.create_connection( - (self.host, self.port), getattr(self, 'source_address', None) - ) - - # Handle the socket if a (proxy) tunnel is present - if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None): - self.sock = sock - self._tunnel() - # http://bugs.python.org/issue7776: Python>=3.4.1 and >=2.7.7 - # change self.host to mean the proxy server host when tunneling is - # being used. Adapt, since we are interested in the destination - # host for the match_hostname() comparison. - actual_host = self._tunnel_host - else: - actual_host = self.host - - if hasattr(ssl, 'create_default_context'): - ctx = ssl.create_default_context(cafile=self.ca_bundle) - self.sock = ctx.wrap_socket(sock, server_hostname=actual_host) - else: - # This is for python < 2.7.9 and < 3.4? - self.sock = ssl.wrap_socket( - sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle - ) - try: - match_hostname(self.sock.getpeercert(), actual_host) - except CertificateError: - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - raise - - -def opener_for(ca_bundle=None): - """Get a urlopen() replacement that uses ca_bundle for verification""" - return urllib.request.build_opener( - VerifyingHTTPSHandler(ca_bundle or find_ca_bundle()) - ).open - - -# from jaraco.functools -def once(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not hasattr(func, 'always_returns'): - func.always_returns = func(*args, **kwargs) - return func.always_returns - return wrapper - - -@once -def get_win_certfile(): - try: - import wincertstore - except ImportError: - return None - - class CertFile(wincertstore.CertFile): - def __init__(self): - super(CertFile, self).__init__() - atexit.register(self.close) - - def close(self): - try: - super(CertFile, self).close() - except OSError: - pass - - _wincerts = CertFile() - _wincerts.addstore('CA') - _wincerts.addstore('ROOT') - return _wincerts.name - - -def find_ca_bundle(): - """Return an existing CA bundle path, or None""" - extant_cert_paths = filter(os.path.isfile, cert_paths) - return ( - get_win_certfile() - or next(extant_cert_paths, None) - or _certifi_where() - ) - - -def _certifi_where(): - try: - return __import__('certifi').where() - except (ImportError, ResolutionError, ExtractionError): - pass diff --git a/venv/Lib/site-packages/setuptools/unicode_utils.py b/venv/Lib/site-packages/setuptools/unicode_utils.py index 7c63efd20b350358ab25c079166dbb00ef49f8d2..e84e65e3e14152a2ba6e6e05d914f0e1bbef187b 100644 --- a/venv/Lib/site-packages/setuptools/unicode_utils.py +++ b/venv/Lib/site-packages/setuptools/unicode_utils.py @@ -1,12 +1,10 @@ import unicodedata import sys -from setuptools.extern import six - # HFS Plus uses decomposed UTF-8 def decompose(path): - if isinstance(path, six.text_type): + if isinstance(path, str): return unicodedata.normalize('NFD', path) try: path = path.decode('utf-8') @@ -23,7 +21,7 @@ def filesys_decode(path): NONE when no expected encoding works """ - if isinstance(path, six.text_type): + if isinstance(path, str): return path fs_enc = sys.getfilesystemencoding() or 'utf-8' diff --git a/venv/Lib/site-packages/setuptools/wheel.py b/venv/Lib/site-packages/setuptools/wheel.py index ca09bd194467b2b33b3b57dc8edd4e090b73e4b1..0be811af2c29e5ef697b63f329b882694c91c88d 100644 --- a/venv/Lib/site-packages/setuptools/wheel.py +++ b/venv/Lib/site-packages/setuptools/wheel.py @@ -14,13 +14,9 @@ import setuptools from pkg_resources import parse_version from setuptools.extern.packaging.tags import sys_tags from setuptools.extern.packaging.utils import canonicalize_name -from setuptools.extern.six import PY3 from setuptools.command.egg_info import write_requirements -__metaclass__ = type - - WHEEL_NAME = re.compile( r"""^(?P.+?)-(?P\d.*?) ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) @@ -112,7 +108,7 @@ class Wheel: def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): def get_metadata(name): with zf.open(posixpath.join(dist_info, name)) as fp: - value = fp.read().decode('utf-8') if PY3 else fp.read() + value = fp.read().decode('utf-8') return email.parser.Parser().parsestr(value) wheel_metadata = get_metadata('WHEEL') diff --git a/venv/Scripts/Activate.ps1 b/venv/Scripts/Activate.ps1 index 4cbc819fd858438215bf39fe9e63c59cbd4df0d6..2057a7e1df47ef2f2073fe3f975b87176e080921 100644 --- a/venv/Scripts/Activate.ps1 +++ b/venv/Scripts/Activate.ps1 @@ -1,51 +1,60 @@ -function global:deactivate ([switch]$NonDestructive) { - # Revert to original values - if (Test-Path function:_OLD_VIRTUAL_PROMPT) { - copy-item function:_OLD_VIRTUAL_PROMPT function:prompt - remove-item function:_OLD_VIRTUAL_PROMPT - } +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent - if (Test-Path env:_OLD_VIRTUAL_PYTHONHOME) { - copy-item env:_OLD_VIRTUAL_PYTHONHOME env:PYTHONHOME - remove-item env:_OLD_VIRTUAL_PYTHONHOME +function global:deactivate([switch] $NonDestructive) { + if (Test-Path variable:_OLD_VIRTUAL_PATH) { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global } - if (Test-Path env:_OLD_VIRTUAL_PATH) { - copy-item env:_OLD_VIRTUAL_PATH env:PATH - remove-item env:_OLD_VIRTUAL_PATH + if (Test-Path function:_old_virtual_prompt) { + $function:prompt = $function:_old_virtual_prompt + Remove-Item function:\_old_virtual_prompt } - if (Test-Path env:VIRTUAL_ENV) { - remove-item env:VIRTUAL_ENV + if ($env:VIRTUAL_ENV) { + Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue } if (!$NonDestructive) { # Self destruct! - remove-item function:deactivate + Remove-Item function:deactivate + Remove-Item function:pydoc } } +function global:pydoc { + python -m pydoc $args +} + +# unset irrelevant variables deactivate -nondestructive -$env:VIRTUAL_ENV="D:\snakeGame\venv" +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV -if (! $env:VIRTUAL_ENV_DISABLE_PROMPT) { - # Set the prompt to include the env name - # Make sure _OLD_VIRTUAL_PROMPT is global - function global:_OLD_VIRTUAL_PROMPT {""} - copy-item function:prompt function:_OLD_VIRTUAL_PROMPT - function global:prompt { - Write-Host -NoNewline -ForegroundColor Green '(venv) ' - _OLD_VIRTUAL_PROMPT - } -} +New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH -# Clear PYTHONHOME -if (Test-Path env:PYTHONHOME) { - copy-item env:PYTHONHOME env:_OLD_VIRTUAL_PYTHONHOME - remove-item env:PYTHONHOME +$env:PATH = "$env:VIRTUAL_ENV/Scripts;" + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) { + function global:_old_virtual_prompt { + "" + } + $function:_old_virtual_prompt = $function:prompt + + if ("" -ne "") { + function global:prompt { + # Add the custom prefix to the existing prompt + $previous_prompt_value = & $function:_old_virtual_prompt + ("() " + $previous_prompt_value) + } + } + else { + function global:prompt { + # Add a prefix to the current prompt, but don't discard it. + $previous_prompt_value = & $function:_old_virtual_prompt + $new_prompt_value = "($( Split-Path $env:VIRTUAL_ENV -Leaf )) " + ($new_prompt_value + $previous_prompt_value) + } + } } - -# Add the venv to the PATH -copy-item env:PATH env:_OLD_VIRTUAL_PATH -$env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH" diff --git a/venv/Scripts/activate b/venv/Scripts/activate index 48eacf14f369f8fa7930a157a334cde7a53e985d..e51d1a1eaa516d360d081f4397cdfd96bb973c56 100644 --- a/venv/Scripts/activate +++ b/venv/Scripts/activate @@ -1,34 +1,41 @@ # This file must be used with "source bin/activate" *from bash* # you cannot run it directly + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + deactivate () { + unset -f pydoc >/dev/null 2>&1 || true + # reset old environment variables - if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then - PATH="${_OLD_VIRTUAL_PATH:-}" + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" export PATH unset _OLD_VIRTUAL_PATH fi - if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then - PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" export PYTHONHOME unset _OLD_VIRTUAL_PYTHONHOME fi - # This should detect bash and zsh, which have a hash command that must - # be called to get it to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r - fi + # The hash command must be called to get it to forget past + # commands. Without forgetting past commands the $PATH changes + # we made may not be respected + hash -r 2>/dev/null - if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then - PS1="${_OLD_VIRTUAL_PS1:-}" + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" export PS1 unset _OLD_VIRTUAL_PS1 fi unset VIRTUAL_ENV - if [ ! "${1:-}" = "nondestructive" ] ; then + if [ ! "${1-}" = "nondestructive" ] ; then # Self destruct! unset -f deactivate fi @@ -37,7 +44,10 @@ deactivate () { # unset irrelevant variables deactivate nondestructive -VIRTUAL_ENV="D:\snakeGame\venv" +VIRTUAL_ENV='D:\kaiyuan\Adapted-game-snake\venv' +if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then + VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") +fi export VIRTUAL_ENV _OLD_VIRTUAL_PATH="$PATH" @@ -45,32 +55,29 @@ PATH="$VIRTUAL_ENV/Scripts:$PATH" export PATH # unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "${PYTHONHOME:-}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" unset PYTHONHOME fi -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" - if [ "x(venv) " != x ] ; then - PS1="(venv) ${PS1:-}" +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + if [ "x" != x ] ; then + PS1="() ${PS1-}" else - if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then - # special case for Aspen magic directories - # see http://www.zetadev.com/software/aspen/ - PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" - else - PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" - fi + PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}" fi export PS1 fi -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r -fi +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# The hash command must be called to get it to forget past +# commands. Without forgetting past commands the $PATH changes +# we made may not be respected +hash -r 2>/dev/null diff --git a/venv/Scripts/activate.bat b/venv/Scripts/activate.bat index abebe65e3dace47625d13d69feab547925ea9743..a83de3871c4b0b53e44c531228ad02f66bc435d7 100644 --- a/venv/Scripts/activate.bat +++ b/venv/Scripts/activate.bat @@ -1,33 +1,39 @@ @echo off -rem This file is UTF-8 encoded, so we need to update the current code page while executing it -for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( - set _OLD_CODEPAGE=%%a +set "VIRTUAL_ENV=D:\kaiyuan\Adapted-game-snake\venv" + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) else ( + if not defined PROMPT ( + set "PROMPT=$P$G" + ) + if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + set "_OLD_VIRTUAL_PROMPT=%PROMPT%" + ) ) -if defined _OLD_CODEPAGE ( - "%SystemRoot%\System32\chcp.com" 65001 > nul +if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + if "" NEQ "" ( + set "PROMPT=() %PROMPT%" + ) else ( + for %%d in ("%VIRTUAL_ENV%") do set "PROMPT=(%%~nxd) %PROMPT%" + ) ) -set VIRTUAL_ENV=D:\snakeGame\venv +REM Don't use () to avoid problems with them in %PATH% +if defined _OLD_VIRTUAL_PYTHONHOME goto ENDIFVHOME + set "_OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%" +:ENDIFVHOME -if not defined PROMPT set PROMPT=$P$G - -if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% -if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% - -set _OLD_VIRTUAL_PROMPT=%PROMPT% -set PROMPT=(venv) %PROMPT% - -if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% set PYTHONHOME= -if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% -if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% +REM if defined _OLD_VIRTUAL_PATH ( +if not defined _OLD_VIRTUAL_PATH goto ENDIFVPATH1 + set "PATH=%_OLD_VIRTUAL_PATH%" +:ENDIFVPATH1 +REM ) else ( +if defined _OLD_VIRTUAL_PATH goto ENDIFVPATH2 + set "_OLD_VIRTUAL_PATH=%PATH%" +:ENDIFVPATH2 -set PATH=%VIRTUAL_ENV%\Scripts;%PATH% - -:END -if defined _OLD_CODEPAGE ( - "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul - set _OLD_CODEPAGE= -) +set "PATH=%VIRTUAL_ENV%\Scripts;%PATH%" diff --git a/venv/Scripts/deactivate.bat b/venv/Scripts/deactivate.bat index 1205c618686fbb0e28bb9f61a782d7be790d1163..7bbc56882d9aa3b5786bea6000669145d2f44794 100644 --- a/venv/Scripts/deactivate.bat +++ b/venv/Scripts/deactivate.bat @@ -1,21 +1,19 @@ @echo off -if defined _OLD_VIRTUAL_PROMPT ( +set VIRTUAL_ENV= + +REM Don't use () to avoid problems with them in %PATH% +if not defined _OLD_VIRTUAL_PROMPT goto ENDIFVPROMPT set "PROMPT=%_OLD_VIRTUAL_PROMPT%" -) -set _OLD_VIRTUAL_PROMPT= + set _OLD_VIRTUAL_PROMPT= +:ENDIFVPROMPT -if defined _OLD_VIRTUAL_PYTHONHOME ( +if not defined _OLD_VIRTUAL_PYTHONHOME goto ENDIFVHOME set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" set _OLD_VIRTUAL_PYTHONHOME= -) +:ENDIFVHOME -if defined _OLD_VIRTUAL_PATH ( +if not defined _OLD_VIRTUAL_PATH goto ENDIFVPATH set "PATH=%_OLD_VIRTUAL_PATH%" -) - -set _OLD_VIRTUAL_PATH= - -set VIRTUAL_ENV= - -:END + set _OLD_VIRTUAL_PATH= +:ENDIFVPATH diff --git a/venv/Scripts/easy_install-3.7.exe b/venv/Scripts/easy_install-3.7.exe deleted file mode 100644 index 76693c397f27f1402097fe5acfd63990918e4172..0000000000000000000000000000000000000000 Binary files a/venv/Scripts/easy_install-3.7.exe and /dev/null differ diff --git a/venv/Scripts/easy_install.exe b/venv/Scripts/easy_install.exe deleted file mode 100644 index 76693c397f27f1402097fe5acfd63990918e4172..0000000000000000000000000000000000000000 Binary files a/venv/Scripts/easy_install.exe and /dev/null differ diff --git a/venv/Scripts/pip.exe b/venv/Scripts/pip.exe index a5de5e9ecb298b8bfda919506112321344f72f9e..279c76baedc78f71fb825a2fd147c8e2c927c8dd 100644 Binary files a/venv/Scripts/pip.exe and b/venv/Scripts/pip.exe differ diff --git a/venv/Scripts/pip3.7.exe b/venv/Scripts/pip3.7.exe deleted file mode 100644 index a5de5e9ecb298b8bfda919506112321344f72f9e..0000000000000000000000000000000000000000 Binary files a/venv/Scripts/pip3.7.exe and /dev/null differ diff --git a/venv/Scripts/pip3.exe b/venv/Scripts/pip3.exe index a5de5e9ecb298b8bfda919506112321344f72f9e..279c76baedc78f71fb825a2fd147c8e2c927c8dd 100644 Binary files a/venv/Scripts/pip3.exe and b/venv/Scripts/pip3.exe differ diff --git a/venv/Scripts/python.exe b/venv/Scripts/python.exe index 3d9ad7a79a16df5e26d2d93d943e24885ad9aefe..8655d9d5a5ce275d03737ec3dc733c7591ca4b8d 100644 Binary files a/venv/Scripts/python.exe and b/venv/Scripts/python.exe differ diff --git a/venv/Scripts/pythonw.exe b/venv/Scripts/pythonw.exe index 3300de6650251848b9914565443d9d44dab68211..b19f1fde5e2f997e341a6e7ae14897785b1ad757 100644 Binary files a/venv/Scripts/pythonw.exe and b/venv/Scripts/pythonw.exe differ diff --git a/venv/pyvenv.cfg b/venv/pyvenv.cfg index ff10fe8519f458b8dcfc1b56b25b3979d771dc36..bb09dd9f4ab002ba812d7975b06307e091173d1c 100644 --- a/venv/pyvenv.cfg +++ b/venv/pyvenv.cfg @@ -1,3 +1,6 @@ -home = C:\Users\Xuezhi Yan\AppData\Local\Programs\Python\Python37 +home = C:\Users\ccgogogo\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0 +implementation = CPython +version_info = 3.10.11.final.0 +virtualenv = 20.13.0 include-system-site-packages = false -version = 3.7.7 +version = 3.10.11