Cope 2.5.0
My personal "standard library" of all the generally useful code I've written for various projects over the years
Loading...
Searching...
No Matches
misc.py
1# from .decorators import todo, confidence
2from .imports import dependsOnPackage
3from ..debugging import get_varname, called_as_decorator, debug, get_metadata
4# from .colors import ERROR
5import re
6import subprocess
7from os.path import join
8from random import randint
9import sys
10# from math import floor
11
12# TODO: I think this broke
13def replace_line(line, offset=0, keepTabs=True, convertTabs=True, calls=0):
14 """ Replaces the line of code this is called from with the give line parameter.
15 This is a very bad idea and you should not use it
16 Automatically adds a newline to the end, but does not automatically add tabs.
17 """
18 meta = get_metadata(calls=calls+2)
19
20 with open(meta.filename, 'r') as f:
21 file = f.readlines()
22
23 # Not really sure of the reason for the -1.
24 if file[meta.lineno-1] == meta.code_context[0]:
25 if keepTabs:
26 tabs = re.match(r'([\t ]+)', file[meta.lineno-1 + offset])
27 if tabs:
28 line = tabs.group(1) + line
29
30 if convertTabs:
31 line = line.replace('\t', ' ')
32
33 file[meta.lineno-1 + offset] = line + '\n'
34
35 else:
36 debug(f"Error: lines don't match, not replacing line.\n\tMetadata: \"{meta.code_context}\"\n\tFile: \"{file[meta.lineno-1]}\"", clr=-1)
37 return
38
39 with open(meta.filename, 'w') as f:
40 f.writelines(file)
41
42# TODO Unfinished
43def comment(comment='', line_limit=80, char='=', start='', end='#', capitalize=False):
44 """ Replaces the call with a nicely formatted comment line next time the line is run
45 NOTE: This is a terrible, terrible function that you should NOT use.
46 I'm pretty confident it won't overwrite your source code.
47 And it's surprisingly useful.
48 But still, use at your own risk.
49 """
50 if capitalize:
51 comment = comment.upper()
52
53 meta = get_metadata(calls=2)
54
55 with open(meta.filename, 'r') as f:
56 file = f.readlines()
57
58 # Not really sure of the reason for the -1.
59 if file[meta.lineno-1] == meta.code_context[0]:
60 tabs = re.match(r'([\t ]+)', file[meta.lineno-1])
61 if tabs is None:
62 tabs = ''
63 else:
64 tabs = tabs.group(1)
65
66 if not len(char):
67 replace_line('# ' + comment, calls=1)
68 return
69 if not len(start):
70 start = char
71 if not len(end):
72 end = char
73
74 # Calculate half the distance - comment
75 half = (((line_limit // len(char)) - len(comment) - (len(tabs) // 2) - 1 - (2 if len(comment) else 0) - len(end)) // 2) - 1
76 # We want the comment to be nicely seperated
77 seperateChar = ' ' if len(comment) else ''
78 # Add them all together
79 c = '#' + start + (char * half) + seperateChar + comment + seperateChar + (char * half)
80 # If it doesn't quite reach the max number of characters, make sure it does
81 c += ((line_limit - len(c) - len(end)) // len(char)) * char
82 c = tabs + c + end
83 file[meta.lineno-1] = c + '\n'
84
85 else:
86 debug(f"Error: lines don't match, not replacing line.\n\tMetadata: \"{meta.code_context}\"\n\tFile: \"{file[meta.lineno-1]}\"", clr=-1)
87 return
88
89 with open(meta.filename, 'w') as f:
90 f.writelines(file)
91
92
93# @todo('Make this use piping and return the command output', False)
94def runCmd(args):
95 """ Run a command and terminate if it fails. """
96 try:
97 ec = subprocess.call(' '.join(args), shell=True)
98 except OSError as e:
99 print("Execution failed:", e, file=sys.stderr)
100 ec = 1
101 if ec:
102 sys.exit(ec)
103
104def center(string):
105 """ Centers a string for printing in the terminal """
106 from os import get_terminal_size
107 for _ in range(int((get_terminal_size().columns - len(string)) / 2)): string = ' ' + string
108 return string
109
110
111# TODO: Failing
112def insert_newlines(string:str, max_line_length:int) -> str:
113 """ Inserts newline characters into `string` in order to keep `string` under `max_line_length`
114 characters long, while not inserting a newline in the middle of a word
115 """
116 # split the string into a list of words
117 words = string.split()
118 # initialize the result as an empty string
119 result = ""
120 # initialize the current line as an empty string
121 current_line = words[0]
122 # iterate over the words in the list
123 for word in words[1:]:
124 # if the current line plus the next word would exceed the maximum line length,
125 # add a newline character to the result and reset the current line
126 if len(current_line) + 1 + len(word) > max_line_length:
127 result += current_line + "\n"
128 current_line = word
129 else:
130 # add the word to the current line and a space character after it
131 current_line += " " + word
132 return result
133
134def assertValue(param, *values, blocking=True):
135 paramName = get_varname(param)
136 if not called_as_decorator('assertValue'):
137 if param not in values:
138 err = ValueError(f"Invalid value for {paramName}, must be one of: {values}")
139 if blocking:
140 raise err
141 else:
142 debug(err)
143 else:
144 print('TODO: AssertValue usage as a decorator')