You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#!/usr/bin/env python3"""General Utilities(part of web.py)"""importdatetimeimportosimportreimportshutilimportsubprocessimportsysimportthreadingimporttimeimporttracebackfromthreadingimportlocalasthreadlocalfrom .py3helpersimport (
iteritems,
itervalues,
)
try:
fromStringIOimportStringIOexceptImportError:
fromioimportStringIO__all__= [
"Storage",
"storage",
"storify",
"Counter",
"counter",
"iters",
"rstrips",
"lstrips",
"strips",
"safeunicode",
"safestr",
"timelimit",
"Memoize",
"memoize",
"re_compile",
"re_subm",
"group",
"uniq",
"iterview",
"IterBetter",
"iterbetter",
"safeiter",
"safewrite",
"dictreverse",
"dictfind",
"dictfindall",
"dictincr",
"dictadd",
"requeue",
"restack",
"listget",
"intget",
"datestr",
"numify",
"denumify",
"commify",
"dateify",
"nthstr",
"cond",
"CaptureStdout",
"capturestdout",
"Profile",
"profile",
"tryall",
"ThreadedDict",
"threadeddict",
"autoassign",
"to36",
"sendmail",
]
classStorage(dict):
""" A Storage object is like a dictionary except `obj.foo` can be used in addition to `obj['foo']`. >>> o = storage(a=1) >>> o.a 1 >>> o['a'] 1 >>> o.a = 2 >>> o['a'] 2 >>> del o.a >>> o.a Traceback (most recent call last): ... AttributeError: 'a' """def__getattr__(self, key):
try:
returnself[key]
exceptKeyErrorask:
raiseAttributeError(k)
def__setattr__(self, key, value):
self[key] =valuedef__delattr__(self, key):
try:
delself[key]
exceptKeyErrorask:
raiseAttributeError(k)
def__repr__(self):
return"<Storage "+dict.__repr__(self) +">"storage=Storagedefstorify(mapping, *requireds, **defaults):
""" Creates a `storage` object from dictionary `mapping`, raising `KeyError` if d doesn't have all of the keys in `requireds` and using the default values for keys found in `defaults`. For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of `storage({'a':1, 'b':2, 'c':3})`. If a `storify` value is a list (e.g. multiple values in a form submission), `storify` returns the last element of the list, unless the key appears in `defaults` as a list. Thus: >>> storify({'a':[1, 2]}).a 2 >>> storify({'a':[1, 2]}, a=[]).a [1, 2] >>> storify({'a':1}, a=[]).a [1] >>> storify({}, a=[]).a [] Similarly, if the value has a `value` attribute, `storify will return _its_ value, unless the key appears in `defaults` as a dictionary. >>> storify({'a':storage(value=1)}).a 1 >>> storify({'a':storage(value=1)}, a={}).a <Storage {'value': 1}> >>> storify({}, a={}).a {} """_unicode=defaults.pop("_unicode", False)
# if _unicode is callable object, use it convert a string to unicode.to_unicode=safeunicodeif_unicodeisnotFalseandhasattr(_unicode, "__call__"):
to_unicode=_unicodedefunicodify(s):
if_unicodeandisinstance(s, str):
returnto_unicode(s)
returnsdefgetvalue(x):
ifhasattr(x, "file") andhasattr(x, "value"):
returnx.valueifhasattr(x, "value"):
returnunicodify(x.value)
returnunicodify(x)
stor=Storage()
forkeyinrequireds+tuple(mapping.keys()):
value=mapping[key]
ifisinstance(value, list):
ifisinstance(defaults.get(key), list):
value= [getvalue(x) forxinvalue]
else:
value=value[-1]
ifnotisinstance(defaults.get(key), dict):
value=getvalue(value)
ifisinstance(defaults.get(key), list) andnotisinstance(value, list):
value= [value]
setattr(stor, key, value)
for (key, value) initeritems(defaults):
result=valueifhasattr(stor, key):
result=stor[key]
ifvalue== () andnotisinstance(result, tuple):
result= (result,)
setattr(stor, key, result)
returnstorclassCounter(storage):
"""Keeps count of how many times something is added. >>> c = counter() >>> c.add('x') >>> c.add('x') >>> c.add('x') >>> c.add('x') >>> c.add('x') >>> c.add('y') >>> c['y'] 1 >>> c['x'] 5 >>> c.most() ['x'] """defadd(self, n):
self.setdefault(n, 0)
self[n] +=1defmost(self):
"""Returns the keys with maximum count."""m=max(itervalues(self))
return [kfork, viniteritems(self) ifv==m]
defleast(self):
"""Returns the keys with minimum count."""m=min(self.itervalues())
return [kfork, viniteritems(self) ifv==m]
defpercent(self, key):
"""Returns what percentage a certain key is of all entries. >>> c = counter() >>> c.add('x') >>> c.add('x') >>> c.add('x') >>> c.add('y') >>> c.percent('x') 0.75 >>> c.percent('y') 0.25 """returnfloat(self[key]) /sum(self.values())
defsorted_keys(self):
"""Returns keys sorted by value. >>> c = counter() >>> c.add('x') >>> c.add('x') >>> c.add('y') >>> c.sorted_keys() ['x', 'y'] """returnsorted(self.keys(), key=lambdak: self[k], reverse=True)
defsorted_values(self):
"""Returns values sorted by value. >>> c = counter() >>> c.add('x') >>> c.add('x') >>> c.add('y') >>> c.sorted_values() [2, 1] """return [self[k] forkinself.sorted_keys()]
defsorted_items(self):
"""Returns items sorted by value. >>> c = counter() >>> c.add('x') >>> c.add('x') >>> c.add('y') >>> c.sorted_items() [('x', 2), ('y', 1)] """return [(k, self[k]) forkinself.sorted_keys()]
def__repr__(self):
return"<Counter "+dict.__repr__(self) +">"counter=Counteriters= [list, tuple, set, frozenset]
class_hack(tuple):
passiters=_hack(iters)
iters.__doc__="""A list of iterable items (like lists, but not strings). Includes whicheverof lists, tuples, sets, and Sets are available in this version of Python."""def_strips(direction, text, remove):
ifisinstance(remove, iters):
forsubrinremove:
text=_strips(direction, text, subr)
returntextifdirection=="l":
iftext.startswith(remove):
returntext[len(remove) :]
elifdirection=="r":
iftext.endswith(remove):
returntext[: -len(remove)]
else:
raiseValueError("Direction needs to be r or l.")
returntextdefrstrips(text, remove):
""" removes the string `remove` from the right of `text` >>> rstrips("foobar", "bar") 'foo' """return_strips("r", text, remove)
deflstrips(text, remove):
""" removes the string `remove` from the left of `text` >>> lstrips("foobar", "foo") 'bar' >>> lstrips('http://foo.org/', ['http://', 'https://']) 'foo.org/' >>> lstrips('FOOBARBAZ', ['FOO', 'BAR']) 'BAZ' >>> lstrips('FOOBARBAZ', ['BAR', 'FOO']) 'BARBAZ' """return_strips("l", text, remove)
defstrips(text, remove):
""" removes the string `remove` from the both sides of `text` >>> strips("foobarfoo", "foo") 'bar' """returnrstrips(lstrips(text, remove), remove)
defsafestr(obj, encoding="utf-8"):
r""" Converts any given object to utf-8 encoded string. >>> safestr('hello') 'hello' >>> safestr(2) '2' """ifobjandhasattr(obj, "__next__"):
return [safestr(i) foriinobj]
returnstr(obj)
# Since Python3, utf-8 encoded strings and unicode strings are the same thingsafeunicode=safestrdeftimelimit(timeout):
""" A decorator to limit a function to `timeout` seconds, raising `TimeoutError` if it takes longer. >>> import time >>> def meaningoflife(): ... time.sleep(.2) ... return 42 >>> >>> timelimit(.1)(meaningoflife)() Traceback (most recent call last): ... RuntimeError: took too long >>> timelimit(1)(meaningoflife)() 42 _Caveat:_ The function isn't stopped after `timeout` seconds but continues executing in a separate thread. (There seems to be no way to kill a thread.) inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878> """def_1(function):
def_2(*args, **kw):
classDispatch(threading.Thread):
def__init__(self):
threading.Thread.__init__(self)
self.result=Noneself.error=Noneself.daemon=Trueself.start()
defrun(self):
try:
self.result=function(*args, **kw)
exceptException:
self.error=sys.exc_info()
c=Dispatch()
c.join(timeout)
ifc.is_alive():
raiseRuntimeError("took too long")
ifc.error:
raisec.error[1]
returnc.resultreturn_2return_1classMemoize:
""" 'Memoizes' a function, caching its return values for each input. If `expires` is specified, values are recalculated after `expires` seconds. If `background` is specified, values are recalculated in a separate thread. >>> calls = 0 >>> def howmanytimeshaveibeencalled(): ... global calls ... calls += 1 ... return calls >>> fastcalls = memoize(howmanytimeshaveibeencalled) >>> howmanytimeshaveibeencalled() 1 >>> howmanytimeshaveibeencalled() 2 >>> fastcalls() 3 >>> fastcalls() 3 >>> import time >>> fastcalls = memoize(howmanytimeshaveibeencalled, .1, background=False) >>> fastcalls() 4 >>> fastcalls() 4 >>> time.sleep(.2) >>> fastcalls() 5 >>> def slowfunc(): ... time.sleep(.1) ... return howmanytimeshaveibeencalled() >>> fastcalls = memoize(slowfunc, .2, background=True) >>> fastcalls() 6 >>> timelimit(.05)(fastcalls)() 6 >>> time.sleep(.2) >>> timelimit(.05)(fastcalls)() 6 >>> timelimit(.05)(fastcalls)() 6 >>> time.sleep(.2) >>> timelimit(.05)(fastcalls)() 7 >>> fastcalls = memoize(slowfunc, None, background=True) >>> threading.Thread(target=fastcalls).start() >>> time.sleep(.01) >>> fastcalls() 9 """def__init__(self, func, expires=None, background=True):
self.func=funcself.cache= {}
self.expires=expiresself.background=backgroundself.running= {}
self.running_lock=threading.Lock()
def__call__(self, *args, **keywords):
key= (args, tuple(keywords.items()))
withself.running_lock:
ifnotself.running.get(key):
self.running[key] =threading.Lock()
defupdate(block=False):
ifself.running[key].acquire(block):
try:
self.cache[key] = (self.func(*args, **keywords), time.time())
finally:
self.running[key].release()
ifkeynotinself.cache:
update(block=True)
elifself.expiresand (time.time() -self.cache[key][1]) >self.expires:
ifself.background:
threading.Thread(target=update).start()
else:
update()
returnself.cache[key][0]
memoize=Memoizere_compile=memoize(re.compile)
re_compile.__doc__="""A memoized version of re.compile."""class_re_subm_proxy:
def__init__(self):
self.match=Nonedef__call__(self, match):
self.match=matchreturn""defre_subm(pat, repl, string):
""" Like re.sub, but returns the replacement _and_ the match object. >>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball') >>> t 'foooooolish' >>> m.groups() ('oooooo',) """compiled_pat=re_compile(pat)
proxy=_re_subm_proxy()
compiled_pat.sub(proxy.__call__, string)
returncompiled_pat.sub(repl, string), proxy.matchdefgroup(seq, size):
""" Returns an iterator over a series of lists of length size from iterable. >>> list(group([1,2,3,4], 2)) [[1, 2], [3, 4]] >>> list(group([1,2,3,4,5], 2)) [[1, 2], [3, 4], [5]] """return (seq[i : i+size] foriinrange(0, len(seq), size))
defuniq(seq, key=None):
""" Removes duplicate elements from a list while preserving the order of the rest. >>> uniq([9,0,2,1,0]) [9, 0, 2, 1] The value of the optional `key` parameter should be a function that takes a single argument and returns a key to test the uniqueness. >>> uniq(["Foo", "foo", "bar"], key=lambda s: s.lower()) ['Foo', 'bar'] """key=keyor (lambdax: x)
seen=set()
result= []
forvinseq:
k=key(v)
ifkinseen:
continueseen.add(k)
result.append(v)
returnresultdefiterview(x):
""" Takes an iterable `x` and returns an iterator over it which prints its progress to stderr as it iterates through. """WIDTH=70defplainformat(n, lenx):
return"%5.1f%% (%*d/%d)"% ((float(n) /lenx) *100, len(str(lenx)), n, lenx)
defbars(size, n, lenx):
val=int((float(n) *size) /lenx+0.5)
ifsize-val:
spacing=">"+ (" "* (size-val))[1:]
else:
spacing=""return"[%s%s]"% ("="*val, spacing)
defeta(elapsed, n, lenx):
ifn==0:
return"--:--:--"ifn==lenx:
secs=int(elapsed)
else:
secs=int((elapsed/n) * (lenx-n))
mins, secs=divmod(secs, 60)
hrs, mins=divmod(mins, 60)
return"%02d:%02d:%02d"% (hrs, mins, secs)
defformat(starttime, n, lenx):
out=plainformat(n, lenx) +" "ifn==lenx:
end=" "else:
end=" ETA "end+=eta(time.time() -starttime, n, lenx)
out+=bars(WIDTH-len(out) -len(end), n, lenx)
out+=endreturnoutstarttime=time.time()
lenx=len(x)
forn, yinenumerate(x):
sys.stderr.write("\r"+format(starttime, n, lenx))
yieldysys.stderr.write("\r"+format(starttime, n+1, lenx) +"\n")
classIterBetter:
""" Returns an object that can be used as an iterator but can also be used via __getitem__ (although it cannot go backwards -- that is, you cannot request `iterbetter[0]` after requesting `iterbetter[1]`). >>> import itertools >>> c = iterbetter(itertools.count()) >>> c[1] 1 >>> c[5] 5 >>> c[3] Traceback (most recent call last): ... IndexError: already passed 3 It is also possible to get the first value of the iterator or None. >>> c = iterbetter(iter([3, 4, 5])) >>> print(c.first()) 3 >>> c = iterbetter(iter([])) >>> print(c.first()) None For boolean test, IterBetter peeps at first value in the itertor without effecting the iteration. >>> c = iterbetter(iter(range(5))) >>> bool(c) True >>> list(c) [0, 1, 2, 3, 4] >>> c = iterbetter(iter([])) >>> bool(c) False >>> list(c) [] """def__init__(self, iterator):
self.i, self.c=iterator, 0deffirst(self, default=None):
"""Returns the first element of the iterator or None when there are no elements. If the optional argument default is specified, that is returned instead of None when there are no elements. """try:
returnnext(iter(self))
exceptStopIteration:
returndefaultdef__iter__(self):
ifhasattr(self, "_head"):
yieldself._headwhile1:
try:
yieldnext(self.i)
exceptStopIteration:
returnself.c+=1def__getitem__(self, i):
# todo: slicesifi<self.c:
raiseIndexError("already passed "+str(i))
try:
whilei>self.c:
next(self.i)
self.c+=1# now self.c == iself.c+=1returnnext(self.i)
exceptStopIteration:
raiseIndexError(str(i))
def__nonzero__(self):
ifhasattr(self, "__len__"):
returnself.__len__() !=0ifhasattr(self, "_head"):
returnTruetry:
self._head=next(self.i)
exceptStopIteration:
returnFalseelse:
returnTrue__bool__=__nonzero__iterbetter=IterBetterdefsafeiter(it, cleanup=None, ignore_errors=True):
"""Makes an iterator safe by ignoring the exceptions occurred during the iteration."""defnext():
whileTrue:
try:
returnnext(it)
exceptStopIteration:
raiseexceptException:
traceback.print_exc()
it=iter(it)
whileTrue:
yieldnext()
defsafewrite(filename, content):
"""Writes the content to a temp file and then moves the temp file to given filename to avoid overwriting the existing file in case of errors. """withopen(filename+".tmp", "w") asf:
f.write(content)
shutil.move(f.name, filename)
defdictreverse(mapping):
""" Returns a new dictionary with keys and values swapped. >>> dictreverse({1: 2, 3: 4}) {2: 1, 4: 3} """returndict([(value, key) for (key, value) initeritems(mapping)])
defdictfind(dictionary, element):
""" Returns a key whose value in `dictionary` is `element` or, if none exists, None. >>> d = {1:2, 3:4} >>> dictfind(d, 4) 3 >>> dictfind(d, 5) """for (key, value) initeritems(dictionary):
ifelementisvalue:
returnkeydefdictfindall(dictionary, element):
""" Returns the keys whose values in `dictionary` are `element` or, if none exists, []. >>> d = {1:4, 3:4} >>> dictfindall(d, 4) [1, 3] >>> dictfindall(d, 5) [] """res= []
for (key, value) initeritems(dictionary):
ifelementisvalue:
res.append(key)
returnresdefdictincr(dictionary, element):
""" Increments `element` in `dictionary`, setting it to one if it doesn't exist. >>> d = {1:2, 3:4} >>> dictincr(d, 1) 3 >>> d[1] 3 >>> dictincr(d, 5) 1 >>> d[5] 1 """dictionary.setdefault(element, 0)
dictionary[element] +=1returndictionary[element]
defdictadd(*dicts):
""" Returns a dictionary consisting of the keys in the argument dictionaries. If they share a key, the value from the last argument is used. >>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1}) {1: 0, 2: 1, 3: 1} """result= {}
fordctindicts:
result.update(dct)
returnresultdefrequeue(queue, index=-1):
"""Returns the element at index after moving it to the beginning of the queue. >>> x = [1, 2, 3, 4] >>> requeue(x) 4 >>> x [4, 1, 2, 3] """x=queue.pop(index)
queue.insert(0, x)
returnxdefrestack(stack, index=0):
"""Returns the element at index after moving it to the top of stack. >>> x = [1, 2, 3, 4] >>> restack(x) 1 >>> x [2, 3, 4, 1] """x=stack.pop(index)
stack.append(x)
returnxdeflistget(lst, ind, default=None):
""" Returns `lst[ind]` if it exists, `default` otherwise. >>> listget(['a'], 0) 'a' >>> listget(['a'], 1) >>> listget(['a'], 1, 'b') 'b' """iflen(lst) -1<ind:
returndefaultreturnlst[ind]
defintget(integer, default=None):
""" Returns `integer` as an int or `default` if it can't. >>> intget('3') 3 >>> intget('3a') >>> intget('3a', 0) 0 """try:
returnint(integer)
except (TypeError, ValueError):
returndefaultdefdatestr(then, now=None):
""" Converts a (UTC) datetime object to a nice string representation. >>> from datetime import datetime, timedelta >>> d = datetime(1970, 5, 1) >>> datestr(d, now=d) '0 microseconds ago' >>> for t, v in iteritems({ ... timedelta(microseconds=1): '1 microsecond ago', ... timedelta(microseconds=2): '2 microseconds ago', ... -timedelta(microseconds=1): '1 microsecond from now', ... -timedelta(microseconds=2): '2 microseconds from now', ... timedelta(microseconds=2000): '2 milliseconds ago', ... timedelta(seconds=2): '2 seconds ago', ... timedelta(seconds=2*60): '2 minutes ago', ... timedelta(seconds=2*60*60): '2 hours ago', ... timedelta(days=2): '2 days ago', ... }): ... assert datestr(d, now=d+t) == v >>> datestr(datetime(1970, 1, 1), now=d) 'January 1' >>> datestr(datetime(1969, 1, 1), now=d) 'January 1, 1969' >>> datestr(datetime(1970, 6, 1), now=d) 'June 1, 1970' >>> datestr(None) '' """defagohence(n, what, divisor=None):
ifdivisor:
n=n//divisorout=str(abs(n)) +" "+what# '2 days'ifabs(n) !=1:
out+="s"# '2 days'out+=" "# '2 days 'ifn<0:
out+="from now"else:
out+="ago"returnout# '2 days ago'oneday=24*60*60ifnotthen:
return""ifnotnow:
now=datetime.datetime.utcnow()
iftype(now).__name__=="DateTime":
now=datetime.datetime.fromtimestamp(now)
iftype(then).__name__=="DateTime":
then=datetime.datetime.fromtimestamp(then)
eliftype(then).__name__=="date":
then=datetime.datetime(then.year, then.month, then.day)
delta=now-thendeltaseconds=int(delta.days*oneday+delta.seconds+delta.microseconds*1e-06)
deltadays=abs(deltaseconds) //onedayifdeltaseconds<0:
deltadays*=-1# fix for oddity of floorifdeltadays:
ifabs(deltadays) <4:
returnagohence(deltadays, "day")
# Trick to display 'June 3' instead of 'June 03'# Even though the %e format in strftime does that, it doesn't work on Windows.out=then.strftime("%B %d").replace(" 0", " ")
ifthen.year!=now.yearordeltadays<0:
out+=", %s"%then.yearreturnoutifint(deltaseconds):
ifabs(deltaseconds) > (60*60):
returnagohence(deltaseconds, "hour", 60*60)
elifabs(deltaseconds) >60:
returnagohence(deltaseconds, "minute", 60)
else:
returnagohence(deltaseconds, "second")
deltamicroseconds=delta.microsecondsifdelta.days:
deltamicroseconds=int(delta.microseconds-1e6) # datetime oddityifabs(deltamicroseconds) >1000:
returnagohence(deltamicroseconds, "millisecond", 1000)
returnagohence(deltamicroseconds, "microsecond")
defnumify(string):
""" Removes all non-digit characters from `string`. >>> numify('800-555-1212') '8005551212' >>> numify('800.555.1212') '8005551212' """return"".join([cforcinstr(string) ifc.isdigit()])
defdenumify(string, pattern):
""" Formats `string` according to `pattern`, where the letter X gets replaced by characters from `string`. >>> denumify("8005551212", "(XXX) XXX-XXXX") '(800) 555-1212' """out= []
forcinpattern:
ifc=="X":
out.append(string[0])
string=string[1:]
else:
out.append(c)
return"".join(out)
defcommify(n):
""" Add commas to an integer `n`. >>> commify(1) '1' >>> commify(123) '123' >>> commify(-123) '-123' >>> commify(1234) '1,234' >>> commify(1234567890) '1,234,567,890' >>> commify(123.0) '123.0' >>> commify(1234.5) '1,234.5' >>> commify(1234.56789) '1,234.56789' >>> commify(' %.2f ' % -1234.5) '-1,234.50' >>> commify(None) >>> """ifnisNone:
returnNonen=str(n).strip()
ifn.startswith("-"):
prefix="-"n=n[1:].strip()
else:
prefix=""if"."inn:
dollars, cents=n.split(".")
else:
dollars, cents=n, Noner= []
fori, cinenumerate(str(dollars)[::-1]):
ifiand (noti%3):
r.insert(0, ",")
r.insert(0, c)
out="".join(r)
ifcents:
out+="."+centsreturnprefix+outdefdateify(datestring):
""" Formats a numified `datestring` properly. """returndenumify(datestring, "XXXX-XX-XX XX:XX:XX")
defnthstr(n):
""" Formats an ordinal. Doesn't handle negative numbers. >>> nthstr(1) '1st' >>> nthstr(0) '0th' >>> [nthstr(x) for x in [2, 3, 4, 5, 10, 11, 12, 13, 14, 15]] ['2nd', '3rd', '4th', '5th', '10th', '11th', '12th', '13th', '14th', '15th'] >>> [nthstr(x) for x in [91, 92, 93, 94, 99, 100, 101, 102]] ['91st', '92nd', '93rd', '94th', '99th', '100th', '101st', '102nd'] >>> [nthstr(x) for x in [111, 112, 113, 114, 115]] ['111th', '112th', '113th', '114th', '115th'] """assertn>=0ifn%100in [11, 12, 13]:
return"%sth"%nreturn {1: "%sst", 2: "%snd", 3: "%srd"}.get(n%10, "%sth") %ndefcond(predicate, consequence, alternative=None):
""" Function replacement for if-else to use in expressions. >>> x = 2 >>> cond(x % 2 == 0, "even", "odd") 'even' >>> cond(x % 2 == 0, "even", "odd") + '_row' 'even_row' """ifpredicate:
returnconsequenceelse:
returnalternativeclassCaptureStdout:
""" Captures everything `func` prints to stdout and returns it instead. >>> def idiot(): ... print("foo") >>> capturestdout(idiot)() 'foo\\n' **WARNING:** Not threadsafe! """def__init__(self, func):
self.func=funcdef__call__(self, *args, **keywords):
out=StringIO()
oldstdout=sys.stdoutsys.stdout=outtry:
self.func(*args, **keywords)
finally:
sys.stdout=oldstdoutreturnout.getvalue()
capturestdout=CaptureStdoutclassProfile:
""" Profiles `func` and returns a tuple containing its output and a string with human-readable profiling information. >>> import time >>> out, inf = profile(time.sleep)(.001) >>> out >>> inf[:10].strip() 'took 0.0' """def__init__(self, func):
self.func=funcdef__call__(self, *args): # , **kw): kw unusedimportcProfileimportpstatsimportosimporttempfilef, filename=tempfile.mkstemp()
os.close(f)
prof=cProfile.Profile()
stime=time.time()
result=prof.runcall(self.func, *args)
stime=time.time() -stimeout=StringIO()
stats=pstats.Stats(prof, stream=out)
stats.strip_dirs()
stats.sort_stats("time", "calls")
stats.print_stats(40)
stats.print_callers()
x="\n\ntook "+str(stime) +" seconds\n"x+=out.getvalue()
# remove the tempfiletry:
os.remove(filename)
exceptIOError:
passreturnresult, xprofile=Profiledeftryall(context, prefix=None):
""" Tries a series of functions and prints their results. `context` is a dictionary mapping names to values; the value will only be tried if it's callable. >>> tryall(dict(j=lambda: True)) j: True ---------------------------------------- results: True: 1 For example, you might have a file `test/stuff.py` with a series of functions testing various things in it. At the bottom, have a line: if __name__ == "__main__": tryall(globals()) Then you can run `python test/stuff.py` and get the results of all the tests. """context=context.copy() # vars() would updateresults= {}
for (key, value) initeritems(context):
ifnothasattr(value, "__call__"):
continueifprefixandnotkey.startswith(prefix):
continueprint(key+":", end=" ")
try:
r=value()
dictincr(results, r)
print(r)
except:
print("ERROR")
dictincr(results, "ERROR")
print(" "+"\n ".join(traceback.format_exc().split("\n")))
print("-"*40)
print("results:")
for (key, value) initeritems(results):
print(" "*2, str(key) +":", value)
classThreadedDict(threadlocal):
""" Thread local storage. >>> d = ThreadedDict() >>> d.x = 1 >>> d.x 1 >>> import threading >>> def f(): d.x = 2 ... >>> t = threading.Thread(target=f) >>> t.start() >>> t.join() >>> d.x 1 """_instances=set()
def__init__(self):
ThreadedDict._instances.add(self)
def__del__(self):
ThreadedDict._instances.remove(self)
def__hash__(self):
returnid(self)
defclear_all():
"""Clears all ThreadedDict instances."""fortinlist(ThreadedDict._instances):
t.clear()
clear_all=staticmethod(clear_all)
# Define all these methods to more or less fully emulate dict -- attribute access# is built into threading.local.def__getitem__(self, key):
returnself.__dict__[key]
def__setitem__(self, key, value):
self.__dict__[key] =valuedef__delitem__(self, key):
delself.__dict__[key]
def__contains__(self, key):
returnkeyinself.__dict__has_key=__contains__defclear(self):
self.__dict__.clear()
defcopy(self):
returnself.__dict__.copy()
defget(self, key, default=None):
returnself.__dict__.get(key, default)
defitems(self):
returnself.__dict__.items()
defiteritems(self):
returniteritems(self.__dict__)
defkeys(self):
returnself.__dict__.keys()
defiterkeys(self):
try:
returniterkeys(self.__dict__)
exceptNameError:
returnself.__dict__.keys()
iter=iterkeysdefvalues(self):
returnself.__dict__.values()
defitervalues(self):
returnitervalues(self.__dict__)
defpop(self, key, *args):
returnself.__dict__.pop(key, *args)
defpopitem(self):
returnself.__dict__.popitem()
defsetdefault(self, key, default=None):
returnself.__dict__.setdefault(key, default)
defupdate(self, *args, **kwargs):
self.__dict__.update(*args, **kwargs)
def__repr__(self):
return"<ThreadedDict %r>"%self.__dict____str__=__repr__threadeddict=ThreadedDictdefautoassign(self, locals):
""" Automatically assigns local variables to `self`. >>> self = storage() >>> autoassign(self, dict(a=1, b=2)) >>> self.a 1 >>> self.b 2 Generally used in `__init__` methods, as in: def __init__(self, foo, bar, baz=1): autoassign(self, locals()) """for (key, value) initeritems(locals):
ifkey=="self":
continuesetattr(self, key, value)
defto36(q):
""" Converts an integer to base 36 (a useful scheme for human-sayable IDs). >>> to36(35) 'z' >>> to36(119292) '2k1o' >>> int(to36(939387374), 36) 939387374 >>> to36(0) '0' >>> to36(-393) Traceback (most recent call last): ... ValueError: must supply a positive integer """ifq<0:
raiseValueError("must supply a positive integer")
letters="0123456789abcdefghijklmnopqrstuvwxyz"converted= []
whileq!=0:
q, r=divmod(q, 36)
converted.insert(0, letters[r])
return"".join(converted) or"0"r_url=re_compile(r"(?<!\()(http://(\S+))")
defsendmail(from_address, to_address, subject, message, headers=None, **kw):
""" Sends the email message `message` with mail and envelope headers for from `from_address_` to `to_address` with `subject`. Additional email headers can be specified with the dictionary `headers. Optionally cc, bcc and attachments can be specified as keyword arguments. Attachments must be an iterable and each attachment can be either a filename or a file object or a dictionary with filename, content and optionally content_type keys. If `web.config.smtp_server` is set, it will send the message to that SMTP server. Otherwise it will look for `/usr/sbin/sendmail`, the typical location for the sendmail-style binary. To use sendmail from a different path, set `web.config.sendmail_path`. """attachments=kw.pop("attachments", [])
mail=_EmailMessage(from_address, to_address, subject, message, headers, **kw)
forainattachments:
ifisinstance(a, dict):
mail.attach(a["filename"], a["content"], a.get("content_type"))
elifhasattr(a, "read"): # filefilename=os.path.basename(getattr(a, "name", ""))
content_type=getattr(a, "content_type", None)
mail.attach(filename, a.read(), content_type)
elifisinstance(a, str):
f=open(a, "rb")
content=f.read()
f.close()
filename=os.path.basename(a)
mail.attach(filename, content, None)
else:
raiseValueError("Invalid attachment: %s"%repr(a))
mail.send()
class_EmailMessage:
def__init__(self, from_address, to_address, subject, message, headers=None, **kw):
deflistify(x):
ifnotisinstance(x, list):
return [safestr(x)]
else:
return [safestr(a) forainx]
subject=safestr(subject)
message=safestr(message)
from_address=safestr(from_address)
to_address=listify(to_address)
cc=listify(kw.get("cc", []))
bcc=listify(kw.get("bcc", []))
recipients=to_address+cc+bccimportemail.utilsself.from_address=email.utils.parseaddr(from_address)[1]
self.recipients= [email.utils.parseaddr(r)[1] forrinrecipients]
self.headers=dictadd(
{"From": from_address, "To": ", ".join(to_address), "Subject": subject},
headersor {},
)
ifcc:
self.headers["Cc"] =", ".join(cc)
self.message=self.new_message()
self.message.add_header("Content-Transfer-Encoding", "7bit")
self.message.add_header("Content-Disposition", "inline")
self.message.add_header("MIME-Version", "1.0")
self.message.set_payload(message, "utf-8")
self.multipart=Falsedefnew_message(self):
fromemail.messageimportMessagereturnMessage()
defattach(self, filename, content, content_type=None):
ifnotself.multipart:
msg=self.new_message()
msg.add_header("Content-Type", "multipart/mixed")
msg.attach(self.message)
self.message=msgself.multipart=Trueimportmimetypestry:
fromemailimportencodersexcept:
fromemailimportEncodersasencoderscontent_type= (
content_typeormimetypes.guess_type(filename)[0]
or"application/octet-stream"
)
msg=self.new_message()
msg.set_payload(content)
msg.add_header("Content-Type", content_type)
msg.add_header("Content-Disposition", "attachment", filename=filename)
ifnotcontent_type.startswith("text/"):
encoders.encode_base64(msg)
self.message.attach(msg)
defprepare_message(self):
fork, viniteritems(self.headers):
ifk.lower() =="content-type":
self.message.set_type(v)
else:
self.message.add_header(k, v)
self.headers= {}
defsend(self):
self.prepare_message()
message_text=self.message.as_string()
try:
from . importwebapiexceptImportError:
webapi=Storage(config=Storage())
ifwebapi.config.get("smtp_server"):
self.send_with_smtp(message_text)
elifwebapi.config.get("email_engine") =="aws":
self.send_with_aws(message_text)
else:
self.default_email_sender(message_text)
defsend_with_aws(self, message_text):
try:
from . importwebapiexceptImportError:
webapi=Storage(config=Storage())
importboto.sesc=boto.ses.SESConnection(
aws_access_key_id=webapi.config.get("aws_access_key_id"),
aws_secret_access_key=webapi.config.get("aws_secret_access_key"),
)
c.send_raw_email(message_text, self.from_address, self.recipients)
defsend_with_smtp(self, message_text):
try:
from . importwebapiexceptImportError:
webapi=Storage(config=Storage())
server=webapi.config.get("smtp_server")
port=webapi.config.get("smtp_port", 0)
username=webapi.config.get("smtp_username")
password=webapi.config.get("smtp_password")
debug_level=webapi.config.get("smtp_debuglevel", None)
starttls=webapi.config.get("smtp_starttls", False)
importsmtplibsmtpserver=smtplib.SMTP(server, port)
ifdebug_level:
smtpserver.set_debuglevel(debug_level)
ifstarttls:
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo()
ifusernameandpassword:
smtpserver.login(username, password)
smtpserver.sendmail(self.from_address, self.recipients, message_text)
smtpserver.quit()
defdefault_email_sender(self, message_text):
try:
from . importwebapiexceptImportError:
webapi=Storage(config=Storage())
sendmail=webapi.config.get("sendmail_path", "/usr/sbin/sendmail")
assertnotself.from_address.startswith("-"), "security"forrinself.recipients:
assertnotr.startswith("-"), "security"cmd= [sendmail, "-f", self.from_address] +self.recipientsp=subprocess.Popen(cmd, stdin=subprocess.PIPE)
p.stdin.write(message_text.encode("utf-8"))
p.stdin.close()
p.wait()
def__repr__(self):
return"<EmailMessage>"def__str__(self):
returnself.message.as_string()
if__name__=="__main__":
importdoctestdoctest.testmod()
pylint crashed with a AstroidError and with the following stacktrace:
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\pylint\lint\pylinter.py", line 782, in _lint_file
check_astroid_module(module)
File "C:\Python310\lib\site-packages\pylint\lint\pylinter.py", line 1049, in check_astroid_module
retval = self._check_astroid_module(
File "C:\Python310\lib\site-packages\pylint\lint\pylinter.py", line 1099, in _check_astroid_module
walker.walk(node)
File "C:\Python310\lib\site-packages\pylint\utils\ast_walker.py", line 93, in walk
self.walk(child)
File "C:\Python310\lib\site-packages\pylint\utils\ast_walker.py", line 93, in walk
self.walk(child)
File "C:\Python310\lib\site-packages\pylint\utils\ast_walker.py", line 93, in walk
self.walk(child)
[Previous line repeated 2 more times]
File "C:\Python310\lib\site-packages\pylint\utils\ast_walker.py", line 90, in walk
callback(astroid)
File "C:\Python310\lib\site-packages\pylint\checkers\refactoring\refactoring_checker.py", line 1071, in visit_call
self._check_raising_stopiteration_in_generator_next_call(node)
File "C:\Python310\lib\site-packages\pylint\checkers\refactoring\refactoring_checker.py", line 1148, in _check_raising_stopiteration_in_generator_next_call
and not _looks_like_infinite_iterator(node.args[0])
IndexError: list index out of range
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\pylint\lint\pylinter.py", line 747, in _lint_files
self._lint_file(fileitem, module, check_astroid_module)
File "C:\Python310\lib\site-packages\pylint\lint\pylinter.py", line 784, in _lint_file
raise astroid.AstroidError from e
astroid.exceptions.AstroidError
web\template.py:1112:8: R1705: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return)
web\template.py:1121:8: R1705: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return)
web\template.py:1136:8: W0233: __init__ method from a non direct base class 'GAE_Render' is called (non-parent-init-called)
web\template.py:1154:8: R1705: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return)
web\template.py:54:0: C0411: standard import "from collections.abc import MutableMapping" should be placed before "from .net import websafe" (wrong-import-order)
************* Module web.utils
web\utils.py:306:7: W1116: Second argument of isinstance is not a type (isinstance-second-argument-not-valid-type)
Exception on node <Call l.748 at 0x26cca998310>in file 'C:\Users\markm\GitHub\webpy\web\utils.py'
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\pylint\utils\ast_walker.py", line 90, in walk
callback(astroid)
File "C:\Python310\lib\site-packages\pylint\checkers\refactoring\refactoring_checker.py", line 1071, in visit_call
self._check_raising_stopiteration_in_generator_next_call(node)
File "C:\Python310\lib\site-packages\pylint\checkers\refactoring\refactoring_checker.py", line 1148, in _check_raising_stopiteration_in_generator_next_call
and not _looks_like_infinite_iterator(node.args[0])
IndexError: list index out of range
web\utils.py:1:0: F0002: web\utils.py: Fatal error while checking 'web\utils.py'. Please open an issue in our bug tracker so we address this. There is a pre-filled template that you can use in'C:\Users\markm\AppData\Local\pylint\pylint\Cache\pylint-crash-2022-10-12-14-24-35.txt'. (astroid-error)
************* Module web.webapi
Expected behavior
to parse currently and show pylint issues
Pylint version
pylint 2.15.3
astroid 2.12.10
Python 3.10.5 (tags/v3.10.5:f377153, Jun 6 2022, 16:14:13) [MSC v.1929 64 bit (AMD64)]
I was able to reproduce by copying webpy utils.py and running latest pytest on it.
Pierre-Sassoulas
added
Needs PR
This issue is accepted, sufficiently specified and now needs an implementation
and removed
Needs triage 📥
Just created, needs acknowledgment, triage, and proper labelling
Needs investigation 🔬
A bug or crash where it's not immediately obvious what is happenning
labels
Oct 13, 2022
def safeiter(it, cleanup=None, ignore_errors=True):
"""Makes an iterator safe by ignoring the exceptions occurred during the iteration."""
def next():
while True:
try:
return next(it)
except StopIteration:
raise
except Exception:
traceback.print_exc()
it = iter(it)
while True:
yield next()
is defining a function called next, which shadows the builtin next. If we rename it to custom_next the issue goes away. Of course this is only a hack-solution; pylint should be able to handle this unfortunate naming.
I'll take a closer look at _check_raising_stopiteration_in_generator_next_call which handles next via inference, but perhaps we need to be more specific!
Bug description
When parsing the following file:
pylint crashed with a
AstroidError
and with the following stacktrace:Configuration
No response
Command used
Pylint output
Expected behavior
to parse currently and show pylint issues
Pylint version
OS / Environment
Windows 11
Additional dependencies
WARNING: Ignoring invalid distribution -ypy (c:\python310\lib\site-packages)
WARNING: Ignoring invalid distribution -umpy (c:\python310\lib\site-packages)
WARNING: Ignoring invalid distribution -ip (c:\python310\lib\site-packages)
WARNING: Ignoring invalid distribution -aker (c:\python310\lib\site-packages)
abodepy==1.2.2
absl-py==1.2.0
aenum==3.1.11
aioambient==2021.12.0
aioaseko==0.0.2
aiocoap==0.4.4
aiohttp==3.8.1
aiosignal==1.2.0
ambee==0.4.0
anthemav==1.4.2
anyio==3.6.1
appdirs==1.4.4
Appium-Python-Client==2.6.0
arcam-fmj==0.12.0
astral==2.2
astroid==2.12.10
asttokens==2.0.8
astunparse==1.6.3
async-generator==1.10
async-timeout==4.0.2
async-upnp-client==0.31.2
asyncio==3.4.3
atomicwrites==1.4.1
atomicwrites-homeassistant==1.4.1
attrs==21.2.0
auditwheel==5.1.2
autobahn==22.7.1
autofaker==0.4.9
Automat==20.2.0
autopage==0.5.1
autopep8==1.7.0
awesomeversion==22.8.0
axe-selenium-python==2.1.6
axis==44
azure-cognitiveservices-vision-computervision==0.9.0
azure-common==1.1.28
azure-core==1.25.1
azure-eventhub==5.10.1
azure-functions==1.11.2
backcall==0.2.0
bandit==1.7.4
bcrypt==3.1.7
beautifulsoup4==4.11.1
bidict==0.22.0
bitarray==2.6.0
black==22.8.0
bleach==5.0.1
bleak==0.16.0
bleak-winrt==1.2.0
blebox-uniapi==2.1.0
blinker==1.5
bluetooth-auto-recovery==0.3.3
botocore==1.27.89
Brotli==1.0.9
bs4==0.0.1
btsocket==0.2.0
CacheControl==0.12.11
cachetools==5.2.0
cachy==0.3.0
certifi==2022.6.15
cffi==1.15.1
cfgv==3.3.1
chacha20poly1305-reuseable==0.0.4
charset-normalizer==2.1.0
cheroot==8.6.0
chromedriver==2.24.1
chromedriver-autoinstaller==0.3.1
ciso8601==2.2.0
classes==0.4.1
cleo==1.0.0a5
cli-ui==0.17.2
click==8.1.3
click-default-group==1.2.2
cliff==4.0.0
cmd2==2.4.2
codespell==2.1.0
colorama==0.4.5
colored==1.4.3
colorlog==6.7.0
commentjson==0.9.0
commonmark==0.9.1
ConfigArgParse==1.5.3
constantly==15.1.0
contextlib2==21.6.0
contributors-txt==0.9.2
coverage==6.4.4
coveralls==3.3.1
crashtest==0.3.1
cryptography==37.0.4
cssselect==1.1.0
cycler==0.11.0
dacite==1.6.0
datadog==0.44.0
debugpy==1.6.3
decorator==5.1.1
deepface==0.0.75
defusedxml==0.7.1
delayed-assert==0.3.6
Deprecated==1.2.13
dill==0.3.5.1
distlib==0.3.6
distro==1.7.0
docopt==0.6.2
docutils==0.19
dodgy==0.2.1
dulwich==0.20.46
entrypoints==0.4
exceptiongroup==1.0.0rc9
execnet==1.9.0
executing==1.0.0
extras==1.0.0
fake-factory==9999.9.9
Faker==15.0.0
filelock==3.8.0
fire==0.4.0
fivem==1.1
fixtures==4.0.1
flake8==5.0.4
flake8-polyfill==1.0.2
flake8-typing-imports==1.13.0
flaky==3.7.0
Flask==2.2.2
flatbuffers==2.0.7
fnvhash==0.1.0
fonttools==4.37.1
freezegun==1.2.2
frozenlist==1.3.1
future==0.18.2
gast==0.4.0
gdown==4.5.1
gitdb==4.0.9
GitPython==3.1.27
goalzero==0.2.1
goodwe==0.2.20
google-api-core==2.10.1
google-api-python-client==2.62.0
google-auth==2.11.0
google-auth-httplib2==0.1.0
google-auth-oauthlib==0.5.3
google-pasta==0.2.0
googleapis-common-protos==1.56.4
gprof2dot==2022.7.29
graphviz==0.20.1
greenlet==1.1.2
grpcio==1.48.1
h11==0.12.0
h2==4.1.0
h5py==3.7.0
hacklib==0.1.5
hangups==0.4.18
herepy==3.5.8
home-assistant-bluetooth==1.3.0
homeassistant==2022.9.2
hpack==4.0.0
html5lib==1.1
httpcore==0.15.0
httplib2==0.20.4
httpx==0.23.0
hyperframe==6.0.1
hyperlink==21.0.0
hypothesis==6.56.0
identify==2.5.5
idna==3.3
ifaddr==0.1.7
importlib-metadata==4.12.0
incremental==21.3.0
iniconfig==1.1.1
invoke==1.7.3
ipykernel==6.15.2
ipython==8.5.0
iso8601==1.0.2
isodate==0.6.1
isort==5.10.1
itemadapter==0.7.0
itemloaders==1.0.6
itsdangerous==2.1.2
jaraco.classes==3.2.2
jaraco.functools==3.5.2
jedi==0.18.1
Jinja2==3.1.2
jmespath==1.0.1
joblib==1.1.0
jsonschema==4.16.0
jupyter-core==4.11.1
jupyter_client==7.3.5
jwt==1.3.1
kaitaistruct==0.10
keras==2.10.0
Keras-Preprocessing==1.1.2
keyring==23.9.1
kiwisolver==1.4.4
lark-parser==0.7.8
lazy-object-proxy==1.7.1
libclang==14.0.6
libsass==0.21.0
lockfile==0.12.2
lomond==0.3.3
lru-dict==1.1.8
lupupy==0.1.9
lxml==4.9.1
Markdown==3.4.1
MarkupSafe==2.1.1
matplotlib==3.5.3
matplotlib-inline==0.1.6
mccabe==0.7.0
MechanicalSoup==0.12.0
mechanize==0.4.8
mediafile==0.9.0
miniaudio==1.52
mock-open==1.4.0
more-itertools==8.14.0
MouseInfo==0.1.3
msgpack==1.0.4
msrest==0.7.1
mtcnn==0.1.1
multidict==6.0.2
mutagen==1.45.1
mypy==0.981
mypy-extensions==0.4.3
nest-asyncio==1.5.5
nexia==2.0.2
nextcord==2.2.0
nodeenv==1.7.0
numpy==1.23.3
oauthlib==3.2.1
objgraph==3.5.0
opencv-python==4.6.0.66
opt-einsum==3.3.0
orjson==3.7.11
outcome==1.2.0
packaging==21.3
paho-mqtt==1.6.1
pandas==1.4.4
parsel==1.6.0
parso==0.8.3
pathspec==0.10.1
pbr==5.9.0
pep517==0.13.0
pep8==1.7.1
pep8-naming==0.10.0
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.2.0
pipreqs==0.4.11
pixelmatch==0.3.0
pkginfo==1.8.3
platformdirs==2.5.2
playwright==1.25.2
pluggy==1.0.0
ply==3.11
poetry==1.2.0
poetry-core==1.1.0
poetry-plugin-export==1.0.6
pre-commit==2.20.0
prettytable==3.4.1
prompt-toolkit==3.0.31
prospector==1.7.7
Protego==0.2.1
psutil==5.9.2
ptyprocess==0.7.0
pure-eval==0.2.2
py==1.11.0
py-cpuinfo==8.0.0
pyairvisual==2022.7.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pyatag==0.3.5.3
pyatmo==7.0.1
pyatv==0.10.3
PyAutoGUI==0.9.53
pybalboa==0.13
pycodestyle==2.9.1
pycparser==2.21
pycryptodomex==3.15.0
pydantic==1.10.2
pydeconz==104
PyDispatcher==2.0.6
pydivert==2.1.0
pydocstyle==6.1.1
pyee==8.1.0
pyelftools==0.29
pyenchant==3.2.2
pyfiglet==0.8.post1
pyfritzhome==0.6.7
pyftpdlib==1.5.7
pyfttt==0.3.2
PyGetWindow==0.0.9
PyGithub==1.55
Pygments==2.13.0
pygtfs==0.1.7
pygtrie==2.5.0
PyJWT==2.4.0
pylama==8.3.8
pyleniumio==1.16.1
pylev==1.4.0
pylint==2.15.3
pylint-celery==0.3
pylint-django==2.5.3
pylint-flask==0.6
pylint-plugin-utils==0.7
pylint-runner==0.6.0
PyMsgBox==1.0.9
PyNaCl==1.5.0
pyOpenSSL==22.0.0
pyotp==2.7.0
pyparsing==3.0.9
pyperclip==1.8.2
pyreadline3==3.4.1
PyRect==0.2.0
PyRIC==0.1.6.3
pyrsistent==0.18.1
PyScreeze==0.1.28
pyserial==3.5
pysmb==1.2.8
pysnmp-pyasn1==1.1.2
pysnmp-pysmi==1.1.10
PySocks==1.7.1
pytest==7.1.3
pytest-asyncio==0.19.0
pytest-base-url==2.0.0
pytest-benchmark==3.4.1
pytest-cov==3.0.0
pytest-forked==1.4.0
pytest-mpi==0.6
pytest-parallel==0.1.1
pytest-playwright==0.3.0
pytest-profiling==1.7.0
pytest-repeat==0.9.1
pytest-reportportal==5.1.2
pytest-socket==0.5.1
pytest-timeout==2.1.0
pytest-xdist==2.5.0
python-awair==0.2.4
python-dateutil==2.8.2
python-didl-lite==1.3.2
python-dotenv==0.21.0
python-engineio==4.3.4
python-nmap==0.7.1
python-slugify==4.0.1
python-socketio==5.7.1
python-subunit==1.4.0
pytweening==1.0.4
pytz==2022.2.1
pyupgrade==2.37.3
pywin32==304
pywin32-ctypes==0.2.0
PyYAML==6.0
pyzmq==23.2.1
qualname==0.1.0
queuelib==1.6.2
random2==1.0.1
re-wx==0.0.10
readlike==0.1.3
readme-renderer==37.2
redbird==0.5.1
refurb==1.1.0
rellu==0.7
ReParser==1.4.3
reportportal-client==5.2.3
requests==2.28.1
requests-file==1.5.1
requests-mock==1.10.0
requests-oauthlib==1.3.1
requests-toolbelt==0.9.1
requirements-detector==0.7
resources==0.0.1
retina-face==0.0.12
rfc3986==2.0.0
rich==12.5.1
rich-click==1.5.2
robot==20071211
robotframework==5.0.1
rsa==4.9
rstr==3.2.0
schema==0.7.5
schiene==0.24
scikit-learn==1.1.2
scipy==1.9.1
Scrapy @ file:///C:/Users/markm/GitHub/scrapy
selenium==4.1.0
selenium-wire==4.6.5
serial==0.0.97
service-identity==21.1.0
setoptconf-tmp==0.3.1
shellingham==1.5.0
six==1.16.0
sklearn==0.0
slugify==0.0.1
smmap==5.0.0
sniffio==1.2.0
snowballstemmer==2.2.0
somecomfort==0.8.0
sortedcontainers==2.4.0
soupsieve==2.3.2.post1
SQLAlchemy==1.4.41
srptools==1.0.1
stack-data==0.5.0
staty==1.2.4
stdlib-list==0.7.0
stestr==4.0.1
stevedore==3.5.0
stringcase==1.2.0
sybil==3.0.1
tabulate==0.8.10
tblib==1.7.0
tbump==6.9.0
tensorboard==2.10.0
tensorboard-data-server==0.6.1
tensorboard-plugin-wit==1.8.1
tensorflow==2.10.0
tensorflow-estimator==2.10.0
tensorflow-io-gcs-filesystem==0.27.0
termcolor==2.0.1
testfixtures==7.0.0
testscenarios==0.5.0
testtools==2.5.0
text-unidecode==1.3
threadpoolctl==3.1.0
tldextract==3.4.0
tokenize-rt==4.2.1
toml==0.10.2
tomli==2.0.1
tomlkit==0.11.4
tornado==6.2
towncrier==22.8.0
tox==3.26.0
tqdm==4.64.1
traitlets==5.4.0
trio==0.21.0
trio-websocket==0.9.2
twine==4.0.1
Twisted==22.4.0
twisted-iocpsupport==1.0.2
txaio==22.2.1
typed-ast==1.5.4
types-atomicwrites==1.4.1
types-backports==0.1.3
types-certifi==0.1.4
types-chardet==0.1.5
types-croniter==1.0.0
types-cryptography==3.3.23
types-decorator==0.1.7
types-enum34==0.1.8
types-ipaddress==0.1.5
types-pkg-resources==0.1.3
types-pyOpenSSL==22.0.10
types-python-slugify==0.1.2
types-pytz==2021.1.2
types-PyYAML==5.4.6
types-requests==2.28.7
types-toml==0.1.5
types-ujson==0.1.1
types-urllib3==1.26.20
typing-inspect==0.8.0
typing_extensions==4.4.0
uamqp==1.6.0
Unidecode==1.3.4
uritemplate==4.1.1
urllib3==1.26.12
urwid==2.1.2
urwid-readline==0.13
validators==0.20.0
virtualenv==20.16.5
voluptuous==0.13.1
voluptuous-serialize==2.5.0
w3lib==2.0.1
waitress==2.1.2
wc==1.0.1
wcwidth==0.2.5
webdriver-manager==3.8.3
webencodings==0.5.1
websockets==10.3
Werkzeug==2.2.2
windows-curses==2.3.0
wrapt==1.14.1
wsproto==1.1.0
wxPython==4.2.0
xmltodict==0.13.0
yamllint==1.27.1
yarg==0.1.9
yarl==1.7.2
zeroconf==0.39.1
zipp==3.8.1
zope.interface==5.4.0
zstandard==0.18.0
The text was updated successfully, but these errors were encountered: