A collection of useful code snippets that I have collected over time. They are mostly all written by me.
Last active
August 25, 2024 14:28
-
-
Save spikespaz/ed35f6e4d774722c156d8038106f4926 to your computer and use it in GitHub Desktop.
Python Snippets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Color: | |
def __init__(self, string=None, rgb=None, hsl=None, alpha=None): | |
arguments = iter((string, rgb, hsl)) | |
if string is not None: | |
self._from_hex(string) | |
return | |
if rgb is not None: | |
self._from_rgb(*rgb, alpha) | |
return | |
if hsl is not None: | |
self._from_hsl(*hsl, alpha) | |
return | |
def _from_hex(self, rgba): | |
rgba = rgba.lstrip("#") | |
if len(rgba) > 8: | |
raise Exception("Expected up to 8 hexadecimal characters") | |
self._red = int(rgba[0:2], 16) | |
self._green = int(rgba[2:4], 16) | |
self._blue = int(rgba[4:6], 16) | |
if len(rgba) == 8: | |
self.alpha = int(rgba[6:8], 16) | |
else: | |
self.alpha = None | |
self._rgb_changed = True | |
self._hsl_changed = False | |
def _from_rgb(self, red, green, blue, alpha=None): | |
self._red = red | |
self._green = green | |
self._blue = blue | |
self.alpha = alpha | |
self._rgb_changed = True | |
self._hsl_changed = False | |
def _from_hsl(self, hue, saturation, lightness, alpha=None): | |
self._hue = hue | |
self._saturation = saturation | |
self._lightness = lightness | |
self.alpha = alpha | |
self._rgb_changed = False | |
self._hsl_changed = True | |
def __repr__(self): | |
return self.hex() | |
def __hash__(self): | |
return hash(self.hex()) | |
def __eq__(self, other): | |
return self.__class__ is other.__class__ and self.__hash__() == other.__hash__() | |
def hex(self): | |
return "#{:02x}{:02x}{:02x}".format(self.red, self.green, self.blue) + \ | |
("" if self.alpha is None else "{:02x}".format(self.alpha)) | |
def hsl(self): | |
if self._hsl_changed: | |
raise Exception( | |
"Cannot convert from RGB when the last modified value was of HSL") | |
rp = self._red / 255 | |
gp = self._green / 255 | |
bp = self._blue / 255 | |
cmax = max(rp, gp, bp) | |
cmin = min(rp, gp, bp) | |
delta = cmax - cmin | |
if delta == 0: | |
self._hue = 0 | |
elif cmax == rp: | |
self._hue = 60 * ((gp - bp) / delta % 6) | |
elif cmax == gp: | |
self._hue = 60 * ((bp - rp) / delta + 2) | |
elif cmax == bp: | |
self._hue = 60 * ((rp - gp) / delta + 4) | |
self._lightness = (cmax + cmin) / 2 | |
if delta == 0: | |
self._saturation = 0 | |
else: | |
self._saturation = delta / (1 - abs(2 * self._lightness - 1)) | |
self._rgb_changed = False | |
return (self._hue, self._saturation, self._lightness) | |
def rgb(self): | |
if self._rgb_changed: | |
raise Exception( | |
"Cannot convert from HSL when the last modified value was of RGB") | |
c = (1 - abs(2 * self._lightness - 1)) * self._saturation | |
x = c * (1 - abs((self._hue / 60) % 2 - 1)) | |
m = self._lightness - c / 2 | |
if self._hue >= 0 and self._hue < 60: | |
(rp, gp, bp) = (c, x, 0) | |
elif self._hue >= 60 and self._hue < 120: | |
(rp, gp, bp) = (x, c, 0) | |
elif self._hue >= 120 and self._hue < 180: | |
(rp, gp, bp) = (0, c, x) | |
elif self._hue >= 180 and self._hue < 240: | |
(rp, gp, bp) = (0, x, c) | |
elif self._hue >= 240 and self._hue < 300: | |
(rp, gp, bp) = (x, 0, c) | |
elif self._hue >= 300 and self._hue < 360: | |
(rp, gp, bp) = (c, 0, x) | |
self._red = round((rp + m) * 255) | |
self._green = round((gp + m) * 255) | |
self._blue = round((bp + m) * 255) | |
self._hsl_changed = False | |
return (self._red, self._green, self._blue) | |
@property | |
def red(self): | |
if self._hsl_changed: | |
self.rgb() | |
return self._red | |
@property | |
def green(self): | |
if self._hsl_changed: | |
self.rgb() | |
return self._green | |
@property | |
def blue(self): | |
if self._hsl_changed: | |
self.rgb() | |
return self._blue | |
@red.setter | |
def red(self, red): | |
self._red = red | |
self._rgb_changed = True | |
@green.setter | |
def green(self, green): | |
self._green = green | |
self._rgb_changed = True | |
@blue.setter | |
def blue(self, blue): | |
self._blue = blue | |
self._rgb_changed = True | |
@property | |
def hue(self): | |
if self._rgb_changed: | |
self.hsl() | |
return self._hue | |
@property | |
def saturation(self): | |
if self._rgb_changed: | |
self.hsl() | |
return self._saturation | |
@property | |
def lightness(self): | |
if self._rgb_changed: | |
self.hsl() | |
return self._lightness | |
@hue.setter | |
def hue(self, hue): | |
self._hue = hue | |
self._hsl_changed = True | |
@saturation.setter | |
def saturation(self, saturation): | |
self._saturation = saturation | |
self._hsl_changed = True | |
@lightness.setter | |
def lightness(self, lightness): | |
self._lightness = lightness | |
self._hsl_changed = True | |
def __test(colors): | |
from copy import deepcopy | |
colors = [Color(color) for color in colors] | |
colors_mutated = deepcopy(colors) | |
for color in colors_mutated: | |
color.hsl() | |
color.rgb() | |
for (color_a, color_b) in zip(colors, colors_mutated): | |
if color_a != color_b: | |
raise AssertionError( | |
"Colors do not match! ({}, {})".format(color_a, color_b)) | |
print("{} == {}".format(color_a, color_b)) | |
if __name__ == "__main__": | |
colors = set("#353b48, #666666, #444852, #fcfcfc, #434343, #90939b, #353537, #2b303b, #b6b8c0, #241f31, #303440, #000000, #9398a2, #dfdfdf, #f0f1f2, #cfcfcf, #d3d8e2, #505666, #808080, #8a939f, #282b36, #afb8c6, #383838, #4dadd4, #353a48, #838383, #202229, #7a7f8a, #7a7f8b, #2e3340, #70788d, #66a1dc, #17191f, #d7d7d7, #545860, #39404d, #161a26, #be3841, #3c4049, #2f3a42, #f0f2f5, #4e4eff, #262934, #1d1f26, #404552, #353945, #383c45, #8f939d, #f7ef45, #a4aab7, #b2cdf1, #444a58, #bac3cf, #ff00ff, #f46067, #5c6070, #c7cacf, #525762, #ff0b00, #323644, #f75a61, #464646, #ecedf0, #171717, #e01b24, #1b1b1b, #797d87, #15171c, #8c919d, #4d4f52, #5b627b, #728495, #454c5c, #4080fb, #e2e2e2, #d1d3da, #c0e3ff, #3580e4, #b7c0d3, #232428, #2d323f, #6e6e6e, #dcdcdc, #b9bcc2, #cc575d, #a1a1a1, #52555e, #353a47, #7c818c, #979dac, #2f343f, #dde3e9, #828282, #c5dcf7, #001aff, #722563, #afb8c5, #222529, #8abfdd, #666a74, #f68086, #edf5fb, #4b5162, #a9acb2, #786613, #c7c7c7, #eeeff1, #2b2e37, #f70505, #292c36, #3e434f, #5c616c, #f57900, #2d303b, #f5f6f7, #5f697f, #2e3436, #808791, #f08437, #cbd2e3, #e5a50a, #eeeeee, #252932, #e7e8eb, #3e4350, #ff1111, #ef2929, #fc4138, #fcfdfd, #7a7a7a, #21242b, #bebebe, #ffffff, #252a35, #5252ff, #767b87, #535353, #3e3e3e, #aa5555, #5f6578, #c4c7cc, #383c4a, #102b68, #21252b, #f3af0b, #cfd6e6, #d7787d, #ff7a80, #fdfdfd, #398dd3, #a51d2d, #73d216, #f8f8f9, #262932, #2f343b, #2b2e39, #2d3036, #f04a50, #006098, #3f4453, #ad4242, #1b1c21, #b9bfce, #ff1616, #e5e5e5, #ed686f, #eaebed, #fbfcfc, #398cd3, #262933, #5294e2, #0000ff, #d7d8dd, #2b2f3b, #f13039, #999999, #1f1f1f, #50dbb5, #525252, #ff2121, #f27835, #91949c, #adafb5, #3b3c3e, #d3d4d8, #525d76, #434652, #cacaca, #2d323d, #f9fafb, #617c95, #ededed, #1a1a1a, #d8354a, #90949e, #313541, #a8a8a8, #dbdfe3, #cecece, #0f0f0f, #1d242a, #b8babf, #0f1116, #eef4fc, #e2e7ef, #d3dae3".split(", ")) | |
__test(colors) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ConcurrentLineWriter: | |
class LineFileIO(StringIO): | |
def __init__(self, parent, row): | |
super().__init__() | |
self.parent = parent | |
self.row = row | |
def write(self, string): | |
super().write(string) | |
self.parent._write(self.row) | |
def flush(self): | |
super().flush() | |
self.parent.file.flush() | |
def __init__(self, file=sys.stdout): | |
self.file = file | |
self.lines = [] | |
self.__pos = 0 | |
def __enter__(self): | |
return self | |
def __exit__(self, *args): | |
self.close() | |
def __move_cursor(self, row): | |
offset = abs(self.__pos - row) | |
if row < self.__pos: | |
self.file.write(f"\033[{offset}A\r") | |
elif row > self.__pos: | |
self.file.write(f"\033[{offset}B\r") | |
self.__pos = row | |
def _write(self, row): | |
self.__move_cursor(row) | |
self.file.write(self.lines[row].getvalue()) | |
def add_line(self): | |
self.lines.append(ConcurrentLineWriter.LineFileIO(self, len(self.lines))) | |
return self.lines[-1] | |
def close(self): | |
for line in self.lines: | |
line.close() | |
self.__move_cursor(len(self.lines)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def get_width(): | |
"""Get the console width (number of chars or columns).""" | |
if platform == "win32": | |
result = Popen("mode con", stdout=PIPE, shell=True).communicate()[0].decode() | |
return int(search(r"Columns:\s*(\d+)", result)[1]) | |
else: | |
return int(Popen("stty size", stdout=PIPE, shell=True).communicate()[0].decode().split()[1]) | |
def get_size(): | |
"""Get the console width and height.""" | |
if platform == "win32": | |
raise Exception("Platform not supported.") | |
else: | |
size = Popen("stty size", stdout=PIPE, shell=True).communicate()[0].decode().split()[1] | |
return int(size[1]), int(size[0]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# -*- coding: utf8 -*- | |
from PIL import Image | |
import numpy as np | |
# Standard cube map | |
# | . | T | . | . | | |
# | L | F | R | B | | |
# | . | B | . | . | | |
def make_map(im_paths, bg=0): | |
face_im = Image.open(im_paths) | |
w, h = face_im.size | |
if not w == h: | |
raise ValueError("Cube map size improper. Must be square.") | |
map_im = Image.new("RGBA", (w * 4, h * 3), bg) | |
map_im.paste(face_im, (w, 0)) | |
map_im.paste(face_im, (w, 2 * h)) | |
map_im.paste(face_im, (0, h)) | |
map_im.paste(face_im, (2 * w, h)) | |
map_im.paste(face_im, (w, h)) | |
map_im.paste(face_im, (3 * w, h)) | |
return map_im | |
def generate(map_path, bg=1): | |
map_im = Image.open(map_path) | |
if not (map_im.height == 3 / 4 * map_im.width and map_im.width % 4 == 0 and map_im.height % 3 == 0): | |
raise ValueError("Cube map size improper. Must be 4:3 scale image, width divisible by 4, and height divisible by 3.") | |
w, h = map_im.size | |
face_bounds = { | |
"top": (1/4 * w, 0, 2/4 * w, 1/3 * h), | |
"bottom": (1/4 * w, 2/3 * h, 2/4 * w, 3/3 * h), | |
"left": (0, 1/3 * h, 1/4 * w, 2/3 * h), | |
"right": (2/4 * w, 1/3 * h, 3/4 * w, 2/3 * h), | |
"front": (1/4 * w, 1/3 * h, 2/4 * w, 2/3 * h), | |
"back": (3/4 * w, 1/3 * h, 4/4 * w, 2/3 * h) | |
} | |
face_ims = {} | |
for face, box in face_bounds.items(): | |
face_ims[face] = map_im.crop(face_bounds[face]) | |
# | |
# for face in face_ims: | |
# face_ims[face].show() | |
generate("test.png") | |
make_map("texture.png").show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
from time import strftime | |
from pyfiglet import figlet_format | |
from os import system, name as os_name, popen | |
# Function to pretty print to the perfect center of a console window | |
def fullscreen_print(text, font="dotmatrix", top_pad=0): | |
size = popen("stty size", "r").read().split() # Make a tuple for the size of the console | |
size = int(size[1]), int(size[0]) # Switch them, make it W, H with ints | |
fig = figlet_format(str(text), font=font, justify="center", width=size[0]) # Get the fig formatted string | |
fig_height = len(fig.split("\n")) # Get the line length of the figlet formatted string | |
top_pad += round((size[1] - fig_height) / 2) # Calculate top padding (newlines) to put before the fig | |
clear() # Clear the screen | |
print("\n" * top_pad, fig) # Print the figlet and padding |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# -*- author: spikespaz -*- | |
from PIL import Image | |
from os import path, listdir, mkdir | |
# Define constants / settings to be used for image conversion. | |
# Shape size is thesize of each image to be generated. Pick a size that seems to be close to the average. | |
shape_size = (100, 120) | |
# Resample is the algorithm to use when downscaling. Image.LANCZOS is best for thumbnails. | |
# NEAREST, BOX, BILINEAR, HAMMING, BICUBIC, LANCZOS, NEAREST | |
resample = Image.LANCZOS | |
# raw_dir is the RELATIVE PATH to the original face images. | |
raw_dir = path.abspath("./faces_raw") | |
# snape_dir and grey_dir are relative paths to the shaped and greyscale outputs. | |
shape_dir = path.abspath("./faces_shape") | |
grey_dir = path.abspath("./faces_grey") | |
# If the shape_dir and grey_dir paths don't exist, create them. | |
try: | |
mkdir(shape_dir) | |
except FileExistsError: | |
pass | |
try: | |
mkdir(grey_dir) | |
except FileExistsError: | |
pass | |
# Helper function to join arbitrary paths and get the absolute full path. | |
def absjoin(*p): | |
return path.abspath(path.join(*p)) | |
# Helper function to rename the file to a .png file. | |
def makepng(p): | |
return path.splitext(p)[0] + ".png" | |
# Looping for each file in raw_dir. | |
for fp in listdir(raw_dir): | |
# If the file is an accepted file type (images). | |
if fp.split(".")[-1] in ("png", "jpg", "jpeg"): | |
# Open and initiate the image. | |
raw_fp = absjoin(raw_dir, fp) | |
print("Opening:", raw_fp) | |
im = Image.open(raw_fp) | |
# Create and resize the image to the shape defined above. | |
shape_fp = makepng(absjoin(shape_dir, fp)) | |
print("Saving:", shape_fp) | |
im = im.resize(shape_size, resample) | |
im.save(shape_fp, "JPEG") | |
# Convert open image to greyscale (LA) and save as a PNG format. | |
grey_fp = makepng(absjoin(grey_dir, fp)) | |
print("Saving:", grey_fp) | |
im = im.convert("LA") | |
im.save(grey_fp, "PNG") | |
# File is not an acceptable image file, skip it. | |
else: | |
print("Skipping:", absjoin(raw_dir, fp)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
from PIL import Image, ImageDraw, ImageFont | |
from os import path | |
from parser import read_config | |
def read_sentences(fp): | |
with open(fp) as f: | |
return tuple(l.strip() for l in f.readlines() if l.strip()) | |
training_settings_path = path.abspath("./store/training_settings.txt") | |
print("Training settings path:", training_settings_path) | |
def get_settings_rel(*p): | |
return path.abspath(path.join(path.dirname(training_settings_path), *p)) | |
training_settings = read_config(training_settings_path) | |
try: | |
training_settings["rotation_filter"] = eval(training_settings["rotation_filter"]) | |
except NameError: | |
print("Rotation filter invalid. Defaulting to BICUBIC.") | |
training_settings["rotation_filter"] = Image.BICUBIC | |
try: | |
training_settings["training_filter"] = eval(training_settings["training_filter"]) | |
except NameError: | |
print("Rotation filter invalid. Defaulting to BICUBIC.") | |
training_settings["training_filter"] = Image.BICUBIC | |
training_sentences_path = get_settings_rel(training_settings["training_sentences"]) | |
print("Training sentences path:", training_sentences_path) | |
training_sentences = read_sentences(training_sentences_path) | |
font_default_path = get_settings_rel(training_settings["font_default"]) | |
print("Font default path:", font_default_path) | |
def create_text_image(text, size=10, rot=None, pad=(4,) * 4, fg=(0,) * 3, bg=(255,) * 3, | |
fpath=font_default_path, | |
ttf=training_settings["font_default_ttf"]): | |
if ttf: | |
font = ImageFont.truetype(fpath, size) | |
else: | |
font = ImageFont.load(fpath) | |
im_size = font.getsize(text) | |
im_size = (im_size[0] + pad[2] + pad[3], im_size[1] + pad[0] + pad[1]) | |
im = Image.new("RGBA", im_size, bg) | |
im_draw = ImageDraw.Draw(im) | |
im_draw.text((pad[2], pad[0]), text, font=font, fill=fg) | |
if rot: | |
return im.rotate(rot, resample=training_settings["rotation_filter"], expand=True) | |
else: | |
return im | |
def get_save_path(scale, name): | |
return path.join(get_settings_rel(training_settings["training_path"], str(scale), name)) | |
counter = -1 | |
for sentence in training_sentences: | |
for size in training_settings["font_sizes"]: | |
for rot in training_settings["rotation_degrees"]: | |
counter += 1 | |
print(str(counter) + ": " + str((sentence, size, rot))) | |
text_im = create_text_image(sentence, size, rot, training_settings["font_padding"], | |
training_settings["font_foreground"], | |
training_settings["font_background"]) | |
text_im.save(get_save_path(1.0, str(counter) + ".png")) | |
im_size = (int(text_im.width * training_settings["training_scale"]), | |
int(text_im.height * training_settings["training_scale"])) | |
text_im = text_im.resize(im_size, training_settings["training_filter"]) | |
text_im.save(get_save_path(training_settings["training_scale"], str(counter) + ".png")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import string | |
import random | |
def generate_id(size=6, chars=string.ascii_lowercase + string.digits): | |
return "".join(random.choice(chars) for _ in range(size)) | |
def make_slug(title, base_id=None, size=20, allowed_chars=string.ascii_lowercase + string.digits): | |
slug = re.sub(r"[^" + allowed_chars + "]", "-", title) | |
slug = re.sub(r"-+", "", slug) | |
return (base_id if base_id else "") + slug.strip()[size:] if size else slug.strip() | |
def title_filter(title): | |
filtered = sub(r"[^\w\d~!@#$%&*()+=/,.:;\"' ]", "", title) | |
return filtered.strip()[50:] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
from sys import argv | |
from pprint import PrettyPrinter | |
from functools import wraps | |
from json import dumps | |
pp = PrettyPrinter(indent=4) | |
class Testable: | |
"""A decorator that allows functions to be tested from the terminal. Pass the function name and simple arguments | |
(`int`s, `float`s, and `str`s) into the terminal to read the direct output. Some advanced printing is available. | |
Example: | |
These are the contents of `testable.py`. | |
>>> @Testable(5, 6, 7) | |
>>> def test_function(first, second, special=1): | |
... return sum(first, second) * special | |
... | |
>>> | |
Command line examples are as follows. | |
$ python testable.py test_function | |
77 | |
$ python testable.py test_function 80 10 | |
90 | |
$ python testable.py test_function 80 10 2 | |
180 | |
Additionally, `_pretty` and `_json` can be appended before the function name in the command to get more visually | |
appealing outputs. | |
>>> @Testable() | |
>>> def test_function(): | |
... return {"one": 1, "two": 2, "three": 3, "four": 4, "five": 5} | |
... | |
>>> | |
Followed by the commands and their outputs. | |
$ python utils.py _json test_function | |
{ | |
"five": 5, | |
"one": 1, | |
"four": 4, | |
"three": 3, | |
"two": 2 | |
} | |
Replacing `_json` with `_pretty` works better for lists with really long values. | |
Attributes: | |
args (tuple): A variable length list of arguments to be tested with if none are supplied by the command line. | |
kwargs (tuple): A dictionary of keyword arguments to be tested with. | |
""" | |
def __init__(self, *args, **kwargs): | |
self.args = args | |
self.kwargs = kwargs | |
def __call__(self, func): | |
if len(argv) > 1 and argv[1] == func.__name__: | |
if len(argv) > 2: | |
parsed_args = argparse(*argv[2:]) | |
print(func(*parsed_args[0], **parsed_args[1])) | |
else: | |
print(func(*self.args, **self.kwargs)) | |
elif len(argv) > 2 and argv[2] == func.__name__: | |
if argv[1] == "_pretty": | |
def printer(output): | |
pp.pprint(output) | |
elif argv[1] == "_json": | |
def printer(output): | |
print(dumps(output, indent=4)) | |
if len(argv) > 3: | |
parsed_args = argparse(*argv[3:]) | |
printer(func(*parsed_args[0], **parsed_args[1])) | |
else: | |
printer(func(*self.args, **self.kwargs)) | |
return func | |
@Testable("90.8") | |
def parsestr(string): | |
"""Attempts to parse a string into a number. This will return the original string if failed. | |
Args: | |
string (str): The string input to parse. | |
Returns: | |
int: The integer value from the string. | |
float: The float value from the string. | |
str: The original string passed in. Returned when `int` and `float` failed. | |
""" | |
try: | |
return int(string) | |
except ValueError: | |
try: | |
return float(string) | |
except ValueError: | |
return string | |
@Testable("90.8", "-1", "10", "string", "--kwarg0", "test", "--kwarg1", "90") | |
def argparse(*args): | |
"""Parse a list of arguments (in ``sys.argv`` format) into a variable length list and a keyword arguments dictionary. | |
This is just a simpler version of the argument parser included with the Python Standard Library. | |
Arguments preceded by two dashes will be considered keys in the keyword arguments dictionary. | |
Example: | |
>>> args = ("90.8", "-1", "10", "string", "--kwarg0", "test", "--kwarg1", "90") | |
>>> argparse(args) | |
([90.8, -1, 10, 'string'], {'kwarg1': 90, 'kwarg0': 'test'}) | |
>>> | |
Args: | |
*args: Variable length argument list of strings to be parsed. | |
Returns: | |
tuple: A two-item tuple with a list of arguments and then the keyword arguments dictionary. | |
""" | |
parsed_args = [] | |
parsed_kwargs = {} | |
iterable = iter(enumerate(args)) | |
for arg in iterable: | |
value = arg[1].strip() | |
if arg[1].startswith("--"): | |
parsed_kwargs[value[2:]] = parsestr(args[arg[0] + 1]) | |
next(iterable) | |
else: | |
parsed_args.append(parsestr(value)) | |
return parsed_args, parsed_kwargs | |
def return_yield(func): | |
"""Decorator that wraps a generator and returns its value in a list as if it were a normal function. | |
Example: | |
Test function definition **without** the ``@return_yield`` applied, returns a generator. | |
>>> def test_generator(loops): | |
... for i in range(loops): | |
... yield str(i) | |
... | |
>>> test_generator(10) | |
<generator object test_generator at 0x00000215B2FC00A0> | |
>>> | |
Now, **with** ``@return_yield``. | |
>>> @return_yield | |
>>> def test_generator(loops): | |
... for i in range(loops): | |
... yield str(i) | |
... | |
>>> test_generator(10) | |
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] | |
>>> | |
Args: | |
func: Function to wrap, applied as a decorator with no parenthesis. | |
Returns: | |
func: Returns the original function as a non-generator, and returns values immediately. | |
""" | |
@wraps(func) | |
def wrapper(*args, **kwargs): | |
return list(func(*args, **kwargs)) | |
return wrapper | |
@Testable() | |
def clamp(value, imin=0, imax=1): | |
"""Return a clamped number between two numbers. | |
Args: | |
value (int, float, double): The value to clamp. | |
imin (int, float, double): The lowest allowed value. | |
imax (int, float, double): The highest allowed value. | |
Returns: | |
value: The clamped value between `imin` and `imax`. | |
""" | |
if value < imin: | |
value = imin | |
elif value > imax: | |
value = imax | |
return value |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class WebDriverThreadPoolExecutor(ThreadPoolExecutor): | |
def __init__(self, *args, driver_class, driver_options=None, logger=LOGGER_NAME, **kwargs): | |
self._logger = logging.getLogger(logger) | |
self._drivers = SimpleQueue() | |
for number in range(kwargs["max_workers"]): | |
self._logger.debug( | |
f"Spawning driver '{driver_class.__name__}' number {number}/{kwargs['max_workers']}") | |
self._drivers.put(driver_class(options=driver_options)) | |
super().__init__(*args, **kwargs) | |
def submit(self, fn, /, *args, **kwargs): | |
self._logger.debug( | |
f"Submitting task '{fn.__qualname__}' with args {args} and kwargs {kwargs}") | |
def _fn(*args, **kwargs): | |
driver = self._drivers.get(block=True) | |
result = fn(driver, *args, **kwargs) | |
if self._shutdown: | |
driver.close() | |
self._drivers.put(driver) | |
return result | |
return super().submit(_fn, *args, **kwargs) | |
def shutdown(self, *args, **kwargs): | |
self._logger.debug("Shutting down thread pool") | |
super().shutdown(*args, **kwargs) | |
while not self._drivers.empty(): | |
self._drivers.get().close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
import math | |
def pprint(*l): | |
"""Pretty print iterables. | |
A simpler alternative for the python standard pprint.PrettyPrint() object. | |
> # Pretty Print iterables. | |
:param l: Objects implementing `__iter__()`. | |
:type l: `list_iterator` | |
""" | |
for i in l: | |
if type(i) is str: | |
print(i) | |
else: | |
try: | |
print("[" + "\n ".join([str(j) for j in i]) + "]") | |
except TypeError: | |
print(i) | |
def flatten(*l): | |
"""Return a flattened list. | |
*Support for 2 dimensions ONLY.* | |
> # Flatten a 2D list. | |
:param l: Objects implementing `__iter__()`. | |
:type l: `list_iterator` | |
:return: `list` | |
""" | |
l = l[0] if len(l) > 1 else l | |
return [i for s in l for i in s] | |
def row_major(l, *xy): | |
"""Get the position of an orered pair as if the grid was flattened. | |
> # Return the flattened position of an item by ordered pair. | |
:param l: A 2D grid of objects implementing `__iter__()` | |
:param xy: An ordered pair. | |
:type l: `list_iterator` | |
:type xy: `list`, `tuple` | |
:return: `int` | |
:raises: `ValueError` | |
""" | |
x, y = xy[0] if len(xy) == 1 else xy | |
if validate(l): | |
return len(l) * x + y | |
else: | |
ValueError("Grid width is not constant. All second level iterables must be equal length.") | |
def flat_len(*l): | |
"""Get the flattened length of a list. | |
> # Return how many cells are in a 2D list. | |
:param l: A list or tuple-like object to be flattened. | |
:type l: `tuple`, `list` | |
:return: `int` | |
:raises: `ValueError` | |
""" | |
l = l[0] if len(l) > 1 else l | |
if validate(l): | |
return len(l[0]) * len(l) | |
else: | |
ValueError("Grid width is not constant. All second level iterables must be equal length.") | |
def grid_print(g, h=0.5): | |
"""Pretty print a 2D iterable, and justify the space between. | |
> # Pretty Print a grid. | |
:param g: Grid. Must have sublists. (2D) | |
:param h: The width of the console text. Usually half of the height. | |
:type g: `list_iterator` | |
:type h: `float` | |
""" | |
widest = len(str(max(flatten(g), key=lambda x: len(str(x))))) | |
for i in g: | |
for j in i: | |
print(" " + str(j) + " " * (widest - len(str(j))), end="") | |
if i == g: | |
print() | |
else: | |
print("\n" * math.floor(widest * h)) | |
def gen_grid(*wh): | |
"""Generates a simple grid with items in XX:YY format. | |
> # Generate a grid by W:H. | |
:param wh: Width, height. | |
:type wh: `list`, `tuple` | |
:return: `list` | |
""" | |
w, h = wh[0] if len(wh) == 1 else wh | |
return [[str(y) + ":" + str(x) for y in range(h)] for x in range(w)] | |
def validate(*l): | |
"""Returns True if all of the sublists in a grid are the same length. | |
> # Return true if all sublists are the same length. | |
:param l: A grid to validate. | |
:type l: `list`, `tuple` | |
:return: `True`, `False` | |
""" | |
l = l[0] if len(l) > 1 else l | |
return all(len(i) == len(l[0]) for i in l) | |
def repair(l, fill=0): | |
"""Repairs a grid (for example, it does not pass `concepts.validate()`) if it isn't a perfect shape. | |
> # If not all sublists are equal length, fill the others with 0's until it matches a constant width. | |
:param l: Grid to repair. | |
:param fill: Value to fill missing spaces with. | |
:type l: `list`, `tuple` | |
:type fill: `object` | |
:return: `list`, `tuple` | |
""" | |
width = len(max(l, key=len)) | |
for i in l: | |
i.extend([fill] * (width - len(i))) | |
return l | |
def clamp(v, min_v, max_v): | |
"""Clamp a value between a minimum and maximum value. | |
> # Clamp a value between a min and a max. | |
:param v: Value to clamp. | |
:param min_v: Minimum allowed value. | |
:param max_v: Maximum allowed value. | |
:type v: `int`, `float`, `double` | |
:type min_v: `int`, `float`, `double` | |
:type max_v: `int`, `float`, `double` | |
:return: `type(v)` | |
""" | |
return max(min(v, max_v), min_v) | |
# Class for easy coordinate access. Behaves like a tuple. | |
class XY: | |
__hash__ = tuple.__hash__ | |
def __init__(self, *xy): | |
x, y = xy[0] if len(xy) == 1 else xy | |
self.x = x | |
self.y = y | |
def __iter__(self): | |
return iter((self.x, self.y)) | |
# Class to make a window of specified size, that can slide to give smaller sections of a large grid. | |
class Window: | |
def __init__(self, grid, size=(3, 3), pos=(0, 0)): | |
if validate(grid): | |
self.width, self.height = size | |
self.pos = list(pos) | |
self.grid = grid | |
self.margin = (math.floor(size[0] / 2), | |
math.floor(size[1] / 2)) | |
else: | |
raise ValueError("Grid width is not constant. All second level iterables must be equal length.") | |
# Return maximum and minimum array bounds, plus half the width of the window. | |
def bounds(self): | |
return (-self.margin[0], -self.margin[1], | |
self.grid_size()[0] - self.margin[0] - 1, | |
self.grid_size()[1] - self.margin[1] - 1) | |
# Getter for window W, H | |
def size(self): | |
return self.width, self.height | |
# Getter for grid W, H | |
def grid_size(self): | |
return len(self.grid[0]), len(self.grid) | |
# Slide the window along the X and Y axis. | |
def slide(self, *xy): | |
x, y = xy[0] if len(xy) == 1 else xy | |
bounds = self.bounds() | |
self.pos[0] = clamp(self.pos[0] + x, bounds[0], bounds[2]) | |
self.pos[1] = clamp(self.pos[1] + y, bounds[1], bounds[3]) | |
# Get item by index relative to window. | |
def get_rel(self, *xy): | |
x, y = xy[0] if len(xy) == 1 else xy | |
return self.grid[self.pos[1] + y][self.pos[0] + x] | |
# Set item by index relative to window. | |
def set_rel(self, v, *xy): | |
x, y = xy[0] if len(xy) == 1 else xy | |
self.grid[self.pos[1] + y][self.pos[0] + x] = v | |
# Set all the data in a complete flattened window form. | |
def set(self, *data): | |
size = self.width * self.height | |
data = data[0] if len(data) == 1 else data | |
if len(data) == size: | |
pass | |
elif len(data) == self.height: | |
data = flatten(data) | |
else: | |
raise ValueError("Data does not appear to be proper size. Must match flattened window length.") | |
for x in range(self.height): | |
for y in range(self.width): | |
self.set_rel(data[self.height * x + y], x, y) | |
# Getter by index. | |
def __getitem__(self, k): | |
if type(k) == tuple: | |
return self.get_rel(k) | |
else: | |
return self.grid[self.pos[1] + k] | |
# Setter by index. | |
def __setitem__(self, k, val): | |
if type(k) == tuple: | |
self.set_rel(val, k) | |
else: | |
self.grid[self.pos[1] + k] | |
# Define behavior for being iterated. Returns a 2D list (subgrid). | |
def __iter__(self): | |
window = [[] for _ in range(self.height)] | |
for x in range(self.width): | |
for y in range(self.height): | |
pos = self.pos[0] + x, self.pos[1] + y | |
# Conditional to check if the index is out of negative bounds. | |
if pos[0] < 0 or pos[1] < 0: | |
window[y].append(None) | |
else: | |
try: | |
window[y].append(self.grid[pos[1]][pos[0]]) | |
except IndexError: | |
window[y].append(None) | |
return iter(window) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from concepts import * | |
from msvcrt import getch | |
from os import system | |
from time import sleep | |
# grid_size = input("Grid Size (WW, HH): ").split(",") | |
# grid_size = int(grid_size[0]), int(grid_size[1]) | |
grid_size = 100, 100 | |
# window_size = input("Window Size (WW, HH): ").split(",") | |
# window_size = int(window_size[0]), int(window_size[1]) | |
window_size = 5, 5 | |
# window_pos = input("Window Position (XX, YY): ").split(",") | |
# window_pos = int(window_pos[0]), int(window_pos[1]) | |
window_pos = 0, 0 | |
grid = gen_grid(*grid_size) | |
window = Window(grid, window_size, window_pos) | |
while True: | |
system("cls") | |
print("\n", window.pos, "\n") | |
grid_print(window) | |
# window_slide = input("\nWindow Slide (XX, YY): ").split(",") | |
# window_slide = int(window_slide[0]), int(window_slide[1]) | |
cmd = getch(), getch() | |
if cmd[0] == b"t": | |
window_slide = input("\nWindow Slide (XX, YY): ").split(",") | |
window_slide = int(window_slide[0]), int(window_slide[1]) | |
window.slide(*window_slide) | |
elif not cmd[0] == b"\xe0": | |
pass | |
elif cmd[1] == b"H": | |
window.slide(0, -1) | |
elif cmd[1] == b"P": | |
window.slide(0, 1) | |
elif cmd[1] == b"K": | |
window.slide(-1, 0) | |
elif cmd[1] == b"M": | |
window.slide(1, 0) | |
sleep(0.02) | |
# window.slide(*window_slide) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@contextlib.contextmanager | |
def work_directory(path): | |
try: | |
cwd = os.getcwd() | |
os.chdir(path) | |
yield Path(path) | |
finally: | |
os.chdir(cwd) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment