first commit

This commit is contained in:
kendfss 2021-02-20 02:21:40 +01:00
commit ce858cdc8b
23 changed files with 2693 additions and 0 deletions

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2020 eli2and40
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# sl4ng
This package serves as a workflow booster for emailing, iterating, and a veritable smorgasboard of cases scattered in between.
You want persistant data but Pickle and Dill ask one too many damned questions?
sl4ng.persistance.save
sl4ng.persistance.load
You be writing generators so nice that you want to use them twice, thrice, or indefinitely? We gotchu!
sl4ng.types.regenerator
You're on the brink of the creative breakthrough of the ages but you'll end it all in a fit of rage if you accidentally overwite your projects ever again? We gotchu!
sl4ng.files.paths.namespacer
You want to look at an object's source code but inspect.getsource makes a mess of rendering things in your REPL, or perhaps you want to jump straight into its package-folder or source-file?
sl4ng.debug.getsource
sl4ng.debug.pop
You want to want to see if your iterable is the codomain of a constant function? We gotchu
sl4ng.functional.eq
You really like your dictionary, but it's got some duplicate values? We.. Got.. Chu!
sl4ng.iteration.deduplicate
You've read this far and think "Damn, son, this package looks diggity fresh, but some of those functions are deeply nested"? We gotchu
Everything is imported to init

6
pyproject.toml Normal file
View File

@ -0,0 +1,6 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"

34
setup.py Normal file
View File

@ -0,0 +1,34 @@
from setuptools import setup, find_packages
with open('README.md', 'r') as fob:
long_description = fob.read()
setup(
name='sl4ng',
version='0.0.1',
author='Kenneth Elisandro',
author_email='eli2and40@tilde.club',
url='https://tildegit.org/eli2and40/sl4ng',
# packages=find_packages(),
packages=['sl4ng'],
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
long_description=long_description,
long_description_content_type='text/markdown',
keywords='utilities productivity',
license='MIT',
requires=[
'pyperclip',
'dill',
'psutil',
'send2trash',
'tqdm',
'filetype',
],
# py_modules=['sl4ng'],
python_requires='>=3.8',
)

157
sl4ng/__init__.py Normal file
View File

@ -0,0 +1,157 @@
from typing import Iterable, Any, Iterator, Sequence
import os
from .debug import *
from .files import *
from .functional import *
from .iteration import *
from .maths import *
from .persistance import *
from .stats import *
from .strings import *
from .system import *
from .types import *
from .web import *
HERE,THIS = os.path.split(__file__)
# def sgm(func:type(lambda:1),iterable:Iterable,arity:int=2) -> Any:
# """Returns the sum of a iterable through a function, or the range(n) through a function for some integer n
# Dependencies: itertools.chain
# In: function, iterable
# Out: value"""
# from itertools import chain,combinations
# if hasattr(iterable,'__iter__') or hasattr(iterable,'__next__'):
# if isinstance(iterable,str):
# if iterable.isnumeric():
# return sum(func(int(i)) for i in iterable)
# elif all(hasattr(i,'__len__') for i in iterable):
# if all(len(x)==len(y) for x,y in combinations(iterable,2)):
# return sum(
# func(
# *(
# v[i] for v in iterable
# )
# ) for i in range(len(iterable[0]))
# )
# else:
# return sum(func(pair) for pair in combinations(iterable,arity))
# elif isinstance(iterable,int):
# return sum(func(i) for i in range(iterable))
# elif isinstance(iterable,float):
# return sum(sgm(func,str(iterable).split('.')[0]),sgm(func,str(iterable).split('.')[1]))
# def primes2(n):
# """Generates a list of primes with n terms
# Dependencies: factors (from meta)
# In: int
# Out: list"""
# result = []
# index = 2
# while len(result) <= n:
# if len(factors(index)) == 2:
# result.append(index)
# index += 1
# return result
if __name__ == "__main__":
# t = range(10_000_000_000_000)
# t = range(1_000_000_000)
# arg = (i for i in t)
# arg = (1 for i in t)
# arg = t
# print(eq(arg))
# show((list(range(10000)) for i in range(10)),1,False,2,' ',False,True)
# print(entropy(int(i) for i in y))
# v = 15
# l = []
# for j in range(1,10):
# print(sgm(lambda *args:pipe(args),[[1,2,3] for i in range(j)]))
# l.append(sgm(lambda *args:pipe(args),[range(1,v) for i in range(j)]))
# print(l)
# print(gcd2(l))
# [print(i,max(j for j in factors(i) if i>j)) for i in l]
# print(guid())
# help(guid)
# ffplay(r"C:\Users\Kenneth\Downloads\byextension\wav")
# ffplay(r'F:\Programs\Project Files\FL\2019 old\bisto\bonnuci\blackburn_2\submixes\blackburn_2_Master.wav',hide=False)
# ffplay(r"C:\Users\Kenneth\Downloads\byextension\wav\alixperez-crooklyn.wav*C:\Users\Kenneth\Downloads\byextension\wav\alixperez-melanie.wav*C:\Users\Kenneth\Downloads\byextension\mkv\Knucks - Home-eKb0XPEnwOk.mkv*C:\Users\Kenneth\Downloads\byextension\mkv\UK Garage - M Dubs - 'Bump n Grind'-fFjWiAR1c-I.mkv*C:\Users\Kenneth\Downloads\byextension\mkv\Ghost - The Club-4_uggscguzs.mkv*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Morning Sun _ Playing Tricks\02 Playing Tricks.mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Morning Sun _ Playing Tricks\01 Morning Sun.mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Dub Rock _ Love Bug\01 Dubrock.mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Dub Rock _ Love Bug\02 Lovebug (feat. Specific).mp3*C:\Users\Kenneth\Downloads\music\music\Unitz\The Drop _ Mornin Blues\01 The Drop.mp3*C:\Users\Kenneth\Music\Collection\Archy Marshall\A New Place 2 Drown\04 Ammi Ammi.mp3*C:\Users\Kenneth\Music\Collection\Archy Marshall\A New Place 2 Drown\09 Empty Vessels.mp3*C:\Users\Kenneth\Downloads\music\music\Belangeo\Unknown Album\belangeo_Tribal.ogg*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\11 Suffer In Silence (feat. Zero T).mp3*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\03 Fade Away.mp3*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\08 I_m Free.mp3*C:\Users\Kenneth\Music\Collection\Alix Perez\1984\15 Hemlines (feat. Sabre).mp3*C:\Users\Kenneth\Downloads\music\music\Alix Perez\Magnolias _ Backlash\02 Backlash.mp3*C:\Users\Kenneth\Downloads\music\music\Las\Outlaw EP\03 Drumspeak.m4a*C:\Users\Kenneth\Downloads\music\music\Las\Backyard _ Tic\02 Tic.mp3*C:\Users\Kenneth\Downloads\music\music\Las\Backyard _ Tic\01 Backyard.mp3*C:\Users\Kenneth\Downloads\music\music\Las\Las x Mikael - Single\01 Dem Break.m4a*C:\Users\Kenneth\Music\Collection\LAS\Your Eyes EP\03 Aspect.mp3*C:\Users\Kenneth\Music\Collection\LAS\Your Eyes EP\04 Lesson.mp3*F:\Programs\Project Files\FL\2019 old\bisto\bonnuci\blackburn_2\submixes\blackburn_2_Master.wav",fullscreen=False,randomize=False,hide=False)
# ffplay('*'.join(i for i in gather(r'c:\users\kenneth\downloads\music',ext='mp3',names=False)))
# ffplay('*'.join(i for i in gather(r'c:\users\kenneth\music\collection\alix perez',names=False)),fullscreen=False,hide=False,randomize=False)
# ffplay(r'C:\Users\Kenneth\Downloads\byextension\wav*c:\users\kenneth\music\collection\alix perez',randomize=False)
# ffplay(
# '*'.join((
# r"C:\Users\Kenneth\Downloads\byextension\mp3\Ryan Patrick Maguire - $†@®.mp3",
# r"C:\Users\Kenneth\Downloads\byextension\mp3\Ryan Patrick Maguire - free_language.mp3",
# r"C:\Users\Kenneth\Downloads\byextension\mp3\Ryan Patrick Maguire - moDernisT.mp3"
# )))
# ffplay(r'C:\Users\Kenneth\Downloads\byextension\mp3\SAR01.mp3*C:\Users\Kenneth\Downloads\byextension\mp3\SAR262.mp3')
# compare(succ,pred)
# tst = {
# 'a':1,
# 'b':{},
# 'c':{},
# 'd':2,
# 'e':[],
# 'f':[],
# 'g':1,
# 'h':1,
# }
# print(deduplicate(tst))
# for i in range(10): print(agora(),agora(True))
# f = r'e:\projects\monties\2020\fileManagement\knownExtensions.pkl'
# [print(i,f.index(i),i==j) for i,j in zip(set(f),shuffle(set(f))) if i==j]# else print(i,j)]# in set(f) for j in shuffle(set(f))]
# from math import log
# help(log)
# print((val:=factorial(len(f))),log(val,10),pyramid(log(val,10)),sep='\n',end='\n\n')
# n = 1
# while (k:=pyramid(n))>0:
# k /= n
# n += 1
# print(n-1,k)
# print(n,pyramid(n))
# help(straw(__file__,False)[0])
# print(straw(__file__,text=False,lines=False))
# for i in straw(__file__,False):
# print(i.decode('utf-8'))
# priter(f,1)
# priter(shuffle(f))
# print(empty(f))
# print(os.curdir)
# print(shutil.get_unpack_formats())
# print(unpack(r"C:\Users\Kenneth\Downloads\music\music.rar"))
# extractRar(r"C:\Users\Kenneth\Downloads\music\music.rar")
# print(nameSpacer(__file__+'loll'))
# unpack(r"C:\Users\Kenneth\Downloads\music\music.rar")
pass

231
sl4ng/debug.py Normal file
View File

@ -0,0 +1,231 @@
from typing import Any, Iterable
import sys, winsound, os, time, inspect
import pyperclip
from .types import SineWave, generator, regurge, function
separator = " ".join(70*'-')
spaceprint = lambda x: print(x, "\n{}".format(separator))
main = '__name__ == "__main__"'
def tipo(inpt:Any=type(lambda:0), keep_module:bool=False) -> str:
"""
Return the name of an object's type
Dependencies: None
In: object
Out: str
"""
if keep_module:
return str(type(inpt)).split("'")[1]
return str(type(inpt)).split("'")[1].split('.')[-1]
def beeper(inpt:int) -> SineWave:
"""Produces a series of beeps of increasing frequency
Dependencies: Beep(a function from the winsound module)
In: (number of beeps desired)
Out: Series of beeps of increasing frequency"""
for i in range(inpt):
frequency = 65*2**(i)
duration = 500
winsound.Beep(frequency, duration)
def syspath():
"""Displays the path
Dependencies: sys, sys.path
In: (N/A)
Out: string with formatted list"""
return f"current:\n{sys.path}"
def padd(inpt):
"""Adds a string to the path
Dependencies: sys, sys.path
In: (desired address)
Out: N/A"""
sys.path.append(inpt)
return("done :)\n{0}".format(sys.path))
def test(func:type(lambda:1), unsortedlist:tuple) -> float:
"""Test measures the amount of time needed to run a function on a given Input
dependencies: time.clock() (perf_counter due to deprecation )
In: function, input
Out: Difference in time between the call and return of the function
lifted from davejm's sorting module on github"""
#Set copy equal to a new copy (different mem. location) of unsortedList to preserve current list
#using the [:] split command which returns a copy of the whole list but as a new object
from time import perf_counter as clock
copy = unsortedlist[:]
start = clock() #Set start time. TODO - May convert to using timit module.
func(copy) #Run bubble or shuttle sort with copy of unsorted list as argument
duration = clock() - start #Work out how long the execution of algorithm took and set to duration
return duration
def modir(module:type(os), copy:bool=True) -> str:
"""
Returns the full path of the directory inwhich a given module is located
Dependencies: os, pyperclip
In: module object, boolean (t-copy,f-not copy)
Out: String or Boolean
Example:
import matplotlib.pyplot as plt
print(modir(plt),modir(plt,False),sep='\n')
Prints
None
E:\Languages\Python37-64\Lib\site-packages\matplotlib
"""
pth = os.sep.join(module.__file__.split(os.sep)[:-1])
return pth if not copy else pyperclip.copy(pth)
def printer(iterable:Iterable, indent:int=0) -> None:
"""
Prints each element of an iterable on a new line, features an optional indent argument
"""
assert isinstance(indent, int), "Indentation level must be a positive integer"
for i in iterable:
print(indent*"\t"+f"{i}")
def tryimport(name:str, pack:str=None, substitute:bool=False, subpack:str=None, silent:bool=False) -> Any:
"""
Import a package, or something from within it, failing its installation import an optional substitute.
Example:
trasher = tryimport('send2trash',pack='send2trash',substitute='remove',subpack='os')
would try to 'import send2trash from send2trash' and be ready to 'import remove from os' if send2trash is not installed, and assign the result to the 'trasher' variable
Dependencies: None
Arguments: name,pack=None,substitute=False,subpack=None; all strings
Outputs: module/object
"""
try:
message = f'from {pack} import {name}' if pack else f'import {name}'
exec(message)
return eval(name)
except ModuleNotFoundError:
if substitute:
if subpack:
return tryimport(substitute, subpack)
else:
return tryimport(substitute)
elif subpack:
return tryimport(subpack)
else:
if not silent:
raise ModuleNotFoundError
def aspectRatio(x:int, y:int, w:int=None, h:int=None) -> float:
"""
Compute aspect ratio and scale to desired size
aspectRatio(a, b) ~> a/b
aspectRatio(a, b, w=c) ~> (c,round(c*(b/a)))
aspectRatio(a, b, h=c) ~> (round(c*(a/b)), c)
aspectRatio(a, b, w=c, h=d) ~> a/b==c/d
Dependencies: None
In: x, y, w, h: numbers
Outs: int or tuple
"""
return w/h==x/y if w and h else (w, (w*(y/x))) if w else ((h*(x/y)), h) if h else x/y
def show(iterable:Iterable[Any], indentation:int=0, enum:bool=False, first:int=1, indentor:str='\t', tail=True, head=False, file=sys.stdout, sep:str='') -> None:
"""
Print each element of an array.
>>> show(zip('abc','123'))
('a', '1')
('b', '2')
('c', '3')
>>>
"""
# consumable = regurge(iterable)
consumable = iter(regurge(iterable))
if (wasstr:=isinstance(file, str)):
file = open(file)
print('\n', file=file) if head else None
for i, j in enumerate(consumable, first):
print(
(
f"{indentation*indentor}{j}",
f"{indentation*indentor}{i}\t{j}"
)[enum],
sep=sep,
file=file
)
# if sep:
# print(sep)
print('\n', file=file) if tail else None
if wasstr:
file.close()
def pop(arg:Any=None, file:bool=True, silent:bool=False) -> str:
"""
Open the folder containing a given module, or object's module, or the content at the path of the given string
Opens current working directory if no object is given
Open the module's file if it is given
If a path is given, it will be opened.
Return the path which is opened
This will raise an attribute error whenever there is no file to which the object/module is imputed
"""
module = type(os)
if arg:
if isinstance(arg, str):
path = arg
elif isinstance(arg, module):
path = arg.__file__
else:
mstr = arg.__module__
if (top:=mstr.split('.')[0]) in globals().keys():
m = eval(mstr)
else:
t = exec(f'import {top}')
m = eval(mstr)
path = m.__file__
if not file:
path = os.path.dirname(path)
else:
path = os.getcwd()
if not silent:
os.startfile(path)
return path
def hasDocs(module:type(os), copy:bool=True) -> bool:
"""
Returns directory of a given module's documentation... if it can be found by lesser mortals such as I
Dependencies: os, pyperclip
In: module object, boolean (t-copy,f-not copy)
Out: String or Boolean
"""
base = modir(module, False)
directory = os.path.join(base, 'docs') if os.path.exists(os.path.join(base, 'docs')) else os.path.join(base, 'doc')
d = False if not os.path.exists(directory) else directory
return d if not copy else pyperclip.copy(d)
def getsource(obj:Any, *args, copy:bool=False, **kwargs) -> str:
"""
Wrapper on inspect.getsource which uses .show to present the text more readably and pyperclip.copy to copy said text to the clipboard.
"""
text = inspect.getsource(obj).splitlines()
if copy:
pyperclip.copy('\n'.join(text))
return
show(text, *args, **kwargs)
if __name__=='__main__':
pass

13
sl4ng/files/__init__.py Normal file
View File

@ -0,0 +1,13 @@
from .paths import *
from .operations import *
from .tasks import *
if __name__ == "__main__":
pass

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

57
sl4ng/files/operations.py Normal file
View File

@ -0,0 +1,57 @@
import os, subprocess
from send2trash import send2trash
from tqdm import tqdm
from .paths import nameUpdater
def convert(file:str, format:str='.wav', bitRate:int=450, delete:bool=False, options:str='') -> str:
"""
Convert an audio file
"""
trash = discard
os.chdir(os.path.split(file)[0])
_title = lambda file: file.split(os.sep)[-1].split('.')[0]
_new = lambda file, format: nameUpdater(_title(file)+format)
_name = lambda file: file.split(os.sep)[-1]
format = '.' + format if '.' != format[0] else format
name = _title(file)
new = _new(file, format)
cmd = f'ffmpeg -y -i "{file}" -ab {bitRate*1000} "{new}"' if bitRate != 0 else f'ffmpeg {options} -y -i "{file}" "{new}"'
announcement = f"Converting:\n\t{file} --> {new}\n\t{cmd=}"
print(announcement)
subprocess.run(cmd)
print('Conversion is complete')
if delete:
trash(file)
print(f'Deletion is complete\n\t{new}\n\n\n')
return new
def move(file:str, destination:str) -> None:
"""
Move a file to a given directory
"""
nmxt = os.path.split(file)[-1]
ext = os.path.splitext(nmxt)[-1].replace('.', '')
with open(file, 'rb') as src:
src = tuple(i for i in src)
with open(destination, 'wb') as dst:
print(f'moving {nmxt.split(".")[0]} to the "{ext}" directory')
for datum in tqdm(src):
dst.write(datum)
def discard(path:str, recycle:bool=True) -> None:
"""
Remove an address from the file-system
"""
fb = (os.remove, send2trash)
first, backup = fb if not recycle else fb[::-1]
try:
first(path)
except PermissionError:
backup(path)

151
sl4ng/files/paths.py Normal file
View File

@ -0,0 +1,151 @@
from typing import Iterable
import os, sys, time
defaultScriptDirectory = r'e:\projects\monties'
commons = {
'music': os.path.join(os.path.expanduser('~'), 'music', 'collection'),
'images': os.path.join(os.path.expanduser('~'), 'pictures'),
'pictures': os.path.join(os.path.expanduser('~'), 'pictures'),
'pics': os.path.join(os.path.expanduser('~'), 'pictures'),
'videos': os.path.join(os.path.expanduser('~'), 'videos'),
'ytdls': {
'music': {
'singles': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'music', 'singles'),
'mixes': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'music', 'mixes'),
'albums': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'music', 'album'),
},
'graff': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'graff'),
'bullshit': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'bullshitters'),
'bull': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'bullshitters'),
'code': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'cscode'),
'cs': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'cscode'),
'maths': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'maths'),
'math': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'maths'),
'movies': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'movies'),
'other': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'other'),
'physics': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'physics'),
'phys': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'physics'),
'politics': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'politics'),
'pol': os.path.join(os.path.expanduser('~'), 'videos', 'ytdls', 'politics'),
},
'documents': os.path.join(os.path.expanduser('~'), 'documents'),
'docs': os.path.join(os.path.expanduser('~'), 'documents'),
'downloads': os.path.join(os.path.expanduser('~'), 'downloads'),
'desktop': os.path.join(os.path.expanduser('~'), 'desktop'),
'books': os.path.join(os.path.expanduser('~'), 'documents', 'bookes'),
'monties': os.path.join(defaultScriptDirectory, str(time.localtime()[0])),
'scripts': defaultScriptDirectory,
'demos': os.path.join(defaultScriptDirectory, 'demos'),
'site': os.path.join(sys.exec_prefix, 'lib', 'site-packages'),
'home': os.path.expanduser('~'),
'user': os.path.expanduser('~'),
'root': os.path.expanduser('~'),
'~': os.path.expanduser('~'),
}
os.makedirs(commons['monties'], exist_ok=True)
def delevel(path:str, steps:int=1) -> str:
"""
This will climb the given path tree by the given number of steps.
No matter how large the number of steps, it will stop as soon as it reaches the root.
Probably needs revision for paths on systems which hide the root drive.
example
>>> for i in range(4):print(delevel(r'c:/users/admin',i))
c:/users/admin
c:/users
c:/
c:/
dependencies: os.sep
"""
while steps and (len(path.split(os.sep))-1):
path = os.sep.join((path.split(os.sep)[:-1]))
steps -= 1
return path if not path.endswith(':') else path+os.sep
def namespacer(path:str, sep:str='_', start:int=2) -> str:
"""
Returns a unique version of a given string by appending an integer
example:
tree:
/folder
/file.ext
/file_2.ext
>>> nameSpacer('file', sep='-', start=2)
file-2.ext
>>> nameSpacer('file', sep='_', start=2)
file_3.ext
>>> nameSpacer('file', sep='_', start=0)
file_0.ext
"""
id = start
oldPath = path[:]
while os.path.exists(path): ##for general use
newPath = list(os.path.splitext(path))
if sep in newPath[0]:
if newPath[0].split(sep)[-1].isnumeric():
# print('case1a')
id = newPath[0].split(sep)[-1]
newPath[0] = newPath[0].replace(f'{sep}{id}', f'{sep}{str(int(id)+1)}')
path = ''.join(newPath)
else:
# print('case1b')
newPath[0] += f'{sep}{id}'
path = ''.join(newPath)
id += 1
else:
# print('case2')
newPath[0] += f'{sep}{id}'
path = ''.join(newPath)
id += 1
return path
nameUpdater = nameSpacer = name_spacer = namespacer
def mcd(args:Iterable[str], go_back:bool=False, recursive:bool=True, overwrite:bool=False) -> str:
"""
recursively create and enter directories.
go_back:
if set to True, the process will return to the starting directory
recursive:
if set to false, all directories will be created in the starting directory
eg
each of the following calls create the following tree:
dir-1
dir0: starting directory
dir1
dir2
dir3
dir4
dir5
>>> mcd('dir1 dir2 .. dir3 .. .. dir4 .. .. dir5'.split())
>>> mcd('dir1/dir2 ../dir3 ../../dir4 ../../dir5'.split())
"""
home = os.getcwd()
for arg in args:
arg = nameSpacer(arg) if arg!='..' and not overwrite else arg
os.makedirs(arg, exist_ok=True)
os.chdir(arg if recursive else home)
last_stop = home if go_back else os.getcwd()
os.chdir(last_stop)
return last_stop
if __name__ == "__main__":
here, this = os.path.split(__file__)
this_2 = nameSpacer(this)
if not os.path.exists(this_2):
with open(this_2, 'x') as f:
print(nameUpdater(this, '-', 2))
print(nameUpdater(this, '_', 2))
print(nameUpdater(this, '_', 0))
os.remove(this_2)
# [*map(os.remove, (this, this_2))]

185
sl4ng/files/tasks.py Normal file
View File

@ -0,0 +1,185 @@
from typing import Sequence, Iterable
import re, os, subprocess
import filetype as ft
from ..types import generator
from ..iteration import shuffle
def search(keyword:str, directory:str, extension:str='', walk=True, **kwargs) -> generator:
"""
Find files matching the given keyword within a directory
"""
if re.search('\s', keyword):
keyword = map(lambda x: x.strip(), keyword.split())
if not isinstance(keyword, str):
keyword = '|'.join(map(re.escape, keyword))
else:
keyword = re.escape(keyword)
return filter(
lambda file:re.search(keyword, file.split(os.sep)[-1], re.I),
gather(directory, ext=extension, names=False, walk=walk, **kwargs)
)
def sortbysize(files:Iterable[str]=None) -> list:
"""
Sort a collection of file paths by the size of the corresponding files (largest to smallest)
"""
files = [os.getcwd(), list(files)][bool(files)]
size = lambda file: os.stat(file).st_size
out = []
while len(files)>0:
sizes = set(size(file) for file in files)
for file in files:
if size(file) == max(sizes):
out.append(file)
files.remove(file)
return out
def gather(folder:str=None, names:bool=False, walk:bool=True, ext:str=None) -> generator:
"""
Generate an iterable of the files rooted in a given folder
"""
folder = [os.getcwd(), folder][bool(folder)]
if walk:
if ext:
ext = ext.replace('.', '')
pred = [i for i in ',`* ' if i in ext]
pattern = '|'.join(f'\.{i}$' for i in ext.split(pred[0] if pred else None))
pat = re.compile(pattern, re.I)
for root, folders, files in os.walk(folder):
for file in files:
if os.path.isfile(p:=os.path.join(root, file)) and pat.search(file) and file!='NTUSER.DAT':
yield file if names else p
else:
for root, folders, titles in os.walk(folder):
for t in titles:
if os.path.exists(p:=os.path.join(root, t)):
yield t if names else p
else:
if ext:
ext = ext.replace('.', '')
pred = [i for i in ',`* ' if i in ext]
pattern = '|'.join(f'\.{i}$' for i in ext.split(pred[0] if pred else None))
pat = re.compile(pattern, re.I)
for file in os.listdir(folder):
if os.path.isfile(p:=os.path.join(folder, file)) and pat.search(file) and file!='NTUSER.DAT':
yield file if names else p
else:
for file in os.listdir(folder):
if os.path.isfile(p:=os.path.join(folder, file)):
yield file if names else p
def straw(path:str, text:bool=True, lines:bool=False) -> [str, list]:
"""
Extract the text, or bytes if the keyword is set to false, from a file
::text::
text or bytes?
::lines::
split text by line or return raw?
"""
if os.path.isfile(path):
mode = "rb r".split()[text]
with open(path, mode) as f:
return f.readlines() if lines else f.read()
def unarchive(path:str, destination:str=None, app:str='rar') -> None:
"""
Extract an archive to a chosen destination, or one generated based on the name of the archive
App refers to the comandlet you wish to invoke via subprocess.run
"""
options = {
'tar':'-x -f',
'rar':'e -or -r',
'winrar':'',
}
if destination != None:
os.makedirs(destination, exist_ok=True)
os.chdir(destination)
subprocess.run(f'{app} {options[app]} "{path}" ')
else:
destination = os.path.splitext(path)[0]
src = os.path.join('.', os.path.split(path)[1])
print(destination, path, src)
os.makedirs(destination, exist_ok=True)
os.chdir(destination)
subprocess.run(f'{app} {options[app]} "{src}" ')
extractRar = unarchive
def empty(path:str, make:bool=False) -> bool:
"""
Check if a given file or folder is empty or not with the option to create it if it doesn't exit
"""
if os.path.exists(path):
if os.path.isfile(path):
with open(path, 'rb') as f:
return not bool(len(tuple(i for i in f)))
elif os.path.isdir(path):
return not bool(len(os.listdir(file)))
elif make:
if os.path.splitext(path)[-1]:
x = open(path, 'x')
x.close()
else:
os.makedirs(path, exist_ok=True)
return True
def ffplay(files:Sequence[str], hide:bool=True, fullscreen:bool=True, loop:bool=True, quiet:bool=True, randomize:bool=True, silent:bool=False) -> None:
"""
Play a collection of files using ffmpeg's "ffplay" cli
If entering files as a string, separate each path by an asterisk (*), othewise feel free to use any iterator
-loop {f"-loop {loop}" if loop else ""}
"""
# fullscreen = False if hide else fullscreen
namext = lambda file: os.path.split(file)[1]
nome = lambda file: os.path.splitext(namext(file))[0]
ext = lambda file: os.path.splitext(file)[1]
isvid = lambda file: ft.match(file) in ft.video_matchers
vidtitle = lambda vid: '-'.join(i.strip() for i in vid.split('-')[:-1])
albumtrack = lambda file: bool(re.search(f'\d+\s.+{ext(file)}', file, re.I))
attitle = lambda file: ' '.join(i.strip() for i in nome(file).split(' ')[1:])
aov = lambda file: albumtrack(file) or isvid(file)
title = lambda file: ''.join(i for i in os.path.splitext(namext(file)[1])[0] if i not in '0123456789').strip()
windowtitle = lambda file: [namext(file), [attitle(file), vidtitle(file)][isvid(file)]][aov(file)]
play = lambda file: subprocess.run(f'ffplay {("", "-nodisp")[hide]} -window_title "{windowtitle(file)}" -autoexit {"-fs" if fullscreen else ""} {"-v error" if quiet else ""} "{file}"')
files = files.split('*') if isinstance(files,str) else files
if loop:
while (1 if loop==True else loop+1):
files = shuffle(files) if randomize else files
for i,f in enumerate(files, 1):
if os.path.isdir(f):
fls = [os.path.join(f, i) for i in gather(f, names=False)]
for j,file in enumerate(fls, 1):
name = os.path.split(file)[1]
print(f'{j} of {len(fls)}:\t{name}') if not silent else None
ffplay(file, hide, fullscreen, False, quiet, randomize, True)
else:
folder,name = os.path.split(f)
print(f'{i} of {len(files)}:\t{name}') if not silent else None
play(f)
loop -= 1
else:
files = shuffle(files) if randomize else files
for i, f in enumerate(files, 1):
if os.path.isdir(f):
fls = [os.path.join(f, i) for i in gather(f, names=False)]
for j, file in enumerate(fls, 1):
name = os.path.split(file)[1]
print(f'{j} of {len(fls)}:\t{name}') if not silent else None
ffplay(file, hide, fullscreen, False, quiet, randomize, True)
else:
print(f'{i} of {len(files)}:\t{title(f)}') if not silent else None
play(f)
size = lambda file: os.stat(file).st_size

94
sl4ng/functional.py Normal file
View File

@ -0,0 +1,94 @@
from typing import Iterable, Any, Callable
import time
from .types import function, generator, regurge
def repeat(func:function, inpt:Any, times:int=2, **kwargs) -> type:
"""
Get the output of recursive calls of a function some number of times
Dependencies: None
In: function,input,times[=1]
Out: Function call
repeat(lambda x: x+1,0)
2
repeat(lambda x: x+1,0,1)
1
"""
for i in range(times):
inpt = func(inpt, **kwargs)
return inpt
recursor = repeat
def eq(*args:Iterable[Any]) -> bool:
"""
Check if arguments are equal
Will always return True if the only argument given is not an iterable
"""
if len(args) == 1:
if hasattr(arg:=args[0], '__iter__'):
return eq(*arg)
return True
args = iter(args)
arg0 = next(args)
for i in args:
if i != arg0:
return False
return True
def clock(func:function, value:bool=False, name:bool=False) -> function:
"""
A decorator for benchmarking functions
:name:
option to print the name of the function with the time taken to call it
:value:
return the value of the function instead of the period
taken from
Mari Wahl, mari.wahl9@gmail.com
University of New York at Stony Brook
"""
def wrapper(*args:Any, **kwargs:Any) -> Any:
t = time.perf_counter()
res = func(*args, **kwargs)
delta = time.perf_counter()-t
if name:
print(f"{func.__name__}\n\t{delta}")
else:
print(f"d={delta}")
return res if value else delta
return wrapper
benchmark = clock
def imap(argument:Any, *functions:Callable) -> generator:
"""
Converse of a map. Yield calls of the functions with the object as an argument
Generator safe
"""
iterates = hasattr(object, '__iter__')
for func in functions:
yield func((argument, regurge(argument))[iterates])
def rmap(argument:Any, *functions:Callable) -> Any:
"""
Recursively call functions on an argument. Return the result of the last call
Generator safe
"""
functions = iter(functions)
iterates = hasattr(object, '__iter__')
result = next(functions)((argument, regurge(argument))[iterates])
for func in functions:
result = func((result, regurge(result))[iterates])
return result

553
sl4ng/iteration.py Normal file
View File

@ -0,0 +1,553 @@
# Statisticals checked against: http://www.alcula.com/calculators/statistics/
from itertools import tee, islice, chain, combinations
from typing import Iterable, Any, Sequence, List, Callable
from copy import deepcopy
from math import log
import random
from .types import generator, regurge, regenerator
from .maths import binomial
from .functional import eq
def xrange(stop:complex, start:complex=0, step:complex=1, reverse:bool=False) -> generator:
"""
xrange(start, stop, step)
An implementation of the old xrange generator
examples:
>>> [*xrange(2)]
[0, 1]
>>> [*xrange(2, 1)]
[1]
>>> [*xrange(2, 1, 0.5)]
[1, 1.5]
>>> [*xrange(2, 1, 0.5, reverse=True)] #
[1.5, 1.0]
>>> [*xrange(10, 1, 1, True)]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
todo:
add the ability to use single-argument negative stops?
"""
if reverse:
while start<stop:
yield stop - step
stop -= step
else:
while start<stop:
yield start
start += step
def tight(iterable:Iterable[Any]) -> generator:
"""
Produce a new iterator of unique elements from a given array
will consume a generator
"""
consumable = list(regurge(iterable))
# consumable = list(consumable)
yielded = []
for i in iterable:
if not i in yielded:
yield i
yielded.append(i)
def walks(iterable:Iterable[Any], length:int=2) -> generator:
"""
Break an iterable into len(iterable)-length steps of the given length, with each step's starting point one after its predecessor
example
>>> for i in walks(itertools.count(),2):print(''.join(i))
(0, 1)
(1, 2)
(2, 3)
# etc.
Inspired by the hyperoperation 16**2[5]2
Extended to generators with cues from more_itertools' "stagger"
Extended to infinite generators by pedantry
"""
consumable = regurge(iterable)
t = tee(consumable, length)
yield from zip(*(it if n==0 else islice(it, n, None) for n, it in enumerate(t)))
def flatten(iterable:Iterable) -> generator:
"""
Flatten a 2d iterable
Example:
>>> list(flatten([[1, 2], [3, 4]]))
[1, 2, 3, 4]
based on:
https://pythonprinciples.com/challenges/Flatten-a-list/
"""
consumable = regurge(iterable)
for i in consumable:
if hasattr(i, '__iter__') or hasattr(i, '__next__'):
for j in i:
yield j
else:
yield i
def nopes(iterable:Iterable[Any], yeps:bool=False) -> generator:
"""
if yeps==False
Return the indices of all false-like boolean values of an iterable
Return indices of all true-like boolean values of an iterable
example
t = (0,1,0,0,1)
>>> tuple(nopes(t))
(0,2,3)
>>> tuple(nopes(t,True))
(1,4)
"""
consumable = regurge(iterable)
for i, j in enumerate(consumable):
if (not j, j)[yeps]:
yield i
def pull(iterable:Iterable, stop:int, start:int=0, step:int=1) -> tuple:
"""
Return 'stop' elements from an effective cycle of the iterable, using only those whose modulus is greater than 'start'
Example:
pull('boris',5)
('b', 'o', 'r', 'i', 's')
pull('boris',6)
('b', 'o', 'r', 'i', 's', 'b')
pull('boris',6,1)
('o', 'r', 'i', 's', 'b', 'o')
pull('boris',6,1,2)
('r', 's', 'o')
"""
consumable = regurge(iterable)
consumable = list(consumable)
return tuple(consumable[i%len(consumable)] for i in range(0, stop+start, step) if i>=start)
def deduplicate(unhashable:Iterable) -> dict:
"""
Because dictionaries seem to be less hashable than lists, which are also formally unhashable
This will consume a generator
"""
if isinstance(unhashable, dict):
trimmed = {}
for key, val in unhashable.items():
if val not in trimmed.values():
trimmed[key] = val
return trimmed
else:
raise TypeError(f'Protocol for your {tipo(unhashable)} is pending')
def band(iterable:Iterable) -> float:
"""
Returns the extrema of the given iterable
"""
consumable = regurge(iterable)
consumable = list(consumable)
return min(consumable), max(consumable)
def bandGap(iterable:Iterable) -> float:
"""
Returns the breadth of a given iterable of elements overwhich subtraction, max, and min, are well defined
"""
consumable = regurge(iterable)
consumable = list(consumable)
return max(consumable) - min(consumable)
def options(iterable:Iterable) -> int:
"""
Returns the number of ways to choose elements from the given iterable
This will consume a generator
"""
consumable = regurge(iterable)
consumable = list(consumable)
length = len(consumable)
return sum(binomial(length, i) for i in range(length))
def lispart(lis:Iterable, depth:int) -> list:
"""
Returns a collection of n*m elements as a list of m lists with n elements each
devised by David C. Ullrich from stack exchange
"""
consumable = regurge(iterable)
consumable = list(consumable)
assert(len(consumable)%depth == 0), f'The iterable cannot be factored into "{depth}" arrays'
return [consumable[j*depth:(j+1)*depth] for j in range(int(len(consumable)/depth))]
def sigma(iterable:Iterable[complex], v0:Any=0) -> complex:
"""
Returns the sum of a iterable
"""
consumable = regurge(iterable)
consumable = list(consumable)
for i in consumable:
v0 += i
return v0
def pipe(iterable:Iterable[complex]) -> complex:
"""
Returns the multiplicative product of the elements of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
if len(consumable) < 1:
return "this list is empty"
else:
v0 = 1
for i in consumable:
v0 *= i
return v0
def powerset(iterable:Iterable) -> regenerator:
"""
Returns the powerset of an iterable as a list
"""
consumable = regenerator(iterable)
return chain.from_iterable(combinations(consumable, r) for r in range(sum(1 for i in consumable)+1))
def sample(iterable:Iterable, size:int) -> tuple:
"""
Obtains a random sample of any length from any iterable and returns it as a tuple
"""
consumable = regurge(iterable)
consumable = list(consumable)
choiceIndices = tuple(random.randint(0, len(consumable)-1) for i in range(size))
return tuple(consumable[i] for i in choiceIndices)
def shuffle(iterable:Iterable) -> tuple:
"""
Given an iterable, this function returns a new tuple of its elements in a new order
"""
consumable = regurge(iterable)
consumable = list(consumable)
cache = []
pot = []
while len(cache) < len(consumable):
v0 = random.randrange(len(consumable))
if v0 not in cache:
cache.append(v0)
pot.append(consumable[v0])
return tuple(pot)
randomizer = shuffler = shuffle
def unzip(iterable:Iterable[Sequence[Any]]) -> List[list]:
"""
Obtain the inverse of a zip of a collection of arrays
This is about the same as a clockwise rotation of a matrix by 90 degrees
This will omit empty arrays
examples
>>> m3ta.show(unzip(range(3) for i in range(3)))
[0, 0, 0]
[1, 1, 1]
[2, 2, 2]
>>> m3ta.show(unzip(range(j) for j in range(10)))
[0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1]
[2, 2, 2, 2, 2, 2, 2]
[3, 3, 3, 3, 3, 3]
[4, 4, 4, 4, 4]
[5, 5, 5, 5]
[6, 6, 6]
[7, 7]
[8]
"""
consumable = regurge(iterable)
str_escape = lambda string: string.replace("'", "\'")
length = 0
racks = []
for i in consumable:
for j, k in enumerate(i):
if j > length-1:
exec(f"x{j} = []")
racks.append(eval(f'x{j}'))
length += 1
app_elem = f"x{j}.append('{str_escape(k)}')" if isinstance(k, str) else f"x{j}.append({k})"
eval(app_elem)
return racks
def diffs(iterable:Iterable[complex], flip:bool=False) -> generator:
"""
Yield the difference between each element of an iterable and its predecessor
example:
>>> [*diffs(range(3))]
[1, 1]
>>> [*diffs(range(3), True)]
[-1, -1]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
for i in consumable:
yield i - last if not flip else last - i
last = i
def discontinuities(iterable:Iterable[complex], delta:complex=1) -> generator:
"""
Obtain the ordinal positions of any elements who do not differ from their successors by delta
Iterable must, at least, be a semigroupoid with subtraction
Not generator safe.
example:
>>> [*discontinuities(range(3))]
[]
>>> [*discontinuities(range(3), 2)]
[0, 1]
"""
iterable = iter(iterable)
last = next(iterable)
for i, e in enumerate(iterable):
if e - last != delta:
yield i
last = e
def stationaries(iterable:Iterable[complex]) -> generator:
"""
Generate the indices of the stationary points of an iterable
example:
>>> [*stationaries(range(3))]
[]
>>> [*stationaries((3, 4, 4, 3, 2, 1, 1), 2)]
[1, 5]
"""
iterable = iter(iterable)
last = next(iterable)
for i, e in enumerate(iterable):
if e == last:
yield i
last = e
def discontinuous(iterable:Iterable[complex], differential:complex=1) -> generator:
"""
Check if an additive semigroupoid has any jumps which are inequivalent to a given differential
example:
>>> [*discontinuous(range(3))]
False
>>> [*discontinuous(range(3), 2)]
True
"""
if differential:
last = None
for i, e in enumerate(iterable):
if i > 0:
if not eq(last + differential, e):
return False
last = e
return True
return eq(*iterable)
def sums(iterable:Iterable[complex], flip:bool=False) -> generator:
"""
Yield the sum of each element of an iterable and its predecessor
example:
>>> [*sums('abc')]
['ba', 'cb']
>>> [*sums('abc', True)]
['ab', 'bc']
"""
consumable = iter(regurge(iterable))
last = next(consumable)
for i in consumable:
yield i + last if not flip else last + i
last = i
def quots(iterable:Iterable[complex], flip:bool=False) -> generator:
"""
Yield the quotient of each element of an iterable by its predecessor
example:
>>> [*quots(range(1, 4))]
[2.0, 1.5]
>>> [*quots(range(1, 4), True)]
[0.5, 0.6666666666666666]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
for i in consumable:
yield i / last if not flip else last / i
last = i
def prods(iterable:Iterable[complex], flip:bool=False) -> generator:
"""
Yield the product of each element of an iterable by its predecessor
example:
>>> [*prods(range(1, 4))]
[2, 6]
>>> [*prods(range(1, 4), True)]
[2, 6]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
for i in consumable:
yield i * last if not flip else last * i
last = i
def logs(iterable:Iterable[complex], flip:bool=False) -> generator:
"""
Yield the log of each element of an iterable in the base of its predecessor
example:
>>> [*logs(range(2, 5))]
[1.5849625007211563, 1.2618595071429148]
>>> [*logs(range(2, 5), True)]
[0.6309297535714574, 0.7924812503605781]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
for i in consumable:
yield log(i, last) if not flip else log(last, i)
last = i
def cumsum(iterable:Iterable[complex], first:bool=True) -> generator:
"""
Yield the log of each element of an iterable in the base of its predecessor
example:
>>> [*cumsum(range(4))]
[0, 1, 3, 6]
>>> [*cumsum(range(4), False)]
[1, 3, 6]
"""
consumable = iter(regurge(iterable))
last = 0
if first:
yield last
for i in consumable:
last += i
yield last
def cumdif(iterable:Iterable[complex], first:bool=True) -> generator:
"""
Yield the log of each element of an iterable in the base of its predecessor
example:
>>> [*cumdif(range(4))]
[0, -1, -3, -6]
>>> [*cumdif(range(4), False)]
[-1, -3, -6]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
if first:
yield last
for i in consumable:
last -= i
yield last
def cumprod(iterable:Iterable[complex], first:bool=True) -> generator:
"""
Yield the cumulative product of the elemements of an iterable
example:
>>> [*cumprod(range(1, 4))]
[1, 2, 6]
>>> [*cumprod(range(1, 4), False)]
[2, 6]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
if first:
yield last
for i in consumable:
last *= i
yield last
def cumquot(iterable:Iterable[complex], first:bool=True) -> generator:
"""
Yield the cumulative quotient of the elemements of an iterable
example:
>>> [*cumquot(range(1, 4))]
[1, 0.5, 0.16666666666666666]
>>> [*cumquot(range(1, 4), False)]
[0.5, 0.16666666666666666]
"""
consumable = iter(regurge(iterable))
last = next(consumable)
if first:
yield last
for i in consumable:
last /= i
yield last
def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> generator:
"""
Yield specific elements from an iterable by index:
>>> [*choose(range(1, 10), 0, 3)]
[1, 4]
>>> [*choose(range(1, 10), (0, 3))]
[1, 4]
"""
indices = [*flatten(indices)]
yielded = []
for i, e in enumerate(iterable):
if i in indices:
yield e
yielded.append(e)
if len(yielded) == len(indices):
break
# def chooser(iterable:Iterable, *)
def dupes(array:Iterable, once:bool=True, key:Callable=lambda x:x) -> generator:
"""
Yield the elements of a finite iterable whose frequency is greater than one
:once:
if set to false, all duplicate copies of the element shall be yielded
:key:
the function/type to use as the key for the sorting function (sorted(array, key=key))
eg
>>> [*dupes([1,2,2,2,1,3])]
[1, 2]
>>> [*dupes([1,2,2,2,1,3], False)]
[1, 2, 2]
"""
array = iter(sorted(array, key=key))
last_seen = next(array)
last_yield = None
for i in array:
if i==last_seen:
if once:
if i!=last_yield:
yield i
else:
yield i
last_yield = i
last_seen = i
def slices(iterable:Iterable, length:int, fill:Any=None) -> generator:
"""
Yield the adjacent slices of a given length for the given iterable. Trailing values will be padded by copies of 'fill'
use filter(all, slices(iterable, length)) to discard remainders
:fill:
the default value of any
eg:
>>> [*slices('abc', 2, None)]
[('a', 'b'), ('c', None)]
>>> [*filter(all, slices('abc', 2, None))]
[('a', 'b')]
"""
itr = iter(iterable)
while (main:=[*islice(itr, 0, length)]):
main += [fill for i in range(length-len(main))]
yield tuple(main)
if __name__ == "__main__":
# pass
for i in range(1,10):
print([*range(i)])
print(median(range(i)))

371
sl4ng/maths.py Normal file
View File

@ -0,0 +1,371 @@
# A house for small stones
from functools import lru_cache, reduce
from itertools import chain, combinations, count
from math import pi, ceil
from typing import Iterable
from .types import generator
def pyramid(length:int, shift:int=0) -> float:
"""
Returns the product of dividing 1 by each intger in the range(2+shift,2+int(length)+shift) (aka - the number of poops in your pocket)
if the length is not an integer it will be converted to one
Dependencies: N/A
In: length, shift=0 (integers)
Out: N/A
"""
val = 1
for i in range(2+shift, 2+int(length)+shift):
val /= i
return val
def primes1(n:int) -> list:
"""Generates a list of the first n primes. A cast will be used if the input is not an integer
Dependencies: N/A
In: (number)
Out: list"""
# pwimes = list(x for x in range(n) if x > 1 and all(x%y for y in range(2, min(x, 11))))
n = int(n) - 1
bank = []
track = 2
while len(bank)<n+1:
if all(track%y for y in range(2, min(track,11))):
bank.append(track)
track += 1
return sorted(set(bank))
def primes2(n:int) -> list:
"""Generates a list of primes with value lower than the input integer
Dependencies: N/A
In: (integer)
Out: list"""
pwimes = list({x for x in range(n) if x > 1 and all(x%y for y in range(2, min(x, 11)))})
return list(pwimes)
def succ(n:int) -> int:
"""Returns the successor of the input number
Dependencies: N/A
In: (number)
Out: number"""
return n+1
def pred(n:int) -> int:
"""Returns the predecessor of the input number
Dependencies: N/A
In: (number)
Out: number"""
return n-1
def rt(x:int, n:complex) -> complex:
"""Returns the nth root of the input number, x
Dependencies: N/A
In: (x, n)
Out: float(number)"""
return x**(1/n)
def gcd0(a:int, b:int) -> int:
"""The famous euclidean algorithm for computing the greatest common divisor of a pair of numbers a and b
Dependencies: N/A
In: (a: first number, b: second number)
Out: int"""
while a != b:
if a > b:
a -= b
else:
b -= a
return a
def gcd(*args:[int, tuple]) -> int:
"""
Compute the gcd for more than two integers at a time. Returns input if only one argument is given and it is greater than zero
Dependencies: itertools.combinations
In: subscriptable
Out: int
"""
if any(i<=0 for i in args):
return None
if len(args)>1:
gcds = {d for pair in combinations(args, 2) if all(i%(d:=gcd0(*pair))==0 for i in args)}
return max(gcds)
elif sum(args)>0:
return args[0]
def eratosthenes(n:int, imaginarypart:bool=False) -> generator:
"""
Implements eratothenes' sieve as a generator.
If the input is not an int it will be rounded to one.
Imaginary-part-based rounding optionable
Dependencies: None
In: int
Out: generator
"""
iscomp = isinstance(n, complex) or issubclass(type(n), complex)
n = round(n.imag) if imaginarypart and iscomp else round(n.real) if iscomp else round(n)
rack = range(2, n)
marked = set()
primes = set()
for i in rack:
if i not in marked:
multiples = {j for j in rack if j%i==0 and j>i}
marked.update(multiples)
yield i
def factors0(n:int) -> generator:
'''
Compute the factors of an integer
Dependencies: itertools.(chain, combinations)
In: int, or sequence of ints
Out: generator
'''
pipe = lambda array: reduce(lambda x, y: x*y, array, 1)
primes = tuple(eratosthenes(n))
facts = {n, 1} if n!= 0 else {}
for p in primes:
if n%p==0:
e = 1
while n%p**e==0:
facts.add(p**e)
e += 1
if facts == {n, 1}:
yield from facts
else:
for numbers in chain.from_iterable(combinations(facts, r) for r in range(1, len(facts))):
if n%pipe(numbers)==0:
facts.add(pipe(numbers))
yield from facts
def factors(*args:[int, tuple]) -> generator:
'''
Compute the common factors of any finite number of integers
Dependencies: m3ta.factors0
In: int, or sequence of ints
Out: generator
'''
if len(args) == 1 and hasattr(args[0], '__iter__'):
args = tuple(args[0])
if all(isinstance(i, int) or i==int(i) for i in args):
# facs = []
for i in args:
for j in factors0(i):
# facs.append(j)
if all(not arg%j for arg in args):
yield j
# for i in facs:
# if freq(i, facs)==len(args):
# yield i
@lru_cache(maxsize = 500)
def factorial(n:int) -> int:
"""
Return n! for any integer
Dependencies: lru_cache(from functools)
In: (int)
Out: int
"""
if n>=0:
k = 1
while n:
k *= n
n -= 1
return k
else:
return -factorial(abs(n))
def binomial(n:int, k:int) -> int:
'''Returns the n choose k for any k in range(n)
Dependencies: factorial
In: (integer)
Out: float'''
return round(factorial(n)/(factorial(n-k)*factorial(k)))
def isHarmoDiv(n:int) -> bool:
"""Computes a boolean whose value corresponds to the statement 'the number n is a Harmonic Divisor Number'
Dependencies: harmean (from meta)
In: Number
Out: Boolean"""
facts = factors(n)
return int(harmean(facts)) == harmean(facts)
def isFactor(divisor:int, predicate:int) -> bool:
"""Determines if a Number is a multiple of a Divisor
Dependencies: N/A
In: (Divisor, Number)
Out: Boolean"""
return predicate % divisor == 0
def isprime(n:int) -> bool:
"""Determines if a Number is a multiple of a Divisor
Dependencies: N/A
In: (Divisor, Number)
Out: Boolean"""
return n in eratosthenes(n+1)
def isPerfect(n:int) -> bool:
"""Returns a Boolean evaluation of an integer's Arithmetic Perfection
Dependencies: sigma, factors (both from meta)
In: Integer
Out: Boolean"""
return n == sum(factors(n))
def isAbundant(n:int) -> bool:
"""Returns a Boolean evaluation of an integer's Arithmetic Abundance
Dependencies: sigma, factors (both from meta)
In: Integer
Out: Boolean"""
return n > sigma(factors(n))
def isDeficient(n:int) -> bool:
"""Returns a Boolean evaluation of an integer's Arithmetic Deficience
Dependencies: sigma, factors (both from meta)
In: Integer
Out: Boolean"""
return n < sigma(factors(n))
def isFilial(n:int) -> bool:
fctrs = factors(n)
lace = [int(i) for i in str(n)]
return sigma(lace) in fctrs
def mulPer(n:int) -> int:
"""Computes the Multiplicative Persistence of an int or float in base-10 positional notation
Dependencies: pipe (from meta)
In: Integer
Out: Integer"""
# Exclusions
if len(str(n)) == 1:
return 0
elif (str(0) in str(n)) or ((len(str(n)) == 2) and (str(1) in str(n))):
return 1
else:
cache = []
while len(str(n)) > 1:
digitList = [int(i) for i in "".join(str(n).split('.'))]
n = pipe(digitList)
cache.append(n)
return len(cache)
def addPer(n:int) -> int:
"""Computes the Additive Persistence of an int or float in base-10 positional notation
Dependencies: sigma (from meta)
In: Integer
Out: Integer"""
if len(str(n)) == 1:
return 0
elif len(str(n)) < 1:
return ValueError("Your number of choice has less than one digit")
else:
cache = []
while len(str(n)) > 1:
digitList = [int(i) for i in "".join(str(n).split('.'))]
n = sigma(digitList)
cache.append(n)
return len(cache)
def triangular(a:int, b:int) -> int:
"""Returns the triangular number of an interval [a,b]
dependencies: none
In: integers a and b
Out: integer"""
interval = [i for i in range(b) if i > a]
for num in interval:
a += num
return a+b
def rationability(v:complex) -> complex:
"""
Get the subtractive series of the digits of a float, or that of an integer's reciprocal, measured from its math.ceil value
Dependencies: math.ceil
In: int/float
Out: float
rationability(math.pi)
0.8584073464102069
rationability(3)
0.6666666666666666
"""
v = 1/v if isinstance(v, int) else v
n = ceil(v)
p = str(v).replace('.', '')
for i, j in enumerate(p):
n -= int(j)*10**-i
return n
def root(value:complex, power:complex=2) -> complex:
"""
Get the root of a number
rt(complex(1,1))
(1.0986841134678098+0.45508986056222733j)
rt(complex(1,1),3)
(1.0842150814913512+0.2905145555072514j)
"""
return value**(1/power)
def odds(n:int=-1) -> generator:
"""
Yield the first n odd numbers, use a negative value for all of them
"""
ctr = count()
while n:
m = next(ctr)
if m % 2:
yield m
n -= 1
def evens(n:int=-1) -> generator:
"""
Yield the first n even numbers, use a negative value for all of them
"""
ctr = count()
while n:
m = next(ctr)
if not m % 2:
yield m
n -= 1
def congrues(n:int, modulus:int=6, cls:int=1) -> bool:
"""
Check if n is equal to +-cls modulo modulus
"""
return n % modulus in {cls, modulus-cls}
def mulseq(root:int, base:int=2, terms:int=-1, start:int=0, step:int=1) -> generator:
"""
Generate a sequence of multiples of a root and a base. By default it will yield the doubles sequence of the root.
"""
counter = count(start=start, step=step)
while terms:
yield root * base**next(counter)
terms -= 1
if __name__ == "__main__":
pass

71
sl4ng/persistance.py Normal file
View File

@ -0,0 +1,71 @@
from typing import Any
import os, pickle, re
import dill
from .iteration import deduplicate
from .debug import tryimport
from .files import nameSpacer
def dillsave(obj:Any, filename:str, overwrite:bool=True) -> Any:
"""
Pickles the given object to a file with the given path/name.
If the object cannot be serialized with pickle, we shall use dill instead
Returns the object
"""
if overwrite:
with open(filename, 'wb') as fob:
dill.dump(obj, fob, protocol=dill.HIGHEST_PROTOCOL)
else:
with open(nameSpacer(filename), 'wb') as fob:
dill.dump(obj, fob, protocol=dill.HIGHEST_PROTOCOL)
def save(obj:Any, filename:str, overwrite:bool=True) -> Any:
"""
Pickles the given object to a file with the given path/name.
If the object cannot be serialized with pickle, we shall use dill instead
Returns the object
"""
try:
if overwrite:
with open(filename, 'wb') as fob:
pickle.dump(obj, fob, protocol=pickle.HIGHEST_PROTOCOL)
else:
with open(nameSpacer(filename), 'wb') as fob:
pickle.dump(obj, fob, protocol=pickle.HIGHEST_PROTOCOL)
except pickle.PicklingError:
dillsave(obj, filename, overwrite)
except TypeError:
dillsave(obj, filename, overwrite)
return obj
def load(filename:str) -> Any:
"""
Return unpickled data from a chosen file
If the file doesn't exist it will be created
"""
if os.path.exists(filename):
with open(filename, 'rb') as fob:
var = pickle.load(fob)
return var
else:
x = open(filename)
x.close()
return
def jar(file, duplicates:bool=False, flags:int=re.I):
"""
Consolidate your pickles and eat them, discard the clones by default though
"""
trash = tryimport('send2trash', 'send2trash', 'remove', 'os')
name, ext = os.path.splitext(file)
pattern = f'^{name}|{name}_\d+\.{ext}$'
p = re.compile(pattern)
folder = None if not os.sep in file else os.path.split(file)[0]
os.chdir(folder) if folder else None
matches = deduplicate({f: load(f) for f in os.listdir(folder) if p.search(f, flags)})
results = list(matches.values())[:]
for i in matches: print(i)
[trash(file) for file in matches]
save(results, file)

220
sl4ng/stats.py Normal file
View File

@ -0,0 +1,220 @@
from typing import Any, Iterable
from math import log
from functools import lru_cache, reduce
from itertools import tee
from .strings import alphabet
from .types import regurge
def shannonEntropy(iterable:Iterable[Any]) -> float:
"""
Returns the Information, or Shannon, Entropy of an iterable
"""
consumable = regurge(iterable)
consumable = list(consumable)
weights = {j: freq(j, iterable)/len(consumable) for j in set(consumable)}
return -sigma([val*log(val, 2) for val in weights.values()])
def entropy(iterable:Iterable[Any], base:int=2, mode:str='kbdUS', space:str=None) -> float:
"""
Computes a modal entropy for a given iterable. Ints and floats will be converted to strings. Comma format ints will raise errors.
Space determines the character you wish to interpret as space if inpt is a string
"""
abc = alphabet
letters = ''.join(i for i in abc if i.isalpha() or i==' ')
digits = ''.join(i for i in abc if i.isnumeric() or i=='.' or i=='-')
prob = lambda x,y: freq(x, y)/len(y)
consumable = regurge(iterable)
iterates = hasattr(consumable,'__iter__')
if mode=="kbdUS":
consumable = str(consumable) if not iterates else tuple(str(i) for i in consumable)
chars = abc if space==None else abc
weights = {i:prob(i, chars) for i in set(consumable)}
return -sigma(val*log(val, base) for val in weights.values())
elif mode=='abc':
consumable = str(consumable) if not iterates else tuple(str(i) for i in consumable)
chars = letters if space==None else abc
weights = {i:prob(i, chars) for i in set(consumable)}
return -sigma(val*log(val, base) for val in weights.values())
elif mode=='num':
consumable = str(consumable) if not iterates else tuple(str(i) for i in consumable)
chars = digits if space==None else abc
weights = {i:prob(i, chars) for i in set(consumable)}
return -sigma(val*log(val, base) for val in weights.values())
elif mode=='shan':
consumable = str(consumable) if not iterates else consumable
chars = tuple(i for i in consumable)
weights = {i:prob(i, chars) for i in set(consumable)}
return -sigma(val*log(val, base) for val in weights.values())
def probability(item:Any, iterable:Iterable) -> float:
"""
Returns the quotient of the frequency with which an item
occurs in an iterable by the length of said iterable
"""
consumable = regurge(iterable)
consumable = list(consumable)
return freq(item, consumable) / len(consumable)
def freq(element:Any, iterable:Iterable[Any], overlap:bool=False) -> int:
"""
Returns the number of appearences of some term in some collection
:: greedy ::
handy for scanning sequences of digits in an integer:
say you're looking for 00 and there's a "000"
you may only get one of what could be two matches
"""
consumable = regurge(iterable)
consumable = list(consumable)
if type(element)==type(consumable)==str:
if not overlap:
return len(consumable.split(element))-1
else:
return freq(tuple(i for i in element), walks(consumable, len(element)))
return sum(1 for i in consumable if i==element)
def expectation(iterable:Iterable[complex]) -> complex:
"""
Returns the expectation value of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
exVal = 0
for i in consumable:
exVal += (i*(freq(i, consumable))/len(consumable))
return exVal
def mean(iterable:Iterable[complex]) -> complex:
"""
Returns the mean value of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
meanVal = sum(consumable)
meanVal /= len(consumable)
return meanVal
def median(iterable:Iterable[complex]) -> complex:
"""
Returns the median value of a collection
Will avoid consuming a generator/map/filter
"""
consumable = regurge(iterable)
consumable = sorted(consumable)
if len(consumable)%2:
index = round((len(consumable)-1)/2)
middle = consumable[index]
else:
index = round((len(consumable))/2)-1
middle = sum(consumable[index:index+2])/2
return middle
def midpoint(iterable:Iterable[complex]) -> complex:
"""
Returns the midpoint of a collection
Will avoid consuming a generator/map/filter
"""
consumable = regurge(iterable)
consumable = sorted(consumable)
return sum(max(consumable), min(consumable))/2
def central_deviation(iterable:Iterable[complex]) -> complex:
"""
Returns the difference between an iterable's midpoint and its median
Will avoid consuming a generator/map/filter
"""
consumable = regurge(iterable)
consumable = (consumable)
return abs(median(consumable) - midpoint(consumable))
def geomean(iterable:Iterable[complex]) -> complex:
"""
Returns the geometric mean of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
geoMean = (sigma(iterable))**(1/len(iterable))
return geoMean
def harmean(iterable:Iterable[complex]) -> complex:
"""
Returns the harmonic mean of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
assert 0 not in [i for i in consumable], "Input contains a zero, try a different one."
reciprocals = [1/i for i in consumable]
return len(consumable)/sigma(reciprocals)
def popdev(iterable:Iterable[complex]) -> complex:
"""
Returns the population standard deviation of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
avg = mean(consumable)
v1 = [(i-avg)**2 for i in consumable]
v2 = ((reduce(lambda x, y: x+y, v1))/len(v1))**(1/2)
return v2
def samdev(iterable:Iterable[complex]) -> complex:
"""
Returns the sample standard deviation of a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
avg = mean(consumable)
v1 = [(i-avg)**2 for i in consumable]
v2 = ((sigma(v1))/(len(v1)-1))**(1/2)
return v2
def popvar(iterable:Iterable[complex]) -> complex:
"""
Returns the population variance for a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
avg = mean(consumable)
deviations = [(i-avg)**2 for i in consumable]
pv = sum(deviations)/len(deviations)
return pv
def samvar(iterable:Iterable[complex]) -> complex:
"""
Returns the sample variance for a collection
"""
consumable = regurge(iterable)
consumable = list(consumable)
avg = mean(consumable)
v2 = [(i-v1)**2 for i in consumable]
v3 = sigma(v2)/len(v2)
return v3
if __name__ == '__main__':
pass

189
sl4ng/strings.py Normal file
View File

@ -0,0 +1,189 @@
from typing import Iterable, Any
from itertools import chain
import string, random
import pyperclip
from .types import generator, regurge
def join(iterable:Iterable[Any]=None, sep:str='', head:str='', tail:str='') -> str:
"""
Cast elements of an array to string and concatenate them.
This will consume a generator
Examples:
>>> m3ta.show(
map(
join,
(range(i) for i in range(1,5))
)
)
0
01
012
0123
# Also works as a closure using keyword arguments
>>> m3ta.show(
map(
join(sep=', ',head='[',tail=']'),
(range(i) for i in range(1,5))
)
)
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
"""
if iterable:
return head + sep.join(map(str, iterable)) + tail
else:
def wrapper(iterable:Iterable[Any]):
iterable = map(str, iterable)
return head + sep.join(iterable) + tail
return wrapper
def ascii(omissions:str='', include:bool=False) -> str:
"""
Return the ascii character base excluding the given omissions:
"p" -> ' ' + punctuation
"u" -> uppercase
"l" -> lowercase
"d" -> digits
Feel free to omit combinations:
ascii('lup')
0123456789
"""
d = {
"p":" "+string.punctuation,
"u":string.ascii_uppercase,
"l":string.ascii_lowercase,
"d":string.digits,
}
return "".join(d[k] for k in d if k in omissions) if include else "".join(d[k] for k in d if not k in omissions)
asciis = kbd = abc = ascii
alphabet = kbd()
def emptyString(line:str) -> bool:
"""
Determine if a string is empty ('', ' ','\n','\t') or not
"""
return any(line==i for i in '* *\n*\t'.split('*'))
def rewrite(string:str, charmap:dict={'A':'T', 'T':'A', 'C':'G', 'G':'C', 'a':'t', 't':'a', 'c':'g', 'g':'c'}, sep:str='') -> str:
"""
Given a sequence derived from 'ATCG', this function returns the complimentary base pairs of the given dna sequence
Dependencies: None
Arguments: permutation from 'ATCG'
Out: compliment of input
"""
return sep.join([charmap[string[i]] for i in range(len(string))])
def monoalphabetic(message:str, shift:int, alphabet:str=kbd(), space:str=None) -> str:
"""
A simple implementation of the monoalphabetic cipher.
By convention, use a:
Positive integer shift if you want to encode
Negative integer if you want to decode
"""
words = message.split(space)
for index, word in enumerate(words):
new = ''
for letter in word:
if decode:
substitute = alphabet[(alphabet.index(letter) - shift) % len(alphabet)]
else:
substitute = alphabet[(alphabet.index(letter) + shift) % len(alphabet)]
new += substitute
words[index] = new
if space == None:
space = ' '
return space.join(words)
caesar = monoalphabetic
def multisplit(splitters:Iterable[str], target:str=None) -> generator:
"""
Split a string by a the elements of an iterable
>>> splitter = multisplit('word1 word2 word3'.split())
>>> map(splitter, texts)
- OR -
>>> [*multisplit('breakfast', 'green eggs and ham')]
['g', 'n ', 'gg', ' ', 'nd h', 'm']
- OR -
>>> [*multisplit('breakfast'.split(), 'green eggs and ham')]
['green eggs and ham']
"""
def wrapper(target):
nonlocal splitters
splitters = iter(splitters)
result = target.split(next(splitters))
for splitter in splitters:
result = [*chain.from_iterable(i.split(splitter) for i in result)]
yield from filter(None, result)
return wrapper(target) if target else wrapper
def memespace(string:str, spaces:int=1, keep_spaces:bool=False, copy:bool=False):
"""
Aestheicize a string
eg:
>>> memespace('so edgy')
s o e d g y
>>> memespace('so edgy', 2)
s o e d g y
>>> memespace('so edgy', 2, True)
s o e d g y
"""
string = string if keep_spaces else join(string.split())
out = ''
for i, j in enumerate(string, 1):
out += j
out += ' '*spaces if i<len(string) else ''
pyperclip.copy(out) if copy else None
return out
def memecase(string, copy:bool=False):
"""
Randomize the case of text in strings
>>> memecase('something about narwhals, bacon, and midnight')
sOmeTHINg aBOuT nArwHaLS, BAcoN, aND mIDNighT
"""
out = ''
for i in string:
out += random.choice([i.upper(), i.lower()])
pyperclip.copy(out) if copy else None
return out
def sinusize(string:str, copy:bool=False):
"""
Map the characters of a string to critical and solvent points of a sinusoid
eg
>>> sinusize('hello_world')
e _ l
h l o w r d
l o
"""
mat = [[] for i in range(3)]
for i, j in enumerate(string):
if not i%2:
mat[1].append(j)
mat[0].append(' ')
mat[2].append(' ')
elif not (i%4) - 1:
mat[0].append(j)
mat[1].append(' ')
mat[2].append(' ')
else:
mat[2].append(j)
mat[0].append(' ')
mat[1].append(' ')
out = join([join(line) for line in mat], '\n')
pyperclip.copy(out) if copy else None
return out

45
sl4ng/system.py Normal file
View File

@ -0,0 +1,45 @@
import time, random, string
import pyperclip, psutil
def agora(string:bool=False):
"""
Here's the local time, mr wolf!
Dependencies: time
In: string=False
Out: tuple/string(HH:MM:SS)
"""
now = tuple(time.localtime())[3:-3]
return now if not string else ':'.join(str(i) for i in now)
def kill(process:str, casefold=True):
"""
Kill all processes of the argued process name
Dependencies: psutil
In: "process_name.exe"[str],casefold=True[bool]
Out: None
Inspired by Giampaolo Rodolà from StackOverflow
http://stackoverflow.com/questions/2940858/ddg#4230226
"""
import psutil
if casefold:
[p.kill() for p in psutil.process_iter() if p.name().casefold()==process.casefold()+('.exe', '')[process.endswith('.exe')]]
else:
[p.kill() for p in psutil.process_iter() if p.name()==process+('.exe', '')[process.endswith('.exe')]]
def guid(format:tuple=[8, 4, 4, 4, 12], sep:str='-', copy:bool=True) -> str:
"""
Generate a GUID for different resources
Dependencies: random,string,pyperclip.copy,m3ta.shuffle
In: format{length of the intervals of the guid you want}
Out: str
"""
chars = ''.join(i for i in shuffle(string.ascii_uppercase + string.digits+string.ascii_lowercase) if i!=sep)
ranstr = [random.choice(chars) for i in range(sum(format))]
[ranstr.insert(i + sum(format[:i+1]), sep) for i, j in enumerate(format[:-1])]
pyperclip.copy(''.join(ranstr)) if copy else None
return ''.join(ranstr)

217
sl4ng/types.py Normal file
View File

@ -0,0 +1,217 @@
__all__ = 'generator module function SineWave defaultdict'.split()
from collections import defaultdict
from typing import Iterable, Any, Iterator, Sequence
from copy import deepcopy
from itertools import tee, _tee
import os
generator = type(i for i in range(0))
module = type(os)
function = type(lambda x: x)
class _regen:
@staticmethod
def choose(iterable:Iterable[Any], *indices:Sequence[int]) -> generator:
"""
Yield specific elements from an iterable by index:
>>> [*choose(range(1, 10), 0, 3)]
[1, 4]
>>> [*choose(range(1, 10), (0, 3))]
[1, 4]
"""
indices = [*flatten(indices)]
yielded = []
for i, e in enumerate(iterable):
if i in indices:
yield e
yielded.append(e)
if len(yielded) == len(indices):
break
@staticmethod
def tipo(inpt:Any=type(lambda:0), keep_module:bool=False) -> str:
"""
Return the name of an object's type
Dependencies: None
In: object
Out: str
"""
if keep_module:
return str(type(inpt)).split("'")[1]
return str(type(inpt)).split("'")[1].split('.')[-1]
@staticmethod
def flatten(iterable:Iterable) -> generator:
"""
Flatten a 2d iterable
Example:
>>> list(flatten([[1, 2], [3, 4]]))
[1, 2, 3, 4]
based on:
https://pythonprinciples.com/challenges/Flatten-a-list/
"""
consumable = regurge(iterable)
for i in consumable:
if hasattr(i, '__iter__') or hasattr(i, '__next__'):
for j in i:
yield j
else:
yield i
class regenerator:
"""
A self-replenishing (or non-consumable) iterator. All methods whose return value is iterable return regenerators
:args & kwargs:
Any arguments needed to initialize the generator-type/function. Will not be used unless hasattr(iterable, '__call__') and iterable is not a regenerator.
eg:
>>> x = regurge(i for i in range(2))
>>> [*x]
[0, 1]
>>> bytes(x)
b'\x00\x01'
>>> [*x]
[0, 1]
"""
def __init__(self, iterable, *args, **kwargs):
if hasattr(iterable, '__call__') and not isinstance(iterable, type(self)):
self.active, self._inert = tee(iterable(*args, **kwargs))
else:
self.active, self._inert = tee(iterable)
def __next__(self):
return next(self.active)
def __iter__(self):
self.active, self._inert = tee(self._inert)
return self.active
def __call__(self, *indices:Iterable[int]):
"""
Access particular indices of the underlying iterator
eg
>>> x = regen(range(3))
>>> [*x(1)]
[1]
>>> [*x(1,2)]
[1, 2]
>>> [*x(1,2,3)]
[1, 2]
"""
return type(self)(_regen.choose(self.active, *_regen.flatten(indices)))
def __bool__(self):
"""
Returns True iff. the underlying iterator is non-empty. False otherwise.
"""
tmp, self._inert = tee(self._inert)
try:
next(tmp)
return True
except StopIteration:
return False
def __matmul__(self, other:Iterable):
if hasattr(other, '__iter__'):
return type(self)(product(self, other))
raise TypeError(f'Matrix-multiplication is not defined for "{_regen.tipo(other, True)}". It must have an "__iter__" or "__index__" method.')
def __len__(self):
return sum(1 for i in self)
def __add__(self, other:Any):
"""
Create a new regenerator whose first elements come from self and remaining element(s) is/come-from other.
If other is not iterable it shall be added as the last element.
If you want to add an iterable as a single element, use self.append
eg
>>> x = regenerator(range(2))
>>> y = x + 10
>>> [*y]
[0, 1, 10]
>>> y += x
>>> [*y]
[0, 1, 10, 0, 1]
"""
other = other if hasattr(other, '__iter__') else [other]
return type(self)([*self, *other])
def __radd__(self, other):
"""
Swap the order of __add__
"""
other = other if hasattr(other, '__iter__') else [other]
return type(self)(chain(other, self))
def __mul__(self, value):
"""
Replicate the behaviour of multiplying lists by integers
"""
if hasattr(value, '__int__'):
return type(self)(chain.from_iterable(self for i in range(int(value))))
raise TypeError(f'Multiplication is not defined for "{_regen.tipo(other, True)}". It must have an "__int__"')
def __rmul__(self, value):
"""Commutative multiplication"""
return self.__mul__(value)
def __pow__(self, value):
"""
value-dimensional Cartesian product of self with itself
"""
if hasattr(value, '__int__'):
return type(self)(product(self, repeat=int(value)))
raise TypeError(f'Exponentiation is not defined for {type(other)}. It must have an "__int__" method.')
def scale(self, value):
"""
Multiply every element of self by value. Done in place.
"""
self._inert = (i*value for i in self)
return self
def boost(self, value):
"""
Add value to every element of self. Done in place.
"""
self._inert = (i+value for i in self)
return self
def indices(self, value, shift:int=0):
"""
Similar to a list's index method, except that it returns every index whose element is a match
Works by calling enumerate and selecting at equivalent elements.
:shift:
kwarg for the "enumerate" call.
"""
return type(self)(i for i, e in enumerate(self, shift) if e == value)
def append(self, value):
"""
add "value" as the last element of the array
"""
other = [value]
self._inert = chain(self._inert, other)
return self
def inject(self, value):
"""
If "value" is an iterable: its elements will be added to the end of "self"
Otherwise: it is the same as append
"""
other = value if hasattr(value, '__iter__') else [value]
self._inert = chain(self._inert, *other)
return self
regen = regenerator
def regurge(iterable:Iterable[Any]) -> Iterable:
"""
Secure your generators by passing them through this function in order to produce a copy.
Your generator will be replaced with a tee object.
If your iterable is not a generator the function shall return a copy.deepcopy of it
"""
if issubclass(type(iterable), (map, filter, generator, _tee)):
iterable, consumable = tee(iterable)
else:
consumable = iterable
return consumable
class defaultdict(defaultdict):
""""""
def __repr__(self):
pairs = (': '.join(map(str, pair)) for pair in self.items())
rack = ',\n '.join(pairs)
return "{ " + rack + "\n}"
def __str__(self):
return self.__repr__()
class SineWave:
pass

54
sl4ng/web.py Normal file
View File

@ -0,0 +1,54 @@
from email.message import EmailMessage
import os, smtplib, subprocess
import pyperclip
def getIP(copy:bool=False) -> str:
"""
Returns the IP address of the device on which the function is called
Dependencies: subprocess.check_result, pyperclip.copy
Arguments: copy=False
Output: ipAddress [str]
"""
call = str(subprocess.check_output('ipconfig')).split('\\n')
line = [l for l in call if 'ipv4' in l.lower()][0]
address = line.strip().strip('. ').strip('\\r').split(': ')[1]
if copy: pyperclip.copy(address)
return address
def quickMail(msg:str, dst:str=os.environ.get('personalEmail'), subj:str=None, head:str=None, extra:str=None, att:str=None) -> None:
"""
Sends a short email to the given dst using gmail's smtp configuration with default sender/receiver info taken from user environment variables
Dependencies: os.environ.get [func], smtplib [mod], email.message.EmailMessage [obj]
Arguments: message, **destination
Output: None
"""
message = EmailMessage()
message['Subject'] = f'{subj if subj!=None else head if head!=None else ""}'
message['From'] = os.environ.get('promoEmail')
message['To'] = dst
message.set_content(msg)
if any([subj!=None,head!=None,extra!=None]):
message.add_alternative(f"""\
<!DOCTYPE html>
<html style='font-family:trebuchet ms;'>
<body>
<h1 style="color:SlateGray;">{head if head!=None else ''}</h1>
<p>{msg}</p>
<P>{extra if extra!=None else ''}
</body>
</html>
""", subtype='html')
if att != None:
for file in att.split('*'):
with open(file, "rb") as f:
fileData = f.read()
fileName = f.name.split(os.sep)[-1]
fileType = (os.path.splitext(f.name)[1]).replace(".","")
message.add_attachment(fileData, maintype="image", subtype=fileType, filename=fileName)
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(os.environ.get('promoEmail'), os.environ.get('promoEmailPass'))
smtp.send_message(message)