A few lines were indented using spaces instead of tabs -- fix them.

This commit is contained in:
Guido van Rossum 1998-03-26 20:56:10 +00:00
parent fa6e254b34
commit 8ca842066c
12 changed files with 282 additions and 265 deletions

View file

@ -89,9 +89,9 @@ else:
fp = open(name) fp = open(name)
data = open(name).read(256) data = open(name).read(256)
for c in data: for c in data:
if not c in string.whitespace \ if not c in string.whitespace \
and (c<' ' or ord(c) > 0177): and (c<' ' or ord(c) > 0177):
break break
else: else:
finfo.Type = 'TEXT' finfo.Type = 'TEXT'
fp.seek(0, 2) fp.seek(0, 2)
@ -214,10 +214,10 @@ class BinHex:
self.ofp.write(data) self.ofp.write(data)
def _writecrc(self): def _writecrc(self):
# XXXX Should this be here?? # XXXX Should this be here??
# self.crc = binascii.crc_hqx('\0\0', self.crc) # self.crc = binascii.crc_hqx('\0\0', self.crc)
self.ofp.write(struct.pack('>h', self.crc)) self.ofp.write(struct.pack('>h', self.crc))
self.crc = 0 self.crc = 0
def write(self, data): def write(self, data):
if self.state != _DID_HEADER: if self.state != _DID_HEADER:

View file

@ -6,7 +6,7 @@
def insort(a, x, lo=0, hi=None): def insort(a, x, lo=0, hi=None):
if hi is None: if hi is None:
hi = len(a) hi = len(a)
while lo < hi: while lo < hi:
mid = (lo+hi)/2 mid = (lo+hi)/2
if x < a[mid]: hi = mid if x < a[mid]: hi = mid
else: lo = mid+1 else: lo = mid+1
@ -18,7 +18,7 @@ def insort(a, x, lo=0, hi=None):
def bisect(a, x, lo=0, hi=None): def bisect(a, x, lo=0, hi=None):
if hi is None: if hi is None:
hi = len(a) hi = len(a)
while lo < hi: while lo < hi:
mid = (lo+hi)/2 mid = (lo+hi)/2
if x < a[mid]: hi = mid if x < a[mid]: hi = mid
else: lo = mid+1 else: lo = mid+1

View file

@ -198,20 +198,20 @@ def _deepcopy_dict(x, memo):
d[types.DictionaryType] = _deepcopy_dict d[types.DictionaryType] = _deepcopy_dict
def _keep_alive(x, memo): def _keep_alive(x, memo):
"""Keeps a reference to the object x in the memo. """Keeps a reference to the object x in the memo.
Because we remember objects by their id, we have Because we remember objects by their id, we have
to assure that possibly temporary objects are kept to assure that possibly temporary objects are kept
alive by referencing them. alive by referencing them.
We store a reference at the id of the memo, which should We store a reference at the id of the memo, which should
normally not be used unless someone tries to deepcopy normally not be used unless someone tries to deepcopy
the memo itself... the memo itself...
""" """
try: try:
memo[id(memo)].append(x) memo[id(memo)].append(x)
except KeyError: except KeyError:
# aha, this is the first one :-) # aha, this is the first one :-)
memo[id(memo)]=[x] memo[id(memo)]=[x]
def _deepcopy_inst(x, memo): def _deepcopy_inst(x, memo):
if hasattr(x, '__deepcopy__'): if hasattr(x, '__deepcopy__'):

View file

@ -40,9 +40,9 @@ import string
# Import SOCKS module if it exists, else standard socket module socket # Import SOCKS module if it exists, else standard socket module socket
try: try:
import SOCKS; socket = SOCKS import SOCKS; socket = SOCKS
except ImportError: except ImportError:
import socket import socket
# Magic number from <socket.h> # Magic number from <socket.h>
@ -293,14 +293,14 @@ class FTP:
thishost = socket.gethostname() thishost = socket.gethostname()
# Make sure it is fully qualified # Make sure it is fully qualified
if not '.' in thishost: if not '.' in thishost:
thisaddr = socket.gethostbyname(thishost) thisaddr = socket.gethostbyname(thishost)
firstname, names, unused = \ firstname, names, unused = \
socket.gethostbyaddr(thisaddr) socket.gethostbyaddr(thisaddr)
names.insert(0, firstname) names.insert(0, firstname)
for name in names: for name in names:
if '.' in name: if '.' in name:
thishost = name thishost = name
break break
try: try:
if os.environ.has_key('LOGNAME'): if os.environ.has_key('LOGNAME'):
realuser = os.environ['LOGNAME'] realuser = os.environ['LOGNAME']
@ -419,15 +419,15 @@ class FTP:
raise error_reply, resp raise error_reply, resp
return self.voidcmd('RNTO ' + toname) return self.voidcmd('RNTO ' + toname)
def delete(self, filename): def delete(self, filename):
'''Delete a file.''' '''Delete a file.'''
resp = self.sendcmd('DELE ' + filename) resp = self.sendcmd('DELE ' + filename)
if resp[:3] == '250': if resp[:3] == '250':
return resp return resp
elif resp[:1] == '5': elif resp[:1] == '5':
raise error_perm, resp raise error_perm, resp
else: else:
raise error_reply, resp raise error_reply, resp
def cwd(self, dirname): def cwd(self, dirname):
'''Change to a directory.''' '''Change to a directory.'''
@ -477,20 +477,21 @@ class FTP:
_150_re = None _150_re = None
def parse150(resp): def parse150(resp):
'''Parse the '150' response for a RETR request. '''Parse the '150' response for a RETR request.
Returns the expected transfer size or None; size is not guaranteed to Returns the expected transfer size or None; size is not guaranteed to
be present in the 150 message. be present in the 150 message.
''' '''
if resp[:3] != '150': if resp[:3] != '150':
raise error_reply, resp raise error_reply, resp
global _150_re global _150_re
if _150_re is None: if _150_re is None:
import re import re
_150_re = re.compile("150 .* \(([0-9][0-9]*) bytes\)", re.IGNORECASE) _150_re = re.compile("150 .* \(([0-9][0-9]*) bytes\)",
m = _150_re.match(resp) re.IGNORECASE)
if m: m = _150_re.match(resp)
return string.atoi(m.group(1)) if m:
return None return string.atoi(m.group(1))
return None
def parse227(resp): def parse227(resp):
@ -561,104 +562,107 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
class Netrc: class Netrc:
"""Class to parse & provide access to 'netrc' format files. """Class to parse & provide access to 'netrc' format files.
See the netrc(4) man page for information on the file format. See the netrc(4) man page for information on the file format.
"""
__defuser = None
__defpasswd = None
__defacct = None
def __init__(self, filename=None):
if not filename:
if os.environ.has_key("HOME"):
filename = os.path.join(os.environ["HOME"], ".netrc")
else:
raise IOError, "specify file to load or set $HOME"
self.__hosts = {}
self.__macros = {}
fp = open(filename, "r")
in_macro = 0
while 1:
line = fp.readline()
if not line: break
if in_macro and string.strip(line):
macro_lines.append(line)
continue
elif in_macro:
self.__macros[macro_name] = tuple(macro_lines)
in_macro = 0
words = string.split(line)
host = user = passwd = acct = None
default = 0
i = 0
while i < len(words):
w1 = words[i]
if i+1 < len(words):
w2 = words[i + 1]
else:
w2 = None
if w1 == 'default':
default = 1
elif w1 == 'machine' and w2:
host = string.lower(w2)
i = i + 1
elif w1 == 'login' and w2:
user = w2
i = i + 1
elif w1 == 'password' and w2:
passwd = w2
i = i + 1
elif w1 == 'account' and w2:
acct = w2
i = i + 1
elif w1 == 'macdef' and w2:
macro_name = w2
macro_lines = []
in_macro = 1
break
i = i + 1
if default:
self.__defuser = user or self.__defuser
self.__defpasswd = passwd or self.__defpasswd
self.__defacct = acct or self.__defacct
if host:
if self.__hosts.has_key(host):
ouser, opasswd, oacct = self.__hosts[host]
user = user or ouser
passwd = passwd or opasswd
acct = acct or oacct
self.__hosts[host] = user, passwd, acct
fp.close()
def get_hosts(self):
"""Return a list of hosts mentioned in the .netrc file."""
return self.__hosts.keys()
def get_account(self, host):
"""Returns login information for the named host.
The return value is a triple containing userid, password, and the
accounting field.
""" """
host = string.lower(host) __defuser = None
user = passwd = acct = None __defpasswd = None
if self.__hosts.has_key(host): __defacct = None
user, passwd, acct = self.__hosts[host]
user = user or self.__defuser
passwd = passwd or self.__defpasswd
acct = acct or self.__defacct
return user, passwd, acct
def get_macros(self): def __init__(self, filename=None):
"""Return a list of all defined macro names.""" if not filename:
return self.__macros.keys() if os.environ.has_key("HOME"):
filename = os.path.join(os.environ["HOME"],
".netrc")
else:
raise IOError, \
"specify file to load or set $HOME"
self.__hosts = {}
self.__macros = {}
fp = open(filename, "r")
in_macro = 0
while 1:
line = fp.readline()
if not line: break
if in_macro and string.strip(line):
macro_lines.append(line)
continue
elif in_macro:
self.__macros[macro_name] = tuple(macro_lines)
in_macro = 0
words = string.split(line)
host = user = passwd = acct = None
default = 0
i = 0
while i < len(words):
w1 = words[i]
if i+1 < len(words):
w2 = words[i + 1]
else:
w2 = None
if w1 == 'default':
default = 1
elif w1 == 'machine' and w2:
host = string.lower(w2)
i = i + 1
elif w1 == 'login' and w2:
user = w2
i = i + 1
elif w1 == 'password' and w2:
passwd = w2
i = i + 1
elif w1 == 'account' and w2:
acct = w2
i = i + 1
elif w1 == 'macdef' and w2:
macro_name = w2
macro_lines = []
in_macro = 1
break
i = i + 1
if default:
self.__defuser = user or self.__defuser
self.__defpasswd = passwd or self.__defpasswd
self.__defacct = acct or self.__defacct
if host:
if self.__hosts.has_key(host):
ouser, opasswd, oacct = \
self.__hosts[host]
user = user or ouser
passwd = passwd or opasswd
acct = acct or oacct
self.__hosts[host] = user, passwd, acct
fp.close()
def get_macro(self, macro): def get_hosts(self):
"""Return a sequence of lines which define a named macro.""" """Return a list of hosts mentioned in the .netrc file."""
return self.__macros[macro] return self.__hosts.keys()
def get_account(self, host):
"""Returns login information for the named host.
The return value is a triple containing userid,
password, and the accounting field.
"""
host = string.lower(host)
user = passwd = acct = None
if self.__hosts.has_key(host):
user, passwd, acct = self.__hosts[host]
user = user or self.__defuser
passwd = passwd or self.__defpasswd
acct = acct or self.__defacct
return user, passwd, acct
def get_macros(self):
"""Return a list of all defined macro names."""
return self.__macros.keys()
def get_macro(self, macro):
"""Return a sequence of lines which define a named macro."""
return self.__macros[macro]
@ -680,17 +684,18 @@ def test():
ftp.set_debuglevel(debugging) ftp.set_debuglevel(debugging)
userid = passwd = acct = '' userid = passwd = acct = ''
try: try:
netrc = Netrc(rcfile) netrc = Netrc(rcfile)
except IOError: except IOError:
if rcfile is not None: if rcfile is not None:
sys.stderr.write("Could not open account file" sys.stderr.write("Could not open account file"
" -- using anonymous login.") " -- using anonymous login.")
else: else:
try: try:
userid, passwd, acct = netrc.get_account(host) userid, passwd, acct = netrc.get_account(host)
except KeyError: except KeyError:
# no account for host # no account for host
sys.stderr.write("No account -- using anonymous login.") sys.stderr.write(
"No account -- using anonymous login.")
ftp.login(userid, passwd, acct) ftp.login(userid, passwd, acct)
for file in sys.argv[2:]: for file in sys.argv[2:]:
if file[:2] == '-l': if file[:2] == '-l':

View file

@ -77,19 +77,20 @@ def send_query(selector, query, host, port = 0):
# Takes a path as returned by urlparse and returns the appropriate selector # Takes a path as returned by urlparse and returns the appropriate selector
def path_to_selector(path): def path_to_selector(path):
if path=="/": if path=="/":
return "/" return "/"
else: else:
return path[2:] # Cuts initial slash and data type identifier return path[2:] # Cuts initial slash and data type identifier
# Takes a path as returned by urlparse and maps it to a string # Takes a path as returned by urlparse and maps it to a string
# See section 3.4 of RFC 1738 for details # See section 3.4 of RFC 1738 for details
def path_to_datatype_name(path): def path_to_datatype_name(path):
if path=="/": if path=="/":
return "TYPE='unknown'" # No way to tell, although "INDEX" is probable # No way to tell, although "INDEX" is likely
else: return "TYPE='unknown'"
return type_to_name(path[1]) else:
return type_to_name(path[1])
# The following functions interpret the data returned by the gopher # The following functions interpret the data returned by the gopher
# server according to the expected type, e.g. textfile or directory # server according to the expected type, e.g. textfile or directory
@ -118,7 +119,8 @@ def get_directory(f):
continue continue
if len(parts) > 4: if len(parts) > 4:
if parts[4:] != ['+']: if parts[4:] != ['+']:
print '(Extra info from server:', parts[4:], ')' print '(Extra info from server:',
print parts[4:], ')'
else: else:
parts.append('') parts.append('')
parts.insert(0, gtype) parts.insert(0, gtype)

View file

@ -7,6 +7,7 @@ import rfc822
import os import os
class _Mailbox: class _Mailbox:
def __init__(self, fp): def __init__(self, fp):
self.fp = fp self.fp = fp
self.seekp = 0 self.seekp = 0
@ -35,6 +36,7 @@ class _Mailbox:
return rfc822.Message(_Subfile(self.fp, start, stop)) return rfc822.Message(_Subfile(self.fp, start, stop))
class _Subfile: class _Subfile:
def __init__(self, fp, start, stop): def __init__(self, fp, start, stop):
self.fp = fp self.fp = fp
self.start = start self.start = start
@ -66,17 +68,18 @@ class _Subfile:
return self.pos - self.start return self.pos - self.start
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
if whence == 0: if whence == 0:
self.pos = self.start + pos self.pos = self.start + pos
elif whence == 1: elif whence == 1:
self.pos = self.pos + pos self.pos = self.pos + pos
elif whence == 2: elif whence == 2:
self.pos = self.stop + pos self.pos = self.stop + pos
def close(self): def close(self):
pass pass
class UnixMailbox(_Mailbox): class UnixMailbox(_Mailbox):
def _search_start(self): def _search_start(self):
while 1: while 1:
line = self.fp.readline() line = self.fp.readline()
@ -96,6 +99,7 @@ class UnixMailbox(_Mailbox):
return return
class MmdfMailbox(_Mailbox): class MmdfMailbox(_Mailbox):
def _search_start(self): def _search_start(self):
while 1: while 1:
line = self.fp.readline() line = self.fp.readline()
@ -115,43 +119,45 @@ class MmdfMailbox(_Mailbox):
return return
class MHMailbox: class MHMailbox:
def __init__(self, dirname):
import re
pat = re.compile('^[0-9][0-9]*$')
self.dirname = dirname
files = os.listdir(self.dirname)
self.boxes = []
for f in files:
if pat.match(f):
self.boxes.append(f)
def next(self): def __init__(self, dirname):
if not self.boxes: import re
return None pat = re.compile('^[0-9][0-9]*$')
fn = self.boxes[0] self.dirname = dirname
del self.boxes[0] files = os.listdir(self.dirname)
fp = open(os.path.join(self.dirname, fn)) self.boxes = []
return rfc822.Message(fp) for f in files:
if pat.match(f):
self.boxes.append(f)
def next(self):
if not self.boxes:
return None
fn = self.boxes[0]
del self.boxes[0]
fp = open(os.path.join(self.dirname, fn))
return rfc822.Message(fp)
class BabylMailbox(_Mailbox): class BabylMailbox(_Mailbox):
def _search_start(self):
while 1:
line = self.fp.readline()
if not line:
raise EOFError
if line == '*** EOOH ***\n':
return
def _search_end(self): def _search_start(self):
while 1: while 1:
pos = self.fp.tell() line = self.fp.readline()
line = self.fp.readline() if not line:
if not line: raise EOFError
return if line == '*** EOOH ***\n':
if line == '\037\014\n': return
self.fp.seek(pos)
return def _search_end(self):
while 1:
pos = self.fp.tell()
line = self.fp.readline()
if not line:
return
if line == '\037\014\n':
self.fp.seek(pos)
return
def _test(): def _test():

View file

@ -45,15 +45,15 @@ class MultiFile:
return self.fp.tell() - self.start return self.fp.tell() - self.start
# #
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
here = self.tell() here = self.tell()
if whence: if whence:
if whence == 1: if whence == 1:
pos = pos + here pos = pos + here
elif whence == 2: elif whence == 2:
if self.level > 0: if self.level > 0:
pos = pos + self.lastpos pos = pos + self.lastpos
else: else:
raise Error, "can't use whence=2 yet" raise Error, "can't use whence=2 yet"
if not 0 <= pos <= here or \ if not 0 <= pos <= here or \
self.level > 0 and pos > self.lastpos: self.level > 0 and pos > self.lastpos:
raise Error, 'bad MultiFile.seek() call' raise Error, 'bad MultiFile.seek() call'

View file

@ -72,14 +72,15 @@ class NNTP:
self.debugging = 0 self.debugging = 0
self.welcome = self.getresp() self.welcome = self.getresp()
if user: if user:
resp = self.shortcmd('authinfo user '+user) resp = self.shortcmd('authinfo user '+user)
if resp[:3] == '381': if resp[:3] == '381':
if not password: if not password:
raise error_reply, resp raise error_reply, resp
else: else:
resp = self.shortcmd('authinfo pass '+password) resp = self.shortcmd(
if resp[:3] != '281': 'authinfo pass '+password)
raise error_perm, resp if resp[:3] != '281':
raise error_perm, resp
# Get the welcome message from the server # Get the welcome message from the server
# (this is read and squirreled away by __init__()). # (this is read and squirreled away by __init__()).

View file

@ -438,7 +438,7 @@ class Profile:
sys.setprofile(None) sys.setprofile(None)
#****************************************************************** #******************************************************************
# The following calculates the overhead for using a profiler. The # The following calculates the overhead for using a profiler. The
# problem is that it takes a fair amount of time for the profiler # problem is that it takes a fair amount of time for the profiler
# to stop the stopwatch (from the time it recieves an event). # to stop the stopwatch (from the time it recieves an event).
@ -481,35 +481,36 @@ class Profile:
# profiler very much, and the accuracy goes way up. # profiler very much, and the accuracy goes way up.
#************************************************************** #**************************************************************
def calibrate(self, m): def calibrate(self, m):
# Modified by Tim Peters
n = m n = m
s = self.timer() s = self.get_time()
while n: while n:
self.simple() self.simple()
n = n - 1 n = n - 1
f = self.timer() f = self.get_time()
my_simple = f[0]+f[1]-s[0]-s[1] my_simple = f - s
#print "Simple =", my_simple, #print "Simple =", my_simple,
n = m n = m
s = self.timer() s = self.get_time()
while n: while n:
self.instrumented() self.instrumented()
n = n - 1 n = n - 1
f = self.timer() f = self.get_time()
my_inst = f[0]+f[1]-s[0]-s[1] my_inst = f - s
# print "Instrumented =", my_inst # print "Instrumented =", my_inst
avg_cost = (my_inst - my_simple)/m avg_cost = (my_inst - my_simple)/m
#print "Delta/call =", avg_cost, "(profiler fixup constant)" #print "Delta/call =", avg_cost, "(profiler fixup constant)"
return avg_cost return avg_cost
# simulate a program with no profiler activity # simulate a program with no profiler activity
def simple(self): def simple(self):
a = 1 a = 1
pass pass
# simulate a program with call/return event processing # simulate a program with call/return event processing
def instrumented(self): def instrumented(self):
a = 1 a = 1
self.profiler_simulation(a, a, a) self.profiler_simulation(a, a, a)

View file

@ -93,43 +93,43 @@ def test():
import sys import sys
import getopt import getopt
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'td') opts, args = getopt.getopt(sys.argv[1:], 'td')
except getopt.error, msg: except getopt.error, msg:
sys.stdout = sys.stderr sys.stdout = sys.stderr
print msg print msg
print "usage: quopri [-t | -d] [file] ..." print "usage: quopri [-t | -d] [file] ..."
print "-t: quote tabs" print "-t: quote tabs"
print "-d: decode; default encode" print "-d: decode; default encode"
sys.exit(2) sys.exit(2)
deco = 0 deco = 0
tabs = 0 tabs = 0
for o, a in opts: for o, a in opts:
if o == '-t': tabs = 1 if o == '-t': tabs = 1
if o == '-d': deco = 1 if o == '-d': deco = 1
if tabs and deco: if tabs and deco:
sys.stdout = sys.stderr sys.stdout = sys.stderr
print "-t and -d are mutually exclusive" print "-t and -d are mutually exclusive"
sys.exit(2) sys.exit(2)
if not args: args = ['-'] if not args: args = ['-']
sts = 0 sts = 0
for file in args: for file in args:
if file == '-': if file == '-':
fp = sys.stdin fp = sys.stdin
else: else:
try: try:
fp = open(file) fp = open(file)
except IOError, msg: except IOError, msg:
sys.stderr.write("%s: can't open (%s)\n" % (file, msg)) sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
sts = 1 sts = 1
continue continue
if deco: if deco:
decode(fp, sys.stdout) decode(fp, sys.stdout)
else: else:
encode(fp, sys.stdout, tabs) encode(fp, sys.stdout, tabs)
if fp is not sys.stdin: if fp is not sys.stdin:
fp.close() fp.close()
if sts: if sts:
sys.exit(sts) sys.exit(sts)
if __name__ == '__main__': if __name__ == '__main__':
test() test()

View file

@ -203,7 +203,7 @@ def joinfields(words, sep = ' '):
(joinfields and join are synonymous) (joinfields and join are synonymous)
""" """
res = '' res = ''
for w in words: for w in words:
res = res + (sep + w) res = res + (sep + w)
@ -430,7 +430,7 @@ def ljust(s, width):
# Right-justify a string # Right-justify a string
def rjust(s, width): def rjust(s, width):
"""rjust(s, width) -> string """rjust(s, width) -> string
Return a right-justified version of s, in a field of the Return a right-justified version of s, in a field of the
specified width, padded with spaces as needed. The string is specified width, padded with spaces as needed. The string is
@ -443,7 +443,7 @@ def rjust(s, width):
# Center a string # Center a string
def center(s, width): def center(s, width):
"""center(s, width) -> string """center(s, width) -> string
Return a center version of s, in a field of the specified Return a center version of s, in a field of the specified
width. padded with spaces as needed. The string is never width. padded with spaces as needed. The string is never
@ -508,7 +508,8 @@ def translate(s, table, deletions=""):
""" """
if type(table) != type('') or len(table) != 256: if type(table) != type('') or len(table) != 256:
raise TypeError, "translation table must be 256 characters long" raise TypeError, \
"translation table must be 256 characters long"
res = "" res = ""
for c in s: for c in s:
if c not in deletions: if c not in deletions:

View file

@ -203,7 +203,7 @@ def joinfields(words, sep = ' '):
(joinfields and join are synonymous) (joinfields and join are synonymous)
""" """
res = '' res = ''
for w in words: for w in words:
res = res + (sep + w) res = res + (sep + w)
@ -430,7 +430,7 @@ def ljust(s, width):
# Right-justify a string # Right-justify a string
def rjust(s, width): def rjust(s, width):
"""rjust(s, width) -> string """rjust(s, width) -> string
Return a right-justified version of s, in a field of the Return a right-justified version of s, in a field of the
specified width, padded with spaces as needed. The string is specified width, padded with spaces as needed. The string is
@ -443,7 +443,7 @@ def rjust(s, width):
# Center a string # Center a string
def center(s, width): def center(s, width):
"""center(s, width) -> string """center(s, width) -> string
Return a center version of s, in a field of the specified Return a center version of s, in a field of the specified
width. padded with spaces as needed. The string is never width. padded with spaces as needed. The string is never
@ -508,7 +508,8 @@ def translate(s, table, deletions=""):
""" """
if type(table) != type('') or len(table) != 256: if type(table) != type('') or len(table) != 256:
raise TypeError, "translation table must be 256 characters long" raise TypeError, \
"translation table must be 256 characters long"
res = "" res = ""
for c in s: for c in s:
if c not in deletions: if c not in deletions: