Coverage for ivatar/utils.py: 49%
85 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-26 00:11 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-26 00:11 +0000
1# -*- coding: utf-8 -*-
2"""
3Simple module providing reusable random_string function
4"""
5import random
6import string
7from io import BytesIO
8from PIL import Image, ImageDraw, ImageSequence
9from urllib.parse import urlparse
12def random_string(length=10):
13 """
14 Return some random string with default length 10
15 """
16 return "".join(
17 random.SystemRandom().choice(string.ascii_lowercase + string.digits)
18 for _ in range(length)
19 )
22def openid_variations(openid):
23 """
24 Return the various OpenID variations, ALWAYS in the same order:
25 - http w/ trailing slash
26 - http w/o trailing slash
27 - https w/ trailing slash
28 - https w/o trailing slash
29 """
31 # Make the 'base' version: http w/ trailing slash
32 if openid.startswith("https://"):
33 openid = openid.replace("https://", "http://")
34 if openid[-1] != "/":
35 openid = openid + "/"
37 # http w/o trailing slash
38 var1 = openid[0:-1]
39 var2 = openid.replace("http://", "https://")
40 var3 = var2[0:-1]
41 return (openid, var1, var2, var3)
44def mm_ng(
45 idhash, size=80, add_red=0, add_green=0, add_blue=0
46): # pylint: disable=too-many-locals
47 """
48 Return an MM (mystery man) image, based on a given hash
49 add some red, green or blue, if specified
50 """
52 # Make sure the lightest bg color we paint is e0, else
53 # we do not see the MM any more
54 if idhash[0] == "f":
55 idhash = "e0"
57 # How large is the circle?
58 circlesize = size * 0.6
60 # Coordinates for the circle
61 start_x = int(size * 0.2)
62 end_x = start_x + circlesize
63 start_y = int(size * 0.05)
64 end_y = start_y + circlesize
66 # All are the same, based on the input hash
67 # this should always result in a "gray-ish" background
68 red = idhash[0:2]
69 green = idhash[0:2]
70 blue = idhash[0:2]
72 # Add some red (i/a) and make sure it's not over 255
73 red = hex(int(red, 16) + add_red).replace("0x", "")
74 if int(red, 16) > 255:
75 red = "ff"
76 if len(red) == 1:
77 red = "0%s" % red
79 # Add some green (i/a) and make sure it's not over 255
80 green = hex(int(green, 16) + add_green).replace("0x", "")
81 if int(green, 16) > 255:
82 green = "ff"
83 if len(green) == 1:
84 green = "0%s" % green
86 # Add some blue (i/a) and make sure it's not over 255
87 blue = hex(int(blue, 16) + add_blue).replace("0x", "")
88 if int(blue, 16) > 255:
89 blue = "ff"
90 if len(blue) == 1:
91 blue = "0%s" % blue
93 # Assemable the bg color "string" in webnotation. Eg. '#d3d3d3'
94 bg_color = "#" + red + green + blue
96 # Image
97 image = Image.new("RGB", (size, size))
98 draw = ImageDraw.Draw(image)
100 # Draw background
101 draw.rectangle(((0, 0), (size, size)), fill=bg_color)
103 # Draw MMs head
104 draw.ellipse((start_x, start_y, end_x, end_y), fill="white")
106 # Draw MMs 'body'
107 draw.polygon(
108 (
109 (start_x + circlesize / 2, size / 2.5),
110 (size * 0.15, size),
111 (size - size * 0.15, size),
112 ),
113 fill="white",
114 )
116 return image
119def is_trusted_url(url, url_filters):
120 """
121 Check if a URL is valid and considered a trusted URL.
122 If the URL is malformed, returns False.
124 Based on: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/events/UrlFilter
125 """
126 (scheme, netloc, path, params, query, fragment) = urlparse(url)
128 for filter in url_filters:
129 if "schemes" in filter:
130 schemes = filter["schemes"]
132 if scheme not in schemes:
133 continue
135 if "host_equals" in filter:
136 host_equals = filter["host_equals"]
138 if netloc != host_equals:
139 continue
141 if "host_suffix" in filter:
142 host_suffix = filter["host_suffix"]
144 if not netloc.endswith(host_suffix):
145 continue
147 if "path_prefix" in filter:
148 path_prefix = filter["path_prefix"]
150 if not path.startswith(path_prefix):
151 continue
153 if "url_prefix" in filter:
154 url_prefix = filter["url_prefix"]
156 if not url.startswith(url_prefix):
157 continue
159 return True
161 return False
164def resize_animated_gif(input_pil: Image, size: list) -> BytesIO:
165 def _thumbnail_frames(image):
166 for frame in ImageSequence.Iterator(image):
167 new_frame = frame.copy()
168 new_frame.thumbnail(size)
169 yield new_frame
171 frames = list(_thumbnail_frames(input_pil))
172 output = BytesIO()
173 output_image = frames[0]
174 output_image.save(
175 output,
176 format="gif",
177 save_all=True,
178 optimize=False,
179 append_images=frames[1:],
180 disposal=input_pil.disposal_method,
181 **input_pil.info,
182 )
183 return output