From 2c6a93de881520cc3c34d43cd6da022e8b8b38ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 16:52:55 +0100 Subject: [PATCH 01/11] Wow format and isort --- src/doge/core.py | 179 ++++++++------- src/doge/wow.py | 553 +++++++++++++++++++++-------------------------- 2 files changed, 333 insertions(+), 399 deletions(-) diff --git a/src/doge/core.py b/src/doge/core.py index 9455674..22581d3 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -1,22 +1,22 @@ #!/usr/bin/env python # coding: utf-8 +import argparse import datetime import os -import sys -import re import random +import re import struct -import traceback -import argparse import subprocess as sp +import sys +import traceback import unicodedata from importlib.resources import files from doge import wow -ROOT = files('doge').joinpath('static') -DEFAULT_DOGE = 'doge.txt' +ROOT = files("doge").joinpath("static") +DEFAULT_DOGE = "doge.txt" class Doge(object): @@ -26,8 +26,7 @@ def __init__(self, tty, ns): self.doge_path = ROOT.joinpath(ns.doge_path or DEFAULT_DOGE) if ns.frequency: # such frequency based - self.words = \ - wow.FrequencyBasedDogeDeque(*wow.WORD_LIST, step=ns.step) + self.words = wow.FrequencyBasedDogeDeque(*wow.WORD_LIST, step=ns.step) else: self.words = wow.DogeDeque(*wow.WORD_LIST) @@ -45,27 +44,27 @@ def setup(self): max_doge = 15 if self.ns.density > 100: - sys.stderr.write('wow, density such over 100%, too high\n') + sys.stderr.write("wow, density such over 100%, too high\n") sys.exit(1) if self.ns.density < 0: - sys.stderr.write('wow, density such negative, too low\n') + sys.stderr.write("wow, density such negative, too low\n") sys.exit(1) if self.tty.width < max_doge: # Shibe won't fit, so abort. - sys.stderr.write('wow, such small terminal\n') - sys.stderr.write('no doge under {0} column\n'.format(max_doge)) + sys.stderr.write("wow, such small terminal\n") + sys.stderr.write("no doge under {0} column\n".format(max_doge)) return False # Check for prompt height so that we can fill the screen minus how high # the prompt will be when done. - prompt = os.environ.get('PS1', '').split('\n') + prompt = os.environ.get("PS1", "").split("\n") line_count = len(prompt) + 1 # Create a list filled with empty lines and Shibe at the bottom. fill = range(self.tty.height - len(doge) - line_count) - self.lines = ['\n' for x in fill] + self.lines = ["\n" for x in fill] self.lines += doge # Try to fetch data fed thru stdin @@ -101,24 +100,25 @@ def setup_seasonal(self): now = datetime.datetime.now() for season, data in wow.SEASONS.items(): - start, end = data['dates'] + start, end = data["dates"] start_dt = datetime.datetime(now.year, start[0], start[1]) # Be sane if the holiday season spans over New Year's day. end_dt = datetime.datetime( - now.year + (start[0] > end[0] and 1 or 0), end[0], end[1]) + now.year + (start[0] > end[0] and 1 or 0), end[0], end[1] + ) if start_dt <= now <= end_dt: # Wow, much holiday! return self.load_season(season) def load_season(self, season_key): - if season_key == 'none': + if season_key == "none": return season = wow.SEASONS[season_key] - self.doge_path = ROOT.joinpath(season['pic']) - self.words.extend(season['words']) + self.doge_path = ROOT.joinpath(season["pic"]) + self.words.extend(season["words"]) def apply_text(self): """ @@ -133,17 +133,19 @@ def apply_text(self): if self.ns.density == 0: return - affected = sorted(random.sample(range(linelen), int(linelen * (self.ns.density / 100)))) + affected = sorted( + random.sample(range(linelen), int(linelen * (self.ns.density / 100))) + ) for i, target in enumerate(affected, start=1): line = self.lines[target] - line = re.sub('\n', ' ', line) + line = re.sub("\n", " ", line) word = self.words.get() # If first or last line, or a random selection, use standalone wow. if i == 1 or i == len(affected) or random.choice(range(20)) == 0: - word = 'wow' + word = "wow" # Generate a new DogeMessage, possibly based on a word. self.lines[target] = DogeMessage(self, line, word).generate() @@ -157,9 +159,9 @@ def load_doge(self): """ if self.ns.no_shibe: - return [''] + return [""] - return self.doge_path.read_text(encoding='utf-8').splitlines(keepends=True) + return self.doge_path.read_text(encoding="utf-8").splitlines(keepends=True) def get_real_data(self): """ @@ -168,24 +170,24 @@ def get_real_data(self): """ ret = [] - username = os.environ.get('USER') + username = os.environ.get("USER") if username: ret.append(username) - editor = os.environ.get('EDITOR') + editor = os.environ.get("EDITOR") if editor: - editor = editor.split('/')[-1] + editor = editor.split("/")[-1] ret.append(editor) # OS, hostname and... architechture (because lel) - if hasattr(os, 'uname'): + if hasattr(os, "uname"): uname = os.uname() ret.append(uname[0]) ret.append(uname[1]) ret.append(uname[4]) # Grab actual files from $HOME. - filenames = os.listdir(os.environ.get('HOME')) + filenames = os.listdir(os.environ.get("HOME")) if filenames: ret.append(random.choice(filenames)) @@ -196,8 +198,9 @@ def get_real_data(self): self.words.extend(map(str.lower, ret)) def filter_words(self, words, stopwords, min_length): - return [word for word in words if - len(word) >= min_length and word not in stopwords] + return [ + word for word in words if len(word) >= min_length and word not in stopwords + ] def get_stdin_data(self): """ @@ -215,13 +218,15 @@ def get_stdin_data(self): # If we have stdin data, we should remove everything else! self.words.clear() - word_list = [match.group(0) - for line in stdin_lines - for match in rx_word.finditer(line.lower())] + word_list = [ + match.group(0) + for line in stdin_lines + for match in rx_word.finditer(line.lower()) + ] if self.ns.filter_stopwords: word_list = self.filter_words( - word_list, stopwords=wow.STOPWORDS, - min_length=self.ns.min_length) + word_list, stopwords=wow.STOPWORDS, min_length=self.ns.min_length + ) self.words.extend(word_list) @@ -237,15 +242,15 @@ def get_processes(self): try: # POSIX ps, so it should work in most environments where doge would - p = sp.Popen(['ps', '-A', '-o', 'comm='], stdout=sp.PIPE) + p = sp.Popen(["ps", "-A", "-o", "comm="], stdout=sp.PIPE) output, error = p.communicate() - output = output.decode('utf-8') + output = output.decode("utf-8") - for comm in output.split('\n'): - name = comm.split('/')[-1] + for comm in output.split("\n"): + name = comm.split("/")[-1] # Filter short and weird ones - if name and len(name) >= 2 and ':' not in name: + if name and len(name) >= 2 and ":" not in name: procs.add(name) finally: @@ -273,16 +278,16 @@ def __init__(self, doge, occupied, word): self.word = word def generate(self): - if self.word == 'wow': + if self.word == "wow": # Standalone wow. Don't apply any prefixes or suffixes. msg = self.word else: # Add a prefix. - msg = u'{0} {1}'.format(wow.PREFIXES.get(), self.word) + msg = "{0} {1}".format(wow.PREFIXES.get(), self.word) # Seldomly add a suffix as well. if random.choice(range(15)) == 0: - msg += u' {0}'.format(wow.SUFFIXES.get()) + msg += " {0}".format(wow.SUFFIXES.get()) # Calculate the maximum possible spacer interval = self.tty.width - onscreen_len(msg) @@ -296,16 +301,16 @@ def generate(self): return self.occupied + "\n" # Apply spacing - msg = u'{0}{1}'.format(' ' * random.choice(range(interval)), msg) + msg = "{0}{1}".format(" " * random.choice(range(interval)), msg) if self.tty.pretty: # Apply pretty ANSI color coding. - msg = u'\x1b[1m\x1b[38;5;{0}m{1}\x1b[39m\x1b[0m'.format( + msg = "\x1b[1m\x1b[38;5;{0}m{1}\x1b[39m\x1b[0m".format( wow.COLORS.get(), msg ) # Line ends are pretty cool guys, add one of those. - return u'{0}{1}\n'.format(self.occupied, msg) + return "{0}{1}\n".format(self.occupied, msg) class TTYHandler(object): @@ -315,18 +320,18 @@ def setup(self): self.out_is_tty = sys.stdout.isatty() self.pretty = self.out_is_tty - if sys.platform == 'win32' and os.getenv('TERM') == 'xterm': + if sys.platform == "win32" and os.getenv("TERM") == "xterm": self.pretty = True def _tty_size_windows(self, handle): try: - from ctypes import windll, create_string_buffer + from ctypes import create_string_buffer, windll h = windll.kernel32.GetStdHandle(handle) buf = create_string_buffer(22) if windll.kernel32.GetConsoleScreenBufferInfo(h, buf): - left, top, right, bottom = struct.unpack('4H', buf.raw[10:18]) + left, top, right, bottom = struct.unpack("4H", buf.raw[10:18]) return right - left + 1, bottom - top + 1 except: pass @@ -337,10 +342,7 @@ def _tty_size_linux(self, fd): import termios return struct.unpack( - 'hh', - fcntl.ioctl( - fd, termios.TIOCGWINSZ, struct.pack('hh', 0, 0) - ) + "hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, struct.pack("hh", 0, 0)) ) except: return @@ -355,7 +357,7 @@ def get_tty_size(self): does. """ - if sys.platform == 'win32': + if sys.platform == "win32": # stdin, stdout, stderr = -10, -11, -12 ret = self._tty_size_windows(-10) ret = ret or self._tty_size_windows(-11) @@ -375,7 +377,7 @@ def clean_len(s): """ - s = re.sub(r'\x1b\[[0-9;]*m', '', s) + s = re.sub(r"\x1b\[[0-9;]*m", "", s) return len(s) @@ -389,75 +391,70 @@ def onscreen_len(s): length = 0 for ch in s: - length += 2 if unicodedata.east_asian_width(ch) == 'W' else 1 + length += 2 if unicodedata.east_asian_width(ch) == "W" else 1 return length def setup_arguments(): - parser = argparse.ArgumentParser('doge') + parser = argparse.ArgumentParser("doge") parser.add_argument( - '--shibe', - help='wow shibe file', - dest='doge_path', - choices=[file.name for file in ROOT.iterdir()] + "--shibe", + help="wow shibe file", + dest="doge_path", + choices=[file.name for file in ROOT.iterdir()], ) - parser.add_argument( - '--no-shibe', - action="store_true", - help="wow no doge show :(" - ) + parser.add_argument("--no-shibe", action="store_true", help="wow no doge show :(") parser.add_argument( - '--season', - help='wow shibe season congrate', - choices=sorted(wow.SEASONS.keys()) + ['none'] + "--season", + help="wow shibe season congrate", + choices=sorted(wow.SEASONS.keys()) + ["none"], ) parser.add_argument( - '-f', '--frequency', - help='such frequency based', - action='store_true' + "-f", "--frequency", help="such frequency based", action="store_true" ) parser.add_argument( - '--step', - help='beautiful step', # how much to step + "--step", + help="beautiful step", # how much to step # between ranks in FrequencyBasedDogeDeque type=int, default=2, ) parser.add_argument( - '--min_length', - help='pretty minimum', # minimum length of a word + "--min_length", + help="pretty minimum", # minimum length of a word type=int, default=1, ) parser.add_argument( - '-s', '--filter_stopwords', - help='many words lol', - action='store_true' + "-s", "--filter_stopwords", help="many words lol", action="store_true" ) parser.add_argument( - '-mh', '--max-height', - help='such max height', + "-mh", + "--max-height", + help="such max height", type=int, ) parser.add_argument( - '-mw', '--max-width', - help='such max width', + "-mw", + "--max-width", + help="such max width", type=int, ) parser.add_argument( - '-d', '--density', - help='such word density percent, max is 100, default is 30, wow', + "-d", + "--density", + help="such word density percent, max is 100, default is 30, wow", type=float, default=30, ) @@ -489,15 +486,15 @@ def main(): traceback.print_exc() print() - lang = os.environ.get('LANG') + lang = os.environ.get("LANG") if not lang: - print('wow error: broken $LANG, so fail') + print("wow error: broken $LANG, so fail") return 3 - if not lang.endswith('UTF-8'): + if not lang.endswith("UTF-8"): print( - "wow error: locale '{0}' is not UTF-8. ".format(lang) + - "doge needs UTF-8 to print Shibe. Please set your system to " + "wow error: locale '{0}' is not UTF-8. ".format(lang) + + "doge needs UTF-8 to print Shibe. Please set your system to " "use a UTF-8 locale." ) return 2 diff --git a/src/doge/wow.py b/src/doge/wow.py index 0340bbf..c6b01df 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -80,7 +80,7 @@ def __init__(self, *args, **kwargs): self.step = 2 args = list(args) # sort words by frequency - args = (sorted(set(args), key=lambda x: args.count(x))) + args = sorted(set(args), key=lambda x: args.count(x)) super(FrequencyBasedDogeDeque, self).__init__(args) def shuffle(self): @@ -106,12 +106,11 @@ def get(self): return res def extend(self, iterable): - existing = list(self) merged = existing + list(iterable) self.clear() self.index = 0 - new_to_add = (sorted(set(merged), key=lambda x: merged.count(x))) + new_to_add = sorted(set(merged), key=lambda x: merged.count(x)) super(FrequencyBasedDogeDeque, self).__init__(new_to_add) @@ -125,22 +124,49 @@ def easter_dates(): PREFIXES = DogeDeque( - 'wow', 'such', 'very', 'so much', 'many', 'lol', 'beautiful', - 'all the', 'the', 'most', 'very much', 'pretty', 'so', + "wow", + "such", + "very", + "so much", + "many", + "lol", + "beautiful", + "all the", + "the", + "most", + "very much", + "pretty", + "so", ) # Please keep in mind that this particular shibe is a terminal hax0r shibe, # and the words added should be in that domain -WORD_LIST = ['computer', 'hax0r', 'code', 'data', 'internet', 'server', - 'hacker', 'terminal', 'doge', 'shibe', 'program', 'free software', - 'web scale', 'monads', 'git', 'daemon', 'loop', 'pretty', - 'uptime', - 'thread safe', 'posix'] +WORD_LIST = [ + "computer", + "hax0r", + "code", + "data", + "internet", + "server", + "hacker", + "terminal", + "doge", + "shibe", + "program", + "free software", + "web scale", + "monads", + "git", + "daemon", + "loop", + "pretty", + "uptime", + "thread safe", + "posix", +] WORDS = DogeDeque(*WORD_LIST) -SUFFIXES = DogeDeque( - 'wow', 'lol', 'hax', 'plz', 'lvl=100' -) +SUFFIXES = DogeDeque("wow", "lol", "hax", "plz", "lvl=100") # A subset of the 255 color cube with the darkest colors removed. This is # suited for use on dark terminals. Lighter colors are still present so some @@ -149,18 +175,19 @@ def easter_dates(): # If you see this and use a light terminal, a pull request with a set that # works well on a light terminal would be awesome. COLORS = DogeDeque( - 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39, 41, 42, 43, - 44, 45, 47, 48, 49, 50, 51, 58, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 94, - 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, - 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 158, 159, 162, 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, 197, 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 + *( + int(x) + for x in """ + 23 24 25 26 27 29 30 31 32 33 35 36 37 38 39 41 42 43 44 45 47 48 49 50 51 + 58 59 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 + 86 87 88 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 + 112 113 114 115 116 117 118 119 120 121 122 123 130 131 132 133 134 135 136 + 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 + 156 157 158 159 162 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 197 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""".split() + ) ) # Seasonal greetings by Shibe. @@ -169,292 +196,202 @@ def easter_dates(): # congratulations, so do whatever complex math you need to make sure Shibe # celebrates with you! SEASONS = { - 'halloween': { - 'dates': ((10, 17), (10, 31)), - 'pic': 'doge-halloween.txt', - 'words': ( - 'halloween', 'scary', 'ghosts', 'boo', 'candy', 'tricks or treats', - 'trick', 'treat', 'costume', 'dark', 'night' - ) + "halloween": { + "dates": ((10, 17), (10, 31)), + "pic": "doge-halloween.txt", + "words": ( + "halloween", + "scary", + "ghosts", + "boo", + "candy", + "tricks or treats", + "trick", + "treat", + "costume", + "dark", + "night", + ), }, - 'thanksgiving': { - 'dates': ((11, 15), (11, 28)), - 'pic': 'doge-thanksgiving.txt', - 'words': ( - 'thanksgiving', 'thanks', 'pilgrim', 'turkeys', 'stuffings', - 'cranberry', 'meshed potatoes' - ) + "thanksgiving": { + "dates": ((11, 15), (11, 28)), + "pic": "doge-thanksgiving.txt", + "words": ( + "thanksgiving", + "thanks", + "pilgrim", + "turkeys", + "stuffings", + "cranberry", + "meshed potatoes", + ), }, - 'xmas': { - 'dates': ((12, 14), (12, 26)), - 'pic': 'doge-xmas.txt', - 'words': ( - 'christmas', 'xmas', 'candles', 'santa', 'merry', 'reindeers', - 'gifts', 'jul', 'vacation', 'carol' - ) + "xmas": { + "dates": ((12, 14), (12, 26)), + "pic": "doge-xmas.txt", + "words": ( + "christmas", + "xmas", + "candles", + "santa", + "merry", + "reindeers", + "gifts", + "jul", + "vacation", + "carol", + ), + }, + "easter": { + "dates": easter_dates(), + "pic": "doge-easter.txt", + "words": ( + "easter", + "bunni", + "playdoge bunni", + "pascha", + "passover", + "påsk", + "life=100", + "crusify", + "fastings", + "eggs", + "lamb", + "candy", + "easter bunni", + "easter eggs", + ), }, - 'easter': { - 'dates': easter_dates(), - 'pic': 'doge-easter.txt', - 'words': ( - 'easter', 'bunni', 'playdoge bunni', 'pascha', 'passover', 'påsk', - 'life=100', 'crusify', 'fastings', 'eggs', 'lamb', 'candy', - 'easter bunni', 'easter eggs' - ) - } - # To be continued... } -STOPWORDS = ["able", "about", "above", "abroad", "according", "accordingly", - "across", "actually", "adj", "after", - "afterwards", "again", "against", "ago", "ahead", "ain't", "all", - "allow", "allows", "almost", "alone", - "along", "alongside", "already", "also", "although", "always", - "am", "amid", "amidst", "among", "amongst", - "an", "and", "another", "any", "anybody", "anyhow", "anyone", - "anything", "anyway", "anyways", "anywhere", - "apart", "appear", "appreciate", "appropriate", "are", "aren't", - "around", "as", "a's", "aside", "ask", - "asking", "associated", "at", "available", "away", "awfully", - "back", "backward", "backwards", "be", - "became", "because", "become", "becomes", "becoming", "been", - "before", "beforehand", "begin", "behind", - "being", "believe", "below", "beside", "besides", "best", - "better", "between", "beyond", "both", "brief", - "but", "by", "came", "can", "cannot", "cant", "can't", "caption", - "cause", "causes", "certain", - "certainly", "changes", "clearly", "c'mon", "co", "co.", "com", - "come", "comes", "concerning", - "consequently", "consider", "considering", "contain", - "containing", "contains", "corresponding", "could", - "couldn't", "course", "c's", "currently", "dare", "daren't", - "definitely", "described", "despite", "did", - "didn't", "different", "directly", "do", "does", "doesn't", - "doing", "done", "don't", "down", "downwards", - "during", "each", "edu", "eg", "eight", "eighty", "either", - "else", "elsewhere", "end", "ending", "enough", - "entirely", "especially", "et", "etc", "even", "ever", "evermore", - "every", "everybody", "everyone", - "everything", "everywhere", "ex", "exactly", "example", "except", - "fairly", "far", "farther", "few", - "fewer", "fifth", "first", "five", "followed", "following", - "follows", "for", "forever", "former", - "formerly", "forth", "forward", "found", "four", "from", - "further", "furthermore", "get", "gets", - "getting", "given", "gives", "go", "goes", "going", "gone", "got", - "gotten", "greetings", "had", "hadn't", - "half", "happens", "hardly", "has", "hasn't", "have", "haven't", - "having", "he", "he'd", "he'll", "hello", - "help", "hence", "her", "here", "hereafter", "hereby", "herein", - "here's", "hereupon", "hers", "herself", - "he's", "hi", "him", "himself", "his", "hither", "hopefully", - "how", "howbeit", "however", "hundred", - "i'd", "ie", "if", "ignored", "i'll", "i'm", "immediate", "in", - "inasmuch", "inc", "inc.", "indeed", - "indicate", "indicated", "indicates", "inner", "inside", - "insofar", "instead", "into", "inward", "is", - "isn't", "it", "it'd", "it'll", "its", "it's", "itself", "i've", - "just", "k", "keep", "keeps", "kept", - "know", "known", "knows", "last", "lately", "later", "latter", - "latterly", "least", "less", "lest", "let", - "let's", "like", "liked", "likely", "likewise", "little", "look", - "looking", "looks", "low", "lower", - "ltd", "made", "mainly", "make", "makes", "many", "may", "maybe", - "mayn't", "me", "mean", "meantime", - "meanwhile", "merely", "might", "mightn't", "mine", "minus", - "miss", "more", "moreover", "most", "mostly", - "mr", "mrs", "much", "must", "mustn't", "my", "myself", "name", - "namely", "nd", "near", "nearly", - "necessary", "need", "needn't", "needs", "neither", "never", - "neverf", "neverless", "nevertheless", "new", - "next", "nine", "ninety", "no", "nobody", "non", "none", - "nonetheless", "noone", "no-one", "nor", - "normally", "not", "nothing", "notwithstanding", "novel", "now", - "nowhere", "obviously", "of", "off", - "often", "oh", "ok", "okay", "old", "on", "once", "one", "ones", - "one's", "only", "onto", "opposite", "or", - "other", "others", "otherwise", "ought", "oughtn't", "our", - "ours", "ourselves", "out", "outside", "over", - "overall", "own", "particular", "particularly", "past", "per", - "perhaps", "placed", "please", "plus", - "possible", "presumably", "probably", "provided", "provides", - "que", "quite", "qv", "rather", "rd", "re", - "really", "reasonably", "recent", "recently", "regarding", - "regardless", "regards", "relatively", - "respectively", "right", "round", "said", "same", "saw", "say", - "saying", "says", "second", "secondly", - "see", "seeing", "seem", "seemed", "seeming", "seems", "seen", - "self", "selves", "sensible", "sent", - "serious", "seriously", "seven", "several", "shall", "shan't", - "she", "she'd", "she'll", "she's", "should", - "shouldn't", "since", "six", "so", "some", "somebody", "someday", - "somehow", "someone", "something", - "sometime", "sometimes", "somewhat", "somewhere", "soon", "sorry", - "specified", "specify", "specifying", - "still", "sub", "such", "sup", "sure", "take", "taken", "taking", - "tell", "tends", "th", "than", "thank", - "thanks", "thanx", "that", "that'll", "thats", "that's", - "that've", "the", "their", "theirs", "them", - "themselves", "then", "thence", "there", "thereafter", "thereby", - "there'd", "therefore", "therein", - "there'll", "there're", "theres", "there's", "thereupon", - "there've", "these", "they", "they'd", "they'll", - "they're", "they've", "thing", "things", "think", "third", - "thirty", "this", "thorough", "thoroughly", - "those", "though", "three", "through", "throughout", "thru", - "thus", "till", "to", "together", "too", - "took", "toward", "towards", "tried", "tries", "truly", "try", - "trying", "t's", "twice", "two", "un", - "under", "underneath", "undoing", "unfortunately", "unless", - "unlike", "unlikely", "until", "unto", "up", - "upon", "upwards", "us", "use", "used", "useful", "uses", "using", - "usually", "v", "value", "various", - "versus", "very", "via", "viz", "vs", "want", "wants", "was", - "wasn't", "way", "we", "we'd", "welcome", - "well", "we'll", "went", "were", "we're", "weren't", "we've", - "what", "whatever", "what'll", "what's", - "what've", "when", "whence", "whenever", "where", "whereafter", - "whereas", "whereby", "wherein", "where's", - "whereupon", "wherever", "whether", "which", "whichever", "while", - "whilst", "whither", "who", "who'd", - "whoever", "whole", "who'll", "whom", "whomever", "who's", - "whose", "why", "will", "willing", "wish", - "with", "within", "without", "wonder", "won't", "would", - "wouldn't", "yes", "yet", "you", "you'd", - "you'll", "your", "you're", "yours", "yourself", "yourselves", - "you've", "zero", "a", "about", "above", - "after", "again", "against", "all", "am", "an", "and", "any", - "are", "aren't", "as", "at", "be", "because", - "been", "before", "being", "below", "between", "both", "but", - "by", "can't", "cannot", "could", "couldn't", - "did", "didn't", "do", "does", "doesn't", "doing", "don't", - "down", "during", "each", "few", "for", "from", - "further", "had", "hadn't", "has", "hasn't", "have", "haven't", - "having", "he", "he'd", "he'll", "he's", - "her", "here", "here's", "hers", "herself", "him", "himself", - "his", "how", "how's", "i", "i'd", "i'll", - "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", - "its", "itself", "let's", "me", "more", - "most", "mustn't", "my", "myself", "no", "nor", "not", "of", - "off", "on", "once", "only", "or", "other", - "ought", "our", "ours", "", "ourselves", "out", "over", "own", - "same", "shan't", "she", "she'd", "she'll", - "she's", "should", "shouldn't", "so", "some", "such", "than", - "that", "that's", "the", "their", "theirs", - "them", "themselves", "then", "there", "there's", "these", "they", - "they'd", "they'll", "they're", - "they've", "this", "those", "through", "to", "too", "under", - "until", "up", "very", "was", "wasn't", "we", - "we'd", "we'll", "we're", "we've", "were", "weren't", "what", - "what's", "when", "when's", "where", - "where's", "which", "while", "who", "who's", "whom", "why", - "why's", "with", "won't", "would", "wouldn't", - "you", "you'd", "you'll", "you're", "you've", "your", "yours", - "yourself", "yourselves", "a", "a's", - "able", "about", "above", "according", "accordingly", "across", - "actually", "after", "afterwards", "again", - "against", "ain't", "all", "allow", "allows", "almost", "alone", - "along", "already", "also", "although", - "always", "am", "among", "amongst", "an", "and", "another", "any", - "anybody", "anyhow", "anyone", - "anything", "anyway", "anyways", "anywhere", "apart", "appear", - "appreciate", "appropriate", "are", - "aren't", "around", "as", "aside", "ask", "asking", "associated", - "at", "available", "away", "awfully", - "b", "be", "became", "because", "become", "becomes", "becoming", - "been", "before", "beforehand", "behind", - "being", "believe", "below", "beside", "besides", "best", - "better", "between", "beyond", "both", "brief", - "but", "by", "c", "c'mon", "c's", "came", "can", "can't", - "cannot", "cant", "cause", "causes", "certain", - "certainly", "changes", "clearly", "co", "com", "come", "comes", - "concerning", "consequently", "consider", - "considering", "contain", "containing", "contains", - "corresponding", "could", "couldn't", "course", - "currently", "d", "definitely", "described", "despite", "did", - "didn't", "different", "do", "does", - "doesn't", "doing", "don't", "done", "down", "downwards", - "during", "e", "each", "edu", "eg", "eight", - "either", "else", "elsewhere", "enough", "entirely", "especially", - "et", "etc", "even", "ever", "every", - "everybody", "everyone", "everything", "everywhere", "ex", - "exactly", "example", "except", "f", "far", - "few", "fifth", "first", "five", "followed", "following", - "follows", "for", "former", "formerly", "forth", - "four", "from", "further", "furthermore", "g", "get", "gets", - "getting", "given", "gives", "go", "goes", - "going", "gone", "got", "gotten", "greetings", "h", "had", - "hadn't", "happens", "hardly", "has", "hasn't", - "have", "haven't", "having", "he", "he's", "hello", "help", - "hence", "her", "here", "here's", "hereafter", - "hereby", "herein", "hereupon", "hers", "herself", "hi", "him", - "himself", "his", "hither", "hopefully", - "how", "howbeit", "however", "i", "i'd", "i'll", "i'm", "i've", - "ie", "if", "ignored", "immediate", "in", - "inasmuch", "inc", "indeed", "indicate", "indicated", "indicates", - "inner", "insofar", "instead", "into", - "inward", "is", "isn't", "it", "it'd", "it'll", "it's", "its", - "itself", "j", "just", "k", "keep", "keeps", - "kept", "know", "knows", "known", "l", "last", "lately", "later", - "latter", "latterly", "least", "less", - "lest", "let", "let's", "like", "liked", "likely", "little", - "look", "looking", "looks", "ltd", "m", - "mainly", "many", "may", "maybe", "me", "mean", "meanwhile", - "merely", "might", "more", "moreover", "most", - "mostly", "much", "must", "my", "myself", "n", "name", "namely", - "nd", "near", "nearly", "necessary", - "need", "needs", "neither", "never", "nevertheless", "new", - "next", "nine", "no", "nobody", "non", "none", - "noone", "nor", "normally", "not", "nothing", "novel", "now", - "nowhere", "o", "obviously", "of", "off", - "often", "oh", "ok", "okay", "old", "on", "once", "one", "ones", - "only", "onto", "or", "other", "others", - "otherwise", "ought", "our", "ours", "ourselves", "out", - "outside", "over", "overall", "own", "p", - "particular", "particularly", "per", "perhaps", "placed", - "please", "plus", "possible", "presumably", - "probably", "provides", "q", "que", "quite", "qv", "r", "rather", - "rd", "re", "really", "reasonably", - "regarding", "regardless", "regards", "relatively", - "respectively", "right", "s", "said", "same", "saw", - "say", "saying", "says", "second", "secondly", "see", "seeing", - "seem", "seemed", "seeming", "seems", - "seen", "self", "selves", "sensible", "sent", "serious", - "seriously", "seven", "several", "shall", "she", - "should", "shouldn't", "since", "six", "so", "some", "somebody", - "somehow", "someone", "something", - "sometime", "sometimes", "somewhat", "somewhere", "soon", "sorry", - "specified", "specify", "specifying", - "still", "sub", "such", "sup", "sure", "t", "t's", "take", - "taken", "tell", "tends", "th", "than", "thank", - "thanks", "thanx", "that", "that's", "thats", "the", "their", - "theirs", "them", "themselves", "then", - "thence", "there", "there's", "thereafter", "thereby", - "therefore", "therein", "theres", "thereupon", - "these", "they", "they'd", "they'll", "they're", "they've", - "think", "third", "this", "thorough", - "thoroughly", "those", "though", "three", "through", "throughout", - "thru", "thus", "to", "together", "too", - "took", "toward", "towards", "tried", "tries", "truly", "try", - "trying", "twice", "two", "u", "un", - "under", "unfortunately", "unless", "unlikely", "until", "unto", - "up", "upon", "us", "use", "used", - "useful", "uses", "using", "usually", "uucp", "v", "value", - "various", "very", "via", "viz", "vs", "w", - "want", "wants", "was", "wasn't", "way", "we", "we'd", "we'll", - "we're", "we've", "welcome", "well", - "went", "were", "weren't", "what", "what's", "whatever", "when", - "whence", "whenever", "where", "where's", - "whereafter", "whereas", "whereby", "wherein", "whereupon", - "wherever", "whether", "which", "while", - "whither", "who", "who's", "whoever", "whole", "whom", "whose", - "why", "will", "willing", "wish", "with", - "within", "without", "won't", "wonder", "would", "would", - "wouldn't", "x", "y", "yes", "yet", "you", - "you'd", "you'll", "you're", "you've", "your", "yours", - "yourself", "yourselves", "z", "zero", "I", "a", - "about", "an", "are", "as", "at", "be", "by", "com", "for", - "from", "how", "in", "is", "it", "of", "on", - "or", "that", "the", "this", "to", "was", "what", "when", "where", - "who", "will", "with", "the", "www"] +STOPWORDS = [ + """ + + able about above abroad according accordingly across actually adj after + afterwards again against ago ahead ain't all allow allows almost alone along + alongside already also although always am amid amidst among amongst an and + another any anybody anyhow anyone anything anyway anyways anywhere apart + appear appreciate appropriate are aren't around as a's aside ask asking + associated at available away awfully back backward backwards be became + because become becomes becoming been before beforehand begin behind being + believe below beside besides best better between beyond both brief but by + came can cannot cant can't caption cause causes certain certainly changes + clearly c'mon co co. com come comes concerning consequently consider + considering contain containing contains corresponding could couldn't course + c's currently dare daren't definitely described despite did didn't different + directly do does doesn't doing done don't down downwards during each edu eg + eight eighty either else elsewhere end ending enough entirely especially et + etc even ever evermore every everybody everyone everything everywhere ex + exactly example except fairly far farther few fewer fifth first five + followed following follows for forever former formerly forth forward found + four from further furthermore get gets getting given gives go goes going + gone got gotten greetings had hadn't half happens hardly has hasn't have + haven't having he he'd he'll hello help hence her here hereafter hereby + herein here's hereupon hers herself he's hi him himself his hither hopefully + how howbeit however hundred i'd ie if ignored i'll i'm immediate in inasmuch + inc inc. indeed indicate indicated indicates inner inside insofar instead + into inward is isn't it it'd it'll its it's itself i've just k keep keeps + kept know known knows last lately later latter latterly least less lest let + let's like liked likely likewise little look looking looks low lower ltd + made mainly make makes many may maybe mayn't me mean meantime meanwhile + merely might mightn't mine minus miss more moreover most mostly mr mrs much + must mustn't my myself name namely nd near nearly necessary need needn't + needs neither never neverf neverless nevertheless new next nine ninety no + nobody non none nonetheless noone no-one nor normally not nothing + notwithstanding novel now nowhere obviously of off often oh ok okay old on + once one ones one's only onto opposite or other others otherwise ought + oughtn't our ours ourselves out outside over overall own particular + particularly past per perhaps placed please plus possible presumably + probably provided provides que quite qv rather rd re really reasonably + recent recently regarding regardless regards relatively respectively right + round said same saw say saying says second secondly see seeing seem seemed + seeming seems seen self selves sensible sent serious seriously seven several + shall shan't she she'd she'll she's should shouldn't since six so some + somebody someday somehow someone something sometime sometimes somewhat + somewhere soon sorry specified specify specifying still sub such sup sure + take taken taking tell tends th than thank thanks thanx that that'll thats + that's that've the their theirs them themselves then thence there thereafter + thereby there'd therefore therein there'll there're theres there's thereupon + there've these they they'd they'll they're they've thing things think third + thirty this thorough thoroughly those though three through throughout thru + thus till to together too took toward towards tried tries truly try trying + t's twice two un under underneath undoing unfortunately unless unlike + unlikely until unto up upon upwards us use used useful uses using usually v + value various versus very via viz vs want wants was wasn't way we we'd + welcome well we'll went were we're weren't we've what whatever what'll + what's what've when whence whenever where whereafter whereas whereby wherein + where's whereupon wherever whether which whichever while whilst whither who + who'd whoever whole who'll whom whomever who's whose why will willing wish + with within without wonder won't would wouldn't yes yet you you'd you'll + your you're yours yourself yourselves you've zero + + a about above after again against all am an and any are aren't as at be + because been before being below between both but by can't cannot could + couldn't did didn't do does doesn't doing don't down during each few for + from further had hadn't has hasn't have haven't having he he'd he'll he's + her here here's hers herself him himself his how how's i i'd i'll i'm i've + if in into is isn't it it's its itself let's me more most mustn't my myself + no nor not of off on once only or other ought our ours ourselves out over + own same shan't she she'd she'll she's should shouldn't so some such than + that that's the their theirs them themselves then there there's these they + they'd they'll they're they've this those through to too under until up very + was wasn't we we'd we'll we're we've were weren't what what's when when's + where where's which while who who's whom why why's with won't would wouldn't + you you'd you'll you're you've your yours yourself yourselves + + a's able about above according accordingly across actually after afterwards + again against ain't all allow allows almost alone along already also + although always am among amongst an and another any anybody anyhow anyone + anything anyway anyways anywhere apart appear appreciate appropriate are + aren't around as aside ask asking associated at available away awfully b be + became because become becomes becoming been before beforehand behind being + believe below beside besides best better between beyond both brief but by c + c'mon c's came can can't cannot cant cause causes certain certainly changes + clearly co com come comes concerning consequently consider considering + contain containing contains corresponding could couldn't course currently d + definitely described despite did didn't different do does doesn't doing + don't done down downwards during e each edu eg eight either else elsewhere + enough entirely especially et etc even ever every everybody everyone + everything everywhere ex exactly example except f far few fifth first five + followed following follows for former formerly forth four from further + furthermore g get gets getting given gives go goes going gone got gotten + greetings h had hadn't happens hardly has hasn't have haven't having he he's + hello help hence her here here's hereafter hereby herein hereupon hers + herself hi him himself his hither hopefully how howbeit however i i'd i'll + i'm i've ie if ignored immediate in inasmuch inc indeed indicate indicated + indicates inner insofar instead into inward is isn't it it'd it'll it's its + itself j just k keep keeps kept know knows known l last lately later latter + latterly least less lest let let's like liked likely little look looking + looks ltd m mainly many may maybe me mean meanwhile merely might more + moreover most mostly much must my myself n name namely nd near nearly + necessary need needs neither never nevertheless new next nine no nobody non + none noone nor normally not nothing novel now nowhere o obviously of off + often oh ok okay old on once one ones only onto or other others otherwise + ought our ours ourselves out outside over overall own p particular + particularly per perhaps placed please plus possible presumably probably + provides q que quite qv r rather rd re really reasonably regarding + regardless regards relatively respectively right s said same saw say saying + says second secondly see seeing seem seemed seeming seems seen self selves + sensible sent serious seriously seven several shall she should shouldn't + since six so some somebody somehow someone something sometime sometimes + somewhat somewhere soon sorry specified specify specifying still sub such + sup sure t t's take taken tell tends th than thank thanks thanx that that's + thats the their theirs them themselves then thence there there's thereafter + thereby therefore therein theres thereupon these they they'd they'll they're + they've think third this thorough thoroughly those though three through + throughout thru thus to together too took toward towards tried tries truly + try trying twice two u un under unfortunately unless unlikely until unto up + upon us use used useful uses using usually uucp v value various very via viz + vs w want wants was wasn't way we we'd we'll we're we've welcome well went + were weren't what what's whatever when whence whenever where where's + whereafter whereas whereby wherein whereupon wherever whether which while + whither who who's whoever whole whom whose why will willing wish with within + without won't wonder would would wouldn't x y yes yet you you'd you'll + you're you've your yours yourself yourselves z zero + + I a about an are as at be by com for from how in is it of on or that the + this to was what when where who will with the www + + """.split() +] From df189c34c9f7a215ebda6745dd8fe8d3d2337436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:17:40 +0100 Subject: [PATCH 02/11] Wow more ruff linting warnings --- src/doge/core.py | 70 +++++++++++++++++++++++++++--------------------- src/doge/wow.py | 42 ++++++++++++++--------------- 2 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/doge/core.py b/src/doge/core.py index 22581d3..d3362ab 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -1,28 +1,33 @@ #!/usr/bin/env python -# coding: utf-8 import argparse import datetime import os import random import re -import struct +import shutil import subprocess as sp import sys import traceback import unicodedata from importlib.resources import files +import dateutil.tz + from doge import wow ROOT = files("doge").joinpath("static") DEFAULT_DOGE = "doge.txt" -class Doge(object): +class Doge: + MAX_PERCENT = 100 + MIN_PS_LEN = 2 + def __init__(self, tty, ns): self.tty = tty self.ns = ns + self.lines = [] self.doge_path = ROOT.joinpath(ns.doge_path or DEFAULT_DOGE) if ns.frequency: # such frequency based @@ -43,7 +48,7 @@ def setup(self): doge = [] max_doge = 15 - if self.ns.density > 100: + if self.ns.density > self.MAX_PERCENT: sys.stderr.write("wow, density such over 100%, too high\n") sys.exit(1) @@ -54,7 +59,7 @@ def setup(self): if self.tty.width < max_doge: # Shibe won't fit, so abort. sys.stderr.write("wow, such small terminal\n") - sys.stderr.write("no doge under {0} column\n".format(max_doge)) + sys.stderr.write(f"no doge under {max_doge} column\n") return False # Check for prompt height so that we can fill the screen minus how high @@ -95,22 +100,24 @@ def setup_seasonal(self): # If we've specified another doge or no doge at all, it does not make # sense to use seasons. if self.ns.doge_path is not None and not self.ns.no_shibe: - return + return None - now = datetime.datetime.now() + tz = dateutil.tz.tzlocal() + now = datetime.datetime.now(tz=tz) for season, data in wow.SEASONS.items(): start, end = data["dates"] - start_dt = datetime.datetime(now.year, start[0], start[1]) + start_dt = datetime.datetime(now.year, start[0], start[1], tzinfo=tz) # Be sane if the holiday season spans over New Year's day. end_dt = datetime.datetime( - now.year + (start[0] > end[0] and 1 or 0), end[0], end[1] + now.year + ((start[0] > end[0] and 1) or 0), end[0], end[1], tzinfo=tz ) if start_dt <= now <= end_dt: # Wow, much holiday! return self.load_season(season) + return None def load_season(self, season_key): if season_key == "none": @@ -179,12 +186,10 @@ def get_real_data(self): editor = editor.split("/")[-1] ret.append(editor) - # OS, hostname and... architechture (because lel) + # OS, hostname and... architecture (because lel) if hasattr(os, "uname"): uname = os.uname() - ret.append(uname[0]) - ret.append(uname[1]) - ret.append(uname[4]) + ret.extend((uname[0], uname[1], uname[4])) # Grab actual files from $HOME. filenames = os.listdir(os.environ.get("HOME")) @@ -197,7 +202,8 @@ def get_real_data(self): # Prepare the returned data. First, lowercase it. self.words.extend(map(str.lower, ret)) - def filter_words(self, words, stopwords, min_length): + @staticmethod + def filter_words(words, stopwords, min_length): return [ word for word in words if len(word) >= min_length and word not in stopwords ] @@ -212,7 +218,7 @@ def get_stdin_data(self): # No pipez found return False - stdin_lines = (l for l in sys.stdin.readlines()) + stdin_lines = (line for line in sys.stdin.readlines()) rx_word = re.compile(r"\w+", re.UNICODE) @@ -243,14 +249,14 @@ def get_processes(self): try: # POSIX ps, so it should work in most environments where doge would p = sp.Popen(["ps", "-A", "-o", "comm="], stdout=sp.PIPE) - output, error = p.communicate() + output, _error = p.communicate() output = output.decode("utf-8") for comm in output.split("\n"): name = comm.split("/")[-1] # Filter short and weird ones - if name and len(name) >= 2 and ":" not in name: + if name and len(name) >= self.MIN_PS_LEN and ":" not in name: procs.add(name) finally: @@ -265,7 +271,7 @@ def print_doge(self): sys.stdout.flush() -class DogeMessage(object): +class DogeMessage: """ A randomly placed and randomly colored message @@ -283,11 +289,11 @@ def generate(self): msg = self.word else: # Add a prefix. - msg = "{0} {1}".format(wow.PREFIXES.get(), self.word) + msg = f"{wow.PREFIXES.get()} {self.word}" # Seldomly add a suffix as well. if random.choice(range(15)) == 0: - msg += " {0}".format(wow.SUFFIXES.get()) + msg += f" {wow.SUFFIXES.get()}" # Calculate the maximum possible spacer interval = self.tty.width - onscreen_len(msg) @@ -301,19 +307,24 @@ def generate(self): return self.occupied + "\n" # Apply spacing - msg = "{0}{1}".format(" " * random.choice(range(interval)), msg) + msg = "{}{}".format(" " * random.choice(range(interval)), msg) if self.tty.pretty: # Apply pretty ANSI color coding. - msg = "\x1b[1m\x1b[38;5;{0}m{1}\x1b[39m\x1b[0m".format( - wow.COLORS.get(), msg - ) + msg = f"\x1b[1m\x1b[38;5;{wow.COLORS.get()}m{msg}\x1b[39m\x1b[0m" # Line ends are pretty cool guys, add one of those. - return "{0}{1}\n".format(self.occupied, msg) + return f"{self.occupied}{msg}\n" + +class TTYHandler: + def __init__(self): + self.height = 25 + self.width = 80 + self.in_is_tty = True + self.out_is_tty = True + self.pretty = True -class TTYHandler(object): def setup(self): self.height, self.width = self.get_tty_size() self.in_is_tty = sys.stdin.isatty() @@ -411,7 +422,7 @@ def setup_arguments(): parser.add_argument( "--season", help="wow shibe season congrate", - choices=sorted(wow.SEASONS.keys()) + ["none"], + choices=[*sorted(wow.SEASONS.keys()), "none"], ) parser.add_argument( @@ -493,9 +504,8 @@ def main(): if not lang.endswith("UTF-8"): print( - "wow error: locale '{0}' is not UTF-8. ".format(lang) - + "doge needs UTF-8 to print Shibe. Please set your system to " - "use a UTF-8 locale." + f"wow error: locale '{lang}' is not UTF-8. doge needs UTF-8 to " + "print Shibe. Please set your system to use a UTF-8 locale." ) return 2 diff --git a/src/doge/wow.py b/src/doge/wow.py index c6b01df..ad12215 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -10,6 +10,7 @@ from collections import deque import dateutil.easter +from dateutil import tz class DogeDeque(deque): @@ -23,11 +24,11 @@ class DogeDeque(deque): """ - def __init__(self, *args, **kwargs): - self.index = 0 + def __init__(self, *args, **_kwargs): + self.doge_index = 0 args = list(args) random.shuffle(args) - super(DogeDeque, self).__init__(args) + super().__init__(args) def get(self): """ @@ -36,23 +37,23 @@ def get(self): """ - self.index += 1 + self.doge_index += 1 # If we've gone through the entire deque once, shuffle it again to # simulate ever-flowing random. self.shuffle() will run __init__(), # which will reset the index to 0. - if self.index == len(self): + if self.doge_index == len(self): self.shuffle() self.rotate(1) try: return self[0] - except: + except IndexError: return "wow" def extend(self, iterable): # Whenever we extend the list, make sure to shuffle in the new items! - super(DogeDeque, self).extend(iterable) + super().extend(iterable) self.shuffle() def shuffle(self): @@ -68,20 +69,17 @@ def shuffle(self): random.shuffle(args) self.clear() - super(DogeDeque, self).__init__(args) + super().__init__(args) class FrequencyBasedDogeDeque(deque): def __init__(self, *args, **kwargs): - self.index = 0 - if "step" in kwargs: - self.step = kwargs["step"] - else: - self.step = 2 + self.doge_index = 0 + self.step = kwargs.get("step", 2) args = list(args) # sort words by frequency - args = sorted(set(args), key=lambda x: args.count(x)) - super(FrequencyBasedDogeDeque, self).__init__(args) + args = sorted(set(args), key=args.count) + super().__init__(args) def shuffle(self): pass @@ -95,13 +93,13 @@ def get(self): if len(self) < 1: return "wow" - if self.index >= len(self): - self.index = 0 + if self.doge_index >= len(self): + self.doge_index = 0 step = random.randint(1, min(self.step, len(self))) res = self[0] - self.index += step + self.doge_index += step self.rotate(step) return res @@ -109,14 +107,14 @@ def extend(self, iterable): existing = list(self) merged = existing + list(iterable) self.clear() - self.index = 0 - new_to_add = sorted(set(merged), key=lambda x: merged.count(x)) - super(FrequencyBasedDogeDeque, self).__init__(new_to_add) + self.doge_index = 0 + new_to_add = sorted(set(merged), key=merged.count) + super().__init__(new_to_add) def easter_dates(): """Calculate the start and stop dates of Easter.""" - this_year = dt.datetime.now().year + this_year = dt.datetime.now(tz=tz.tzlocal()).year easter_day = dateutil.easter.easter(this_year) start = easter_day - dt.timedelta(days=7) stop = easter_day + dt.timedelta(days=1) From bb9c03cfb0a92a277c8b76e85d9e488dc0c787ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:18:13 +0100 Subject: [PATCH 03/11] Such shutil.get_terminal_size() --- src/doge/core.py | 49 +----------------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/src/doge/core.py b/src/doge/core.py index d3362ab..9dc6ea6 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -326,7 +326,7 @@ def __init__(self): self.pretty = True def setup(self): - self.height, self.width = self.get_tty_size() + self.width, self.height = shutil.get_terminal_size() self.in_is_tty = sys.stdin.isatty() self.out_is_tty = sys.stdout.isatty() @@ -334,53 +334,6 @@ def setup(self): if sys.platform == "win32" and os.getenv("TERM") == "xterm": self.pretty = True - def _tty_size_windows(self, handle): - try: - from ctypes import create_string_buffer, windll - - h = windll.kernel32.GetStdHandle(handle) - buf = create_string_buffer(22) - - if windll.kernel32.GetConsoleScreenBufferInfo(h, buf): - left, top, right, bottom = struct.unpack("4H", buf.raw[10:18]) - return right - left + 1, bottom - top + 1 - except: - pass - - def _tty_size_linux(self, fd): - try: - import fcntl - import termios - - return struct.unpack( - "hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, struct.pack("hh", 0, 0)) - ) - except: - return - - def get_tty_size(self): - """ - Get the current terminal size without using a subprocess - - http://stackoverflow.com/questions/566746 - I have no clue what-so-fucking ever over how this works or why it - returns the size of the terminal in both cells and pixels. But hey, it - does. - - """ - if sys.platform == "win32": - # stdin, stdout, stderr = -10, -11, -12 - ret = self._tty_size_windows(-10) - ret = ret or self._tty_size_windows(-11) - ret = ret or self._tty_size_windows(-12) - else: - # stdin, stdout, stderr = 0, 1, 2 - ret = self._tty_size_linux(0) - ret = ret or self._tty_size_linux(1) - ret = ret or self._tty_size_linux(2) - - return ret or (25, 80) - def clean_len(s): """ From 38bb9f7abaf67cf4c059396007917451bfda9ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:02:40 +0100 Subject: [PATCH 04/11] Wow pretty docstrings --- src/doge/core.py | 65 +++++++++++++++++++----------------------------- src/doge/wow.py | 31 ++++++++++------------- 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/src/doge/core.py b/src/doge/core.py index 9dc6ea6..9d0458b 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +"""Wow print Shibe to terminal, such random words.""" + import argparse import datetime import os @@ -21,6 +23,8 @@ class Doge: + """Make Shibe and pretty random words.""" + MAX_PERCENT = 100 MIN_PS_LEN = 2 @@ -36,6 +40,7 @@ def __init__(self, tty, ns): self.words = wow.DogeDeque(*wow.WORD_LIST) def setup(self): + """Check args and seasons, load data, and decorate shibe.""" # Setup seasonal data self.setup_seasonal() @@ -84,15 +89,14 @@ def setup(self): return True def setup_seasonal(self): - """ + """Handle seasonal holidays. + Check if there's some seasonal holiday going on, setup appropriate Shibe picture and load holiday words. Note: if there are two or more holidays defined for a certain date, the first one takes precedence. - """ - # If we've specified a season, just run that one if self.ns.season: return self.load_season(self.ns.season) @@ -120,6 +124,7 @@ def setup_seasonal(self): return None def load_season(self, season_key): + """Try to load a season, unless 'none' given.""" if season_key == "none": return @@ -128,11 +133,7 @@ def load_season(self, season_key): self.words.extend(season["words"]) def apply_text(self): - """ - Apply text around doge - - """ - + """Apply text around doge.""" # Calculate a random sampling of lines that are to have text applied # onto them. Return value is a sorted list of line index integers. linelen = len(self.lines) @@ -158,24 +159,17 @@ def apply_text(self): self.lines[target] = DogeMessage(self, line, word).generate() def load_doge(self): - """ - Return pretty ASCII Shibe. + """Return pretty ASCII Shibe. wow - """ - if self.ns.no_shibe: return [""] return self.doge_path.read_text(encoding="utf-8").splitlines(keepends=True) def get_real_data(self): - """ - Grab actual data from the system - - """ - + """Grab actual data from the system.""" ret = [] username = os.environ.get("USER") if username: @@ -204,16 +198,13 @@ def get_real_data(self): @staticmethod def filter_words(words, stopwords, min_length): + """Filter out unwanted words.""" return [ word for word in words if len(word) >= min_length and word not in stopwords ] def get_stdin_data(self): - """ - Get words from stdin. - - """ - + """Get words from stdin.""" if self.tty.in_is_tty: # No pipez found return False @@ -239,11 +230,7 @@ def get_stdin_data(self): return True def get_processes(self): - """ - Grab a shuffled list of all currently running process names - - """ - + """Grab a shuffled list of all currently running process names.""" procs = set() try: @@ -266,16 +253,14 @@ def get_processes(self): return proc_list def print_doge(self): + """Print doge to terminal.""" for line in self.lines: sys.stdout.write(line) sys.stdout.flush() class DogeMessage: - """ - A randomly placed and randomly colored message - - """ + """Make a randomly placed and randomly colored message.""" def __init__(self, doge, occupied, word): self.doge = doge @@ -284,6 +269,7 @@ def __init__(self, doge, occupied, word): self.word = word def generate(self): + """Add a word to a line, with color, random prefix and suffix.""" if self.word == "wow": # Standalone wow. Don't apply any prefixes or suffixes. msg = self.word @@ -318,6 +304,8 @@ def generate(self): class TTYHandler: + """Get terminal properties.""" + def __init__(self): self.height = 25 self.width = 80 @@ -326,6 +314,7 @@ def __init__(self): self.pretty = True def setup(self): + """Calculate terminal properties.""" self.width, self.height = shutil.get_terminal_size() self.in_is_tty = sys.stdin.isatty() self.out_is_tty = sys.stdout.isatty() @@ -336,23 +325,17 @@ def setup(self): def clean_len(s): - """ - Calculate the length of a string without it's color codes - - """ - + """Calculate the length of a string without its color codes.""" s = re.sub(r"\x1b\[[0-9;]*m", "", s) return len(s) def onscreen_len(s): - """ - Calculate the length of a unicode string on screen, - accounting for double-width characters + """Calculate the length of a unicode string on screen. + Also account for double-width characters. """ - length = 0 for ch in s: length += 2 if unicodedata.east_asian_width(ch) == "W" else 1 @@ -361,6 +344,7 @@ def onscreen_len(s): def setup_arguments(): + """Make an ArgumentParser.""" parser = argparse.ArgumentParser("doge") parser.add_argument( @@ -426,6 +410,7 @@ def setup_arguments(): def main(): + """Run the main CLI script.""" tty = TTYHandler() tty.setup() diff --git a/src/doge/wow.py b/src/doge/wow.py index ad12215..2d04131 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -1,8 +1,6 @@ -""" -Words and static data +"""Define words and static data. Please extend this file with more lvl=100 shibe wow. - """ import datetime as dt @@ -14,14 +12,12 @@ class DogeDeque(deque): - """ - A doge deque. A doqe, if you may. + """A doge deque. A doqe, if you may. Because random is random, just using a random choice from the static lists below there will always be some repetition in the output. This collection will instead shuffle the list upon init, and act as a rotating deque whenever an item is gotten from it. - """ def __init__(self, *args, **_kwargs): @@ -31,12 +27,11 @@ def __init__(self, *args, **_kwargs): super().__init__(args) def get(self): - """ - Get one item. This will rotate the deque one step. Repeated gets will - return different items. + """Get one item and prepare the next. + This will rotate the deque one step. Repeated gets will + return different items. """ - self.doge_index += 1 # If we've gone through the entire deque once, shuffle it again to @@ -52,19 +47,17 @@ def get(self): return "wow" def extend(self, iterable): + """Extend and shuffle.""" # Whenever we extend the list, make sure to shuffle in the new items! super().extend(iterable) self.shuffle() def shuffle(self): - """ - Shuffle the deque + """Shuffle the deque. Deques themselves do not support this, so this will make all items into a list, shuffle that list, clear the deque, and then re-init the deque. - """ - args = list(self) random.shuffle(args) @@ -73,6 +66,8 @@ def shuffle(self): class FrequencyBasedDogeDeque(deque): + """A doge deque based on word frequencies.""" + def __init__(self, *args, **kwargs): self.doge_index = 0 self.step = kwargs.get("step", 2) @@ -82,13 +77,12 @@ def __init__(self, *args, **kwargs): super().__init__(args) def shuffle(self): - pass + """Shuffle the deque.""" def get(self): - """ - Get one item and prepare to get an item with lower - rank on the next call. + """Get one item and prepare the next. + Prepare to get an item with lower rank on the next call. """ if len(self) < 1: return "wow" @@ -104,6 +98,7 @@ def get(self): return res def extend(self, iterable): + """Extend and recalculate.""" existing = list(self) merged = existing + list(iterable) self.clear() From 8de469e8e2d333efc1ce3803b562fed5e75d31c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:03:06 +0100 Subject: [PATCH 05/11] Such small fixes --- src/doge/wow.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/doge/wow.py b/src/doge/wow.py index 2d04131..5c43e24 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -171,15 +171,15 @@ def easter_dates(): *( int(x) for x in """ - 23 24 25 26 27 29 30 31 32 33 35 36 37 38 39 41 42 43 44 45 47 48 49 50 51 - 58 59 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 - 86 87 88 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 - 112 113 114 115 116 117 118 119 120 121 122 123 130 131 132 133 134 135 136 - 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 - 156 157 158 159 162 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 197 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""".split() + 23 24 25 26 27 29 30 31 32 33 35 36 37 38 39 41 42 43 44 45 47 48 49 50 + 51 58 59 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 + 84 85 86 87 88 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 + 110 111 112 113 114 115 116 117 118 119 120 121 122 123 130 131 132 133 + 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 + 152 153 154 155 156 157 158 159 162 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 197 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""".split() ) ) @@ -270,7 +270,7 @@ def easter_dates(): because become becomes becoming been before beforehand begin behind being believe below beside besides best better between beyond both brief but by came can cannot cant can't caption cause causes certain certainly changes - clearly c'mon co co. com come comes concerning consequently consider + clearly c'mon co co. com come comes concerning consequently consider considering contain containing contains corresponding could couldn't course c's currently dare daren't definitely described despite did didn't different directly do does doesn't doing done don't down downwards during each edu eg @@ -283,7 +283,7 @@ def easter_dates(): haven't having he he'd he'll hello help hence her here hereafter hereby herein here's hereupon hers herself he's hi him himself his hither hopefully how howbeit however hundred i'd ie if ignored i'll i'm immediate in inasmuch - inc inc. indeed indicate indicated indicates inner inside insofar instead + inc inc. indeed indicate indicated indicates inner inside insofar instead into inward is isn't it it'd it'll its it's itself i've just k keep keeps kept know known knows last lately later latter latterly least less lest let let's like liked likely likewise little look looking looks low lower ltd From 7f3676afe916d545d9173fc85530ca295ff1914c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:16:58 +0100 Subject: [PATCH 06/11] Wow ruff ruff! --- pyproject.toml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a537179..70cc855 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "doge" -version = "3.7.0" +version = "3.7.1" description = "wow very terminal doge" authors = [{ name = "Olivia Thiderman", email = "olivia@thiderman.org" }] readme = "README.md" @@ -28,3 +28,24 @@ Homepage = "https://github.com/thiderman/doge" [project.scripts] doge = "doge.core:main" + +[tool.ruff] +extend-exclude = ["pdbsh/rpcc_client.py"] +line-length = 88 +target-version = "py39" + +[tool.ruff.lint] +select = ["ALL"] +ignore = ["ISC001", "D107", "D203", "D213", "COM812", "CPY001", "T20", "S311"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["D104"] + +[tool.ruff.lint.flake8-annotations] +ignore-fully-untyped = true + +[tool.ruff.lint.flake8-type-checking] +quote-annotations = true + +[tool.ruff.format] +docstring-code-format = true From 5b1866419956b72747995c923d36b8b2ea878f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:25:08 +0100 Subject: [PATCH 07/11] Wow such dateutil --- src/doge/wow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doge/wow.py b/src/doge/wow.py index 5c43e24..13d8a6d 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -8,7 +8,7 @@ from collections import deque import dateutil.easter -from dateutil import tz +import dateutil.tz class DogeDeque(deque): @@ -109,7 +109,7 @@ def extend(self, iterable): def easter_dates(): """Calculate the start and stop dates of Easter.""" - this_year = dt.datetime.now(tz=tz.tzlocal()).year + this_year = dt.datetime.now(tz=dateutil.tz.tzlocal()).year easter_day = dateutil.easter.easter(this_year) start = easter_day - dt.timedelta(days=7) stop = easter_day + dt.timedelta(days=1) From 6748ff01b12261e38cf78295c528315748246e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:40:24 +0100 Subject: [PATCH 08/11] Such copyright wow --- pyproject.toml | 2 +- src/doge/__init__.py | 1 + src/doge/core.py | 2 ++ src/doge/wow.py | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 70cc855..27950cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ target-version = "py39" [tool.ruff.lint] select = ["ALL"] -ignore = ["ISC001", "D107", "D203", "D213", "COM812", "CPY001", "T20", "S311"] +ignore = ["D107", "D203", "D213", "COM812", "T20", "S311"] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["D104"] diff --git a/src/doge/__init__.py b/src/doge/__init__.py index e69de29..41ec20e 100644 --- a/src/doge/__init__.py +++ b/src/doge/__init__.py @@ -0,0 +1 @@ +# Copyright (C) 2013-2024 Olivia Thiderman diff --git a/src/doge/core.py b/src/doge/core.py index 9d0458b..4181f97 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +# Copyright (C) 2013-2024 Olivia Thiderman + """Wow print Shibe to terminal, such random words.""" import argparse diff --git a/src/doge/wow.py b/src/doge/wow.py index 13d8a6d..9cc684e 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -3,6 +3,8 @@ Please extend this file with more lvl=100 shibe wow. """ +# Copyright (C) 2013-2024 Olivia Thiderman + import datetime as dt import random from collections import deque From 42d7b51747fee35005a36f20f7a41b783fd1effc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:57:53 +0100 Subject: [PATCH 09/11] Fix return statement --- src/doge/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doge/core.py b/src/doge/core.py index 4181f97..e388a63 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -455,6 +455,7 @@ def main(): "/usr/bin/locale" ) return 1 + return 0 # wow very main From 23caee64fb530bec397b3d9d7e45e2cc6a325ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 20:11:18 +0100 Subject: [PATCH 10/11] Such confuse! --- src/doge/wow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doge/wow.py b/src/doge/wow.py index 9cc684e..8182dd0 100644 --- a/src/doge/wow.py +++ b/src/doge/wow.py @@ -165,10 +165,12 @@ def easter_dates(): # A subset of the 255 color cube with the darkest colors removed. This is # suited for use on dark terminals. Lighter colors are still present so some -# colors might be semi-unreadabe on lighter backgrounds. +# colors might be semi-unreadable on lighter backgrounds. # # If you see this and use a light terminal, a pull request with a set that # works well on a light terminal would be awesome. +# +# The "1 2 3".split() trick keeps the line count low, even with black auto-formatting. COLORS = DogeDeque( *( int(x) From 4f607b91bac8533d3a32dabdf7736c1e81a20c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Mellstr=C3=B6m?= <11281108+harkabeeparolus@users.noreply.github.com> Date: Sat, 10 Feb 2024 20:23:41 +0100 Subject: [PATCH 11/11] Wow spelling and pylint warnings --- src/doge/core.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/doge/core.py b/src/doge/core.py index e388a63..ce125cd 100755 --- a/src/doge/core.py +++ b/src/doge/core.py @@ -138,13 +138,13 @@ def apply_text(self): """Apply text around doge.""" # Calculate a random sampling of lines that are to have text applied # onto them. Return value is a sorted list of line index integers. - linelen = len(self.lines) + line_len = len(self.lines) if self.ns.density == 0: return affected = sorted( - random.sample(range(linelen), int(linelen * (self.ns.density / 100))) + random.sample(range(line_len), int(line_len * (self.ns.density / 100))) ) for i, target in enumerate(affected, start=1): @@ -233,7 +233,7 @@ def get_stdin_data(self): def get_processes(self): """Grab a shuffled list of all currently running process names.""" - procs = set() + processes = set() try: # POSIX ps, so it should work in most environments where doge would @@ -246,11 +246,11 @@ def get_processes(self): name = comm.split("/")[-1] # Filter short and weird ones if name and len(name) >= self.MIN_PS_LEN and ":" not in name: - procs.add(name) + processes.add(name) finally: # Either it executed properly or no ps was found. - proc_list = list(procs) + proc_list = list(processes) random.shuffle(proc_list) return proc_list @@ -295,7 +295,8 @@ def generate(self): return self.occupied + "\n" # Apply spacing - msg = "{}{}".format(" " * random.choice(range(interval)), msg) + spacer = " " * random.choice(range(interval)) + msg = f"{spacer}{msg}" if self.tty.pretty: # Apply pretty ANSI color coding.