From e014776d1bcb6d8cbfd0ac72b038de9b5f9dc828 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Sat, 24 Dec 2016 19:18:23 -0800 Subject: [PATCH] Upload voussoirkit to pypi --- AESFile/aesfile.py | 3 +- Downloady/downloady.py | 5 +- Instathief/instathief.py | 3 +- OpenDirDL/opendirdl.py | 3 +- Pathclass/pathclass.py | 9 +- Piecewise/piecewise.py | 170 +++++++++++++++++++ README.md | 2 +- ServerReference/simpleserver.py | 3 +- SpinalTap/spinal.py | 13 +- ThreadedDL/threaded_dl.py | 3 +- Toolbox/fileswith.py | 74 +++++--- Toolbox/search.py | 50 ++++++ VoxelSphereGenerator/voxelspheregenerator.py | 26 --- _voussoirkit/phase1.py | 18 ++ _voussoirkit/phase2.py | 12 ++ _voussoirkit/setup.py | 11 ++ _voussoirkit/voussoirkit.bat | 4 + _voussoirkit/voussoirkit.py | 73 -------- _voussoirkit/voussoirkit.zip | Bin 18405 -> 0 bytes 19 files changed, 342 insertions(+), 140 deletions(-) create mode 100644 Piecewise/piecewise.py create mode 100644 Toolbox/search.py create mode 100644 _voussoirkit/phase1.py create mode 100644 _voussoirkit/phase2.py create mode 100644 _voussoirkit/setup.py create mode 100644 _voussoirkit/voussoirkit.bat delete mode 100644 _voussoirkit/voussoirkit.py delete mode 100644 _voussoirkit/voussoirkit.zip diff --git a/AESFile/aesfile.py b/AESFile/aesfile.py index c63d396..891f9a7 100644 --- a/AESFile/aesfile.py +++ b/AESFile/aesfile.py @@ -4,8 +4,7 @@ from Crypto.Cipher import AES import sys import os -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import bytestring diff --git a/Downloady/downloady.py b/Downloady/downloady.py index 91c8246..9a8828e 100644 --- a/Downloady/downloady.py +++ b/Downloady/downloady.py @@ -1,14 +1,13 @@ import argparse import os -import pyperclip # pip install pyperclip +import pyperclip import requests import sys import time import urllib import warnings -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import bytestring from voussoirkit import ratelimiter from voussoirkit import clipext diff --git a/Instathief/instathief.py b/Instathief/instathief.py index b297f0b..a726269 100644 --- a/Instathief/instathief.py +++ b/Instathief/instathief.py @@ -6,8 +6,7 @@ import os import requests import sys -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import clipext from voussoirkit import downloady diff --git a/OpenDirDL/opendirdl.py b/OpenDirDL/opendirdl.py index 9de6f86..ab28f81 100644 --- a/OpenDirDL/opendirdl.py +++ b/OpenDirDL/opendirdl.py @@ -136,8 +136,7 @@ import sys ## import tkinter import urllib.parse -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import bytestring from voussoirkit import downloady diff --git a/Pathclass/pathclass.py b/Pathclass/pathclass.py index 9e2eb87..80ab47e 100644 --- a/Pathclass/pathclass.py +++ b/Pathclass/pathclass.py @@ -55,6 +55,11 @@ class Path: def is_link(self): return os.path.islink(self.absolute_path) + def join(self, subpath): + if not isinstance(subpath, str): + raise TypeError('subpath must be a string') + return Path(os.path.join(self.absolute_path, subpath)) + @property def normcase(self): return os.path.normcase(self.absolute_path) @@ -95,8 +100,7 @@ class Path: return os.stat(self.absolute_path) def with_child(self, basename): - basename = os.path.basename(basename) - return Path(os.path.join(self.absolute_path, basename)) + return self.join(os.path.basename(basename)) def common_path(paths, fallback): @@ -167,6 +171,7 @@ def get_path_casing(path): imaginary_portion = imaginary_portion.replace(real_portion, '') imaginary_portion = imaginary_portion.lstrip(os.sep) cased = os.path.join(cased, imaginary_portion) + cased = cased.rstrip(os.sep) return cased except IndexError: return input_path diff --git a/Piecewise/piecewise.py b/Piecewise/piecewise.py new file mode 100644 index 0000000..c159763 --- /dev/null +++ b/Piecewise/piecewise.py @@ -0,0 +1,170 @@ +import argparse +import os +import sqlite3 +import sys +import threading +import time +import traceback + +from voussoirkit import bytestring +from voussoirkit import downloady + +DEFAULT_PIECE_SIZE = bytestring.MIBIBYTE +DEFAULT_THREAD_COUNT = 10 + +def init(url, localname=None, piece_size=DEFAULT_PIECE_SIZE): + localname = localname or downloady.basename_from_url(url) or str(int(time.time())) + plan = downloady.prepare_plan(url, localname, headers={'range': 'bytes=0-'}) + sql_name = localname + '.db' + if os.path.exists(sql_name): + raise ValueError('database already exists %s' % sql_name) + sql = sqlite3.connect(sql_name) + cur = sql.cursor() + cur.execute('CREATE TABLE IF NOT EXISTS meta (url TEXT)') + cur.execute('CREATE TABLE IF NOT EXISTS pieces (indx INT, start INT, end INT, done INT)') + cur.execute('INSERT INTO meta VALUES(?)', [url]) + cur.execute('CREATE INDEX IF NOT EXISTS index_done on pieces(done)') + index = 0 + while True: + start = index * piece_size + end = start + piece_size + end = min(end, plan['remote_total_bytes']) + done = 0 + cur.execute('INSERT INTO pieces VALUES(?, ?, ?, ?)', [index, start, end, done]) + start += piece_size + if start > plan['remote_total_bytes']: + break + index += 1 + sql.commit() + print('Initialized %s with %d pieces' % (sql_name, index + 1)) + +def reset(databasename): + sql = sqlite3.connect(databasename) + cur = sql.cursor() + cur.execute('UPDATE pieces SET done = 0 WHERE done == 1') + sql.commit() + +def piece_thread(entry): + try: + headers = {'range': 'bytes=%d-%d' % (entry['start'], entry['end'])} + x = downloady.download_file(entry['url'], entry['localname'], headers=headers, timeout=10) + entry['job'].finish() + return x + except: + traceback.print_exc() + entry['job'].reset() + +class Downloader: + def __init__(self, sql_name, thread_count=10): + self.thread_count = thread_count + self.sql_name = sql_name + self.localname = self.sql_name.rsplit('.db')[0] + self.sql = sqlite3.connect(self.sql_name) + self.cur = self.sql.cursor() + self.url = self.cur.execute('SELECT * FROM meta').fetchone()[0] + + def start(self): + self.cur.execute('SELECT * FROM pieces WHERE done == 0') + fetch = self.cur.fetchall() + jobs = [] + for entry in fetch: + entry = { + 'url': self.url, + 'localname': self.localname, + 'index': entry[0], + 'start': entry[1], + 'end': entry[2], + } + job = Job(entry) + jobs.append(job) + + while len(jobs) > 0: + finished_jobs = [] + active_jobs = [] + pending_jobs = [] + + for job in jobs: + if job.state == 'finished': + finished_jobs.append(job) + elif job.state == 'in progress': + active_jobs.append(job) + else: + pending_jobs.append(job) + + for job in finished_jobs: + self.cur.execute('UPDATE pieces SET done == 1 WHERE indx == ?', [job.entry['index']]) + print('finished #%d' % job.entry['index']) + self.sql.commit() + + need = self.thread_count - len(active_jobs) + while need > 0 and len(pending_jobs) > 0: + job = pending_jobs.pop() + print('starting #%d' % job.entry['index']) + job.start() + active_jobs.append(job) + need -= 1 + + jobs = pending_jobs + active_jobs + print('%d jobs in progress' % len(active_jobs)) + print('%d jobs waiting' % len(pending_jobs)) + time.sleep(2) + +class Job: + def __init__(self, entry): + self.state = 'pending' + self.entry = entry + + def start(self): + self.state = 'in progress' + self.entry['job'] = self + thread = threading.Thread(target=piece_thread, args=[self.entry]) + thread.daemon = True + thread.start() + + def finish(self): + self.state = 'finished' + + def reset(self): + self.state = 'pending' + + +def init_argparse(args): + if isinstance(args.piece_size, str): + piece_size = bytestring.parsebytes(args.piece_size) + else: + piece_size = args.piece_size + init(args.url, localname=args.localname, piece_size=piece_size) + +def reset_argparse(args): + reset(args.databasename) + +def download_argparse(args): + downloader = Downloader(args.databasename, int(args.thread_count)) + downloader.start() + +def main(argv): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers() + subparsers.required = True + subparsers.dest = 'command' + + p_init = subparsers.add_parser('init') + p_init.add_argument('url') + p_init.add_argument('localname', nargs='?', default=None) + p_init.add_argument('--piece-size', dest='piece_size', default=DEFAULT_PIECE_SIZE) + p_init.set_defaults(func=init_argparse) + + p_reset = subparsers.add_parser('reset') + p_reset.add_argument('databasename') + p_reset.set_defaults(func=reset_argparse) + + p_download = subparsers.add_parser('download') + p_download.add_argument('databasename') + p_download.add_argument('--threads', dest='thread_count', default=DEFAULT_THREAD_COUNT) + p_download.set_defaults(func=download_argparse) + + args = parser.parse_args(argv) + args.func(args) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/README.md b/README.md index ed6fc38..949dd2a 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,4 @@ For anything that isn't Reddit Note: Many projects in this repository import other projects. If you are having any Import Errors, please: - pip install https://github.com/voussoir/else/raw/master/_voussoirkit/voussoirkit.zip --upgrade + pip install voussoirkit --upgrade diff --git a/ServerReference/simpleserver.py b/ServerReference/simpleserver.py index 36e71fb..b2c910f 100644 --- a/ServerReference/simpleserver.py +++ b/ServerReference/simpleserver.py @@ -8,8 +8,7 @@ import socketserver import sys import types -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import bytestring from voussoirkit import pathclass from voussoirkit import ratelimiter diff --git a/SpinalTap/spinal.py b/SpinalTap/spinal.py index 81f2598..ab1c9e1 100644 --- a/SpinalTap/spinal.py +++ b/SpinalTap/spinal.py @@ -5,8 +5,7 @@ import os import shutil import sys -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import bytestring from voussoirkit import pathclass from voussoirkit import ratelimiter @@ -558,6 +557,7 @@ def walk_generator( path='.', callback_exclusion=None, callback_permission_denied=None, + depth_first=True, exclude_directories=None, exclude_filenames=None, recurse=True, @@ -690,6 +690,9 @@ def walk_generator( if not recurse: break - # Extendleft causes them to get reversed, so flip it first. - directories.reverse() - directory_queue.extendleft(directories) + if depth_first: + # Extendleft causes them to get reversed, so flip it first. + directories.reverse() + directory_queue.extendleft(directories) + else: + directory_queue.extend(directories) diff --git a/ThreadedDL/threaded_dl.py b/ThreadedDL/threaded_dl.py index 7285ef3..4f54ee4 100644 --- a/ThreadedDL/threaded_dl.py +++ b/ThreadedDL/threaded_dl.py @@ -3,8 +3,7 @@ import sys import threading import time -# pip install -# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip +# pip install voussoirkit from voussoirkit import clipext from voussoirkit import downloady diff --git a/Toolbox/fileswith.py b/Toolbox/fileswith.py index 598a4bd..2246de2 100644 --- a/Toolbox/fileswith.py +++ b/Toolbox/fileswith.py @@ -5,6 +5,7 @@ For example: fileswith.py *.py "import random" ''' +import argparse import fnmatch import glob import re @@ -13,24 +14,57 @@ import sys from voussoirkit import safeprint from voussoirkit import spinal -filepattern = sys.argv[1] -searchpattern = sys.argv[2] -for filename in spinal.walk_generator(): - filename = filename.absolute_path - if not fnmatch.fnmatch(filename, filepattern): - continue - matches = [] - handle = open(filename, 'r', encoding='utf-8') - try: - with handle: - for (index, line) in enumerate(handle): - if re.search(searchpattern, line, flags=re.IGNORECASE): - line = '%d | %s' % (index, line.strip()) - matches.append(line) - except: - pass - if matches: - print(filename) - safeprint.safeprint('\n'.join(matches)) - print() +def fileswith(filepattern, terms, do_regex=False, do_glob=False): + search_terms = [term.lower() for term in terms] + + def term_matches(text, term): + return ( + (term in text) or + (do_regex and re.search(term, text)) or + (do_glob and fnmatch.fnmatch(text, term)) + ) + + + generator = spinal.walk_generator(depth_first=False, yield_directories=True) + for filepath in generator: + if not fnmatch.fnmatch(filepath.basename, filepattern): + continue + handle = open(filepath.absolute_path, 'r', encoding='utf-8') + matches = [] + try: + with handle: + for (index, line) in enumerate(handle): + if all(term_matches(line, term) for term in terms): + line = '%d | %s' % (index, line.strip()) + matches.append(line) + except: + pass + if matches: + print(filepath.absolute_path) + safeprint.safeprint('\n'.join(matches)) + print() + + +def fileswith_argparse(args): + return fileswith( + filepattern=args.filepattern, + terms=args.search_terms, + do_glob=args.do_glob, + do_regex=args.do_regex, + ) + +def main(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('filepattern') + parser.add_argument('search_terms', nargs='+', default=None) + parser.add_argument('--regex', dest='do_regex', action='store_true') + parser.add_argument('--glob', dest='do_glob', action='store_true') + parser.set_defaults(func=fileswith_argparse) + + args = parser.parse_args(argv) + args.func(args) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/Toolbox/search.py b/Toolbox/search.py new file mode 100644 index 0000000..64c93e2 --- /dev/null +++ b/Toolbox/search.py @@ -0,0 +1,50 @@ +import argparse +import fnmatch +import os +import re +import sys + +from voussoirkit import safeprint +from voussoirkit import spinal + +def search(terms, match_any=False, do_regex=False, do_glob=False): + search_terms = [term.lower() for term in terms] + + def term_matches(text, term): + return ( + (term in text) or + (do_regex and re.search(term, text)) or + (do_glob and fnmatch.fnmatch(text, term)) + ) + + anyall = any if match_any else all + + generator = spinal.walk_generator(depth_first=False, yield_directories=True) + for filepath in generator: + basename = filepath.basename.lower() + if anyall(term_matches(basename, term) for term in search_terms): + safeprint.safeprint(filepath.absolute_path) + + +def search_argparse(args): + return search( + terms=args.search_terms, + do_glob=args.do_glob, + do_regex=args.do_regex, + match_any=args.match_any, + ) + +def main(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('search_terms', nargs='+', default=None) + parser.add_argument('--any', dest='match_any', action='store_true') + parser.add_argument('--regex', dest='do_regex', action='store_true') + parser.add_argument('--glob', dest='do_glob', action='store_true') + parser.set_defaults(func=search_argparse) + + args = parser.parse_args(argv) + args.func(args) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/VoxelSphereGenerator/voxelspheregenerator.py b/VoxelSphereGenerator/voxelspheregenerator.py index 241c92a..c02da6e 100644 --- a/VoxelSphereGenerator/voxelspheregenerator.py +++ b/VoxelSphereGenerator/voxelspheregenerator.py @@ -215,32 +215,6 @@ def voxelspheregenerator(WIDTH, HEIGH, DEPTH, WALL_THICKNESS=None): layer_image.save(layer_filename) - # Copy to the upper right corner. - #for y in range(math.ceil(RAD_Y)): - # #print(y) - # for x in range(math.ceil(RAD_X), WIDTH): - # #print(x, '==', (WIDTH-1) - x) - # mapped_x = (WIDTH - 1) - x - # layer_matrix[x][y] = layer_matrix[mapped_x][y] - - # Copy to the lower semicircle. - #for y in range(math.ceil(RAD_Y), HEIGH): - # #print(y) - # for x in range(WIDTH): - # #print(y, '==', (HEIGH-1) - y) - # mapped_y = (HEIGH-1) - y - # layer_matrix[x][y] = layer_matrix[x][mapped_y] - - - - #break - #layer_matrix = [['▓' if dot == dot_highlight else '░' if dot == dot_normal else ' 'for dot in sublist] for sublist in layer_matrix] - #layer_matrix = [''.join(sublist) for sublist in layer_matrix] - #layer_matrix = '\n'.join(layer_matrix) - #print(layer_matrix) - #print() - - def voxelsphere_argparse(args): height_depth_match = bool(args.height) == bool(args.depth) if not height_depth_match: diff --git a/_voussoirkit/phase1.py b/_voussoirkit/phase1.py new file mode 100644 index 0000000..e978f07 --- /dev/null +++ b/_voussoirkit/phase1.py @@ -0,0 +1,18 @@ +import shutil +import os + +PATHS = [ +'C:\\git\\else\\Bytestring\\bytestring.py', +'C:\\git\\else\\Clipext\\clipext.py', +'C:\\git\\else\\Downloady\\downloady.py', +'C:\\git\\else\\Pathclass\\pathclass.py', +'C:\\git\\else\\Ratelimiter\\ratelimiter.py', +'C:\\git\\else\\RateMeter\\ratemeter.py', +'C:\\git\\else\\Safeprint\\safeprint.py', +'C:\\git\\else\\SpinalTap\\spinal.py', +] + +os.makedirs('voussoirkit', exist_ok=True) +for filename in PATHS: + shutil.copy(filename, os.path.join('voussoirkit', os.path.basename(filename))) +open(os.path.join('voussoirkit', '__init__.py'), 'w').close() diff --git a/_voussoirkit/phase2.py b/_voussoirkit/phase2.py new file mode 100644 index 0000000..fd59f3f --- /dev/null +++ b/_voussoirkit/phase2.py @@ -0,0 +1,12 @@ +import shutil +import os + +def delete(folder): + try: + shutil.rmtree(folder) + except: + pass + +delete('dist') +delete('voussoirkit') +delete('voussoirkit.egg-info') diff --git a/_voussoirkit/setup.py b/_voussoirkit/setup.py new file mode 100644 index 0000000..bf79d3c --- /dev/null +++ b/_voussoirkit/setup.py @@ -0,0 +1,11 @@ +import setuptools + +setuptools.setup( + name='voussoirkit', + packages=['voussoirkit'], + version='0.0.7', + author='voussoir', + author_email='_', + description='voussoir\'s toolkit', + url='https://github.com/voussoir/else', +) diff --git a/_voussoirkit/voussoirkit.bat b/_voussoirkit/voussoirkit.bat new file mode 100644 index 0000000..d0a0224 --- /dev/null +++ b/_voussoirkit/voussoirkit.bat @@ -0,0 +1,4 @@ +phase1 +py setup.py register -r pypi +py setup.py sdist upload -r pypi +phase2 diff --git a/_voussoirkit/voussoirkit.py b/_voussoirkit/voussoirkit.py deleted file mode 100644 index 41fcf97..0000000 --- a/_voussoirkit/voussoirkit.py +++ /dev/null @@ -1,73 +0,0 @@ -import glob -import shutil -import os - -PACKAGE = 'voussoirkit' -PATHS = [ -'C:\\git\\else\\Bytestring\\bytestring.py', -'C:\\git\\else\\Clipext\\clipext.py', -'C:\\git\\else\\Downloady\\downloady.py', -'C:\\git\\else\\Pathclass\\pathclass.py', -'C:\\git\\else\\Ratelimiter\\ratelimiter.py', -'C:\\git\\else\\RateMeter\\ratemeter.py', -'C:\\git\\else\\Safeprint\\safeprint.py', -'C:\\git\\else\\SpinalTap\\spinal.py', -'C:\\git\\else\\WebstreamZip\\webstreamzip.py', -] - -os.makedirs(PACKAGE, exist_ok=True) - -for zipfile in glob.glob('*.zip'): - os.remove(zipfile) - -py_modules = [] -local_paths = [] - -for path in PATHS: - basename = os.path.basename(path) - module_name = '{package}.{module}'.format(package=PACKAGE, module=basename.replace('.py', '')) - py_modules.append(module_name) - local_path = os.path.join(PACKAGE, basename) - local_paths.append(local_path) - try: - os.link(path, local_path) - except FileExistsError: - pass - -print('Creating setup.py') -setup_content = ''' -import setuptools - -setuptools.setup( - author='voussoir', - name='{package}', - version='0.0.4', - description='', - py_modules=[{py_modules}], -) -''' - -py_modules = ["'%s'" % x for x in py_modules] -py_modules = ', '.join(py_modules) -setup_content = setup_content.format(package=PACKAGE, py_modules=py_modules) - -setup_file = open('setup.py', 'w') -setup_file.write(setup_content) -setup_file.close() - -print('Executing setup.py') -os.system('python setup.py sdist') - -print('Moving zip file') -zips = glob.glob('dist\\*.zip') -for zip_filename in zips: - new_zip = os.path.basename(zip_filename) - new_zip = os.path.abspath(new_zip) - shutil.move(zip_filename, new_zip) - -print('Deleting temp') -shutil.rmtree('dist') -shutil.rmtree(PACKAGE) -shutil.rmtree(glob.glob('*.egg-info')[0]) -os.remove('setup.py') -os.rename(glob.glob('*.zip')[0], 'voussoirkit.zip') diff --git a/_voussoirkit/voussoirkit.zip b/_voussoirkit/voussoirkit.zip deleted file mode 100644 index 635ae9a46c9a0dab49fd806a8c723ac89f221eb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18405 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 N{7l@UBJ|hY{{zV*m3;sJ