[Tutor] module.class.method in logging.debug
Hi all, I have some code that is a plugin for a larger app, and I'd like to be able to properly log issues that arise in the plugin code. I may not be maintaining this code forever, and I'd like the logging to work even if it's refactored later and code moved around, method names change, etc. So I'd like my logging to include a string identifying (programatically) the "module.class.method" where things went afoul. I tried a couple of things I could think of, and successfully got the module and class without issue, but the one missing piece was the method name. I imagine a method can introspect and get its own name, but I haven't been able to find the incantation to make it work. I saw the documentation on the data model here: http://docs.python.org/reference/datamodel.html but I think the use of things like im_func or __func__ assume that you're referencing those as an attribute of a method object. I'm just not sure how that's done from inside the method itself. Clues hereby solicited. Humbly, brian -- Brian K. Jones Python Magazine http://www.pythonmagazine.com My Blog http://www.protocolostomy.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] module.class.method in logging.debug
On Mon, Mar 15, 2010 at 1:26 PM, Brian Jones wrote: > Hi all, > > I have some code that is a plugin for a larger app, and I'd like to be able > to properly log issues that arise in the plugin code. I may not be > maintaining this code forever, and I'd like the logging to work even if it's > refactored later and code moved around, method names change, etc. So I'd > like my logging to include a string identifying (programatically) the > "module.class.method" where things went afoul. > Well, after some more reading, I found that I can get the module *and* the method name if I add %(module)s and %(funcName) to my logging formatter. No class though, and it's all or nothing: I really only want that behavior for debug messages, not err/crit messages. Any other thoughts? -- Brian K. Jones Python Magazine http://www.pythonmagazine.com My Blog http://www.protocolostomy.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
[Tutor] What's the best way to ask forgiveness here?
I've been coding Python long enough that 'asking forgiveness instead of permission' is my first instinct, but the resulting code is sometimes clumsy, and I wonder if someone can suggest something I'm missing, or at least validate what's going on here in some way. What I'm trying to do is write a file to a directory. However, the directory may not exist the first time I try to write a file there, so I'm going to first try to write the file, and if I get an exception, create the directory (er, *try* to), and *then* write the file there. Here's my first shot at the code: try: self.save_file(picfile_fullpath, picdata) except IOError as err: # directory doesn't exist. Try to create it. try: os.makedirs(picfile_fullpath) except OSError as oserr: logging.error("Can't create file path: %s (%s)" % (picfile_fullpath, oserr)) else: # Created dir, now write file. try: self.save_file(picfile_fullpath, picdata) except IOError as err: logging.error("Bailing. Couldn't save file %s (%s)" % (picfile_fullpath, err)) return False Doesn't this seem less readable than the 'ask permission' equivalent? I think it does, but in this case asking permission for every single operation when the dir will only need to be created a single time (and then may be written to several hundred times) is pretty wasteful. I suppose I could set some sentinel variable and check for it in a while loop, but then I need some other scaffolding code to make sure I don't infinitely loop trying to create the directory, and probably some other stuff I'm forgetting, so it strikes me as being just as messy. Is there a clean sort of pattern to apply in instances like this? Thanks. brian -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] What's the best way to ask forgiveness here?
Thanks for the replies so far. One thing that's probably relevant: once a directory is created, I can expect to write a couple of hundred files to it, so doing a 'try os.makedirs' right off the bat strikes me as coding for the *least* common case instead of the *most* common (which is that the directory exists and the file write succeeds). If this were a one-off file write well then things get easier, but I'd like to avoid attempting a makedirs 100 times when 99 of those times I know it'll give me an error. brian On Mon, Sep 13, 2010 at 3:07 PM, Evert Rol wrote: > > I've been coding Python long enough that 'asking forgiveness instead of > permission' is my first instinct, but the resulting code is sometimes > clumsy, and I wonder if someone can suggest something I'm missing, or at > least validate what's going on here in some way. > > > > What I'm trying to do is write a file to a directory. However, the > directory may not exist the first time I try to write a file there, so I'm > going to first try to write the file, and if I get an exception, create the > directory (er, *try* to), and *then* write the file there. > > That would work. > Though I would just try to create the error directory first. If that fails, > I check the error message/code in the except clause: if it indicates the > directory already exists, I pass the exception, otherwise reraise it. > Then I continue with creating the file within the directory I now am > certain of exists. > Note 1: certain is only half: some other process could remove the directory > in the split second between my code creating it and my code creating the > file. If you think that could happen, you'll need to look at the other ways > to do this. > Note 2: the directory can exist, but not have write access to your process. > So between the try-except for creating a directory and the try-except for > creating a file, you may put in a try-except for chmod. Of course, if you're > not the owner, both the chmod and file creation will fail (I'm assuming some > *nix platform here, btw). > > > Here's my first shot at the code: > > > > try: > > self.save_file(picfile_fullpath, picdata) > > except IOError as err: > > # directory doesn't exist. Try to create it. > > Careful: you're not checking the actually error given by the exception. > There may be more than one reason that the file can't be created (examples: > the permissions mentioned above, or some file creation limit in a > directory). > > > > try: > > os.makedirs(picfile_fullpath) > > except OSError as oserr: > > logging.error("Can't create file path: %s (%s)" % > (picfile_fullpath, oserr)) > > else: > > # Created dir, now write file. > > try: > > self.save_file(picfile_fullpath, picdata) > > except IOError as err: > > logging.error("Bailing. Couldn't save file %s (%s)" % > (picfile_fullpath, err)) > > return False > > > > Doesn't this seem less readable than the 'ask permission' equivalent? I > think it does, but in this case asking permission for every single operation > when the dir will only need to be created a single time (and then may be > written to several hundred times) is pretty wasteful. > > One of the things I once read (but I forgot where) on this issue, is the > usual "it depends". It depends whether you except that often, the directory > doesn't exist and the file can't directly be created. In that case, if may > be quicker to ask permission first (simple if-statement). If you expect that > in general, you can immediately create the file within the directory (so an > exception is unlikely to occur), then use a try-except clause. > But don't take my word for it; I'd be curious what others on this list say > about this. > > > > > > I suppose I could set some sentinel variable and check for it in a while > loop, but then I need some other scaffolding code to make sure I don't > infinitely loop trying to create the directory, and probably some other > stuff I'm forgetting, so it strikes me as being just as messy. > > > > Is there a clean sort of pattern to apply in instances like this? > > I guess not, though readability of code is an important thing to consider. > In this case, I don't find the correctly unreadable, but at some point, > these things do tend to get out of hand and you're better off coding it > differently (perhaps even using functions). > > > Evert > > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] input and raw input
On Sat, Sep 25, 2010 at 9:40 AM, Evert Rol wrote: > > any one have an idea about how we can input many number in the one time > and change it to list. > > for example: > > > > a=input("Enter the number of your class in the school:") # the number > can be enter as: 12,13,14 or 12 13 14 with a space in between. > > > > now how I can put these numbers into list like b=[12,13,14] with len( a ) > =3 > > A string has a method split(); that may help you. > In your case, where you want either a space or a comma as a separator, it > depends whether both can be used at the same time. If not, you can check for > the occurrence of one or the other separator and run split() with the > correct separator. If both can occur in the same line, you may want to use > the regex module instead: re.split() > No need for the 're' module. Even in the case where both can be used together, you can still just use string methods: >>> s '12, 13 14' >>> s.replace(',', '').split(' ') ['12', '13', '14'] > hth, > > Evert > > > > I tried with that but it's working only for a numbers less than 10 ex. > 1,2,3 or 1 2 3 but it's not when I go for numbers higher than 10 like in > example above. > > > > a=raw_input("Enter the number of your class in the school:") > > m=[] > > for I range (len( a)): > > if a[I]==',': > > pass > > elif a[I]==' ': > > pass > > else: > > m.append(a[I]) > > m=map(float,m) > > print m;print len( m ) > > >> [1,2,3] > > >> 3 > > > > looking forward to seeing your help, > > regards, > > Ahmed > > > > > > > > ___ > > Tutor maillist - Tutor@python.org > > To unsubscribe or change subscription options: > > http://mail.python.org/mailman/listinfo/tutor > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] dynamic arrays?
On Mon, Sep 27, 2010 at 11:32 AM, Alex Hall wrote: > Hi all, > One thing I have never much liked about Python is its need for > specifically sized arrays and lack of a dynamic, array-like data > structure. For example, the following fails with a "list assignment > index out of range" error: > > a=[] > i=0 > for l in open("file.txt", "r"): > a[i]=l > i+=1 > Is there some reason to use this construct rather than the list object's 'append' method? for i in open('file.txt', 'r'): a.append(l) brian > > Is there something in Python I am missing that would let the above > work? I am hoping that my annoyance at the apparent lack of such a > thing is unfounded. BTW, I know why the above throws that exception. > TIA. > > -- > Have a great day, > Alex (msg sent from GMail website) > mehg...@gmail.com; http://www.facebook.com/mehgcap > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] function with multiple checks
On Mon, Sep 27, 2010 at 11:09 AM, Tim Miller wrote: > I've got a small function that I'm using to check whether a password is of > a certain length and contains mixed case, numbers and punctuation. > > Originally I was using multiple "if re.search" for the patterns but it > looked terrible so I've read up on list comprehensions and it's slightly > improved. I just can't escape the feeling that there's a more elegant way to > do this that I'm missing. > > I've been looking through all the python stuff that I thought might be > relevant (lambda, map, filter, set, frozenset, etc) but nothing has come > together. Just wondering if anyone has suggested reading material for > alternate ways they'd handle this code. > > CODE: > > from string import ascii_lowercase, ascii_uppercase, digits, punctuation > > > def complex_password(password): >"""Checks password for sufficient complexity.""" >if len(password) < 12: >return False >if len([c for c in password if c in punctuation]) == 0: >return False >if len([c for c in password if c in digits]) == 0: >return False >if len([c for c in password if c in ascii_uppercase]) == 0: >return False >if len([c for c in password if c in ascii_lowercase]) == 0: >return False >return True > You can probably make other optimizations, but just to start, you can get rid of 'len' and '== 0': if not [c for c in password if c in ascii_lowercase]: return False will return False if there are no lowercase characters. An empty list evaluates to False. With that in mind, you could just 'return [c for c in password if c in ascii_lowercase]' if the calling code is written to handle it. > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] dynamic arrays?
On Mon, Sep 27, 2010 at 11:39 AM, Alex Hall wrote: > On 9/27/10, Brian Jones wrote: > > On Mon, Sep 27, 2010 at 11:32 AM, Alex Hall wrote: > > > >> Hi all, > >> One thing I have never much liked about Python is its need for > >> specifically sized arrays and lack of a dynamic, array-like data > >> structure. For example, the following fails with a "list assignment > >> index out of range" error: > >> > >> a=[] > >> i=0 > >> for l in open("file.txt", "r"): > >> a[i]=l > >> i+=1 > >> > > > > Is there some reason to use this construct rather than the list object's > > 'append' method? > > > > for i in open('file.txt', 'r'): > > a.append(l) > > Ah, good thought. So exactly what are the functional differences > between a list and an array in Python, or are they so close that it > makes no difference which you use? It seems like you can index into > both with the bracket notation. Are lists limited in any way that > makes them not as desirable as arrays? > A python list and a python array are one and the same to my knowledge. Do you mean a list and a dict? A dict is an unordered collection of key-value pairs, and the keys are more or less arbitrary values of your own creation (I believe the single limitation is the key must be a hashable type). A list is a 'flat' array, although you can use enumerate(l) to get index->value pairs from a list 'l'. hth. > > > > brian > > > >> > >> Is there something in Python I am missing that would let the above > >> work? I am hoping that my annoyance at the apparent lack of such a > >> thing is unfounded. BTW, I know why the above throws that exception. > >> TIA. > >> > >> -- > >> Have a great day, > >> Alex (msg sent from GMail website) > >> mehg...@gmail.com; http://www.facebook.com/mehgcap > >> ___ > >> Tutor maillist - Tutor@python.org > >> To unsubscribe or change subscription options: > >> http://mail.python.org/mailman/listinfo/tutor > >> > > > > > > > > -- > > Brian K. Jones > > My Blog http://www.protocolostomy.com > > Follow me http://twitter.com/bkjones > > > > > -- > Have a great day, > Alex (msg sent from GMail website) > mehg...@gmail.com; http://www.facebook.com/mehgcap > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] function with multiple checks
On Mon, Sep 27, 2010 at 11:43 AM, Brian Jones wrote: > > > On Mon, Sep 27, 2010 at 11:09 AM, Tim Miller wrote: > >> I've got a small function that I'm using to check whether a password is of >> a certain length and contains mixed case, numbers and punctuation. >> >> Originally I was using multiple "if re.search" for the patterns but it >> looked terrible so I've read up on list comprehensions and it's slightly >> improved. I just can't escape the feeling that there's a more elegant way to >> do this that I'm missing. >> >> I've been looking through all the python stuff that I thought might be >> relevant (lambda, map, filter, set, frozenset, etc) but nothing has come >> together. Just wondering if anyone has suggested reading material for >> alternate ways they'd handle this code. >> >> CODE: >> >> from string import ascii_lowercase, ascii_uppercase, digits, punctuation >> >> >> def complex_password(password): >>"""Checks password for sufficient complexity.""" >>if len(password) < 12: >>return False >>if len([c for c in password if c in punctuation]) == 0: >>return False >>if len([c for c in password if c in digits]) == 0: >>return False >>if len([c for c in password if c in ascii_uppercase]) == 0: >>return False >>if len([c for c in password if c in ascii_lowercase]) == 0: >>return False >>return True >> > How about this: d = [digits, punctuation, ascii_uppercase, ascii_lowercase] s = 'asdf1234A' for c in d: if not [x for x in s if x in c]: print x, ' not in ', c Just a quick hack, but you get the idea. It breaks when you want different numbers of characters from the different lists in the password. > > > You can probably make other optimizations, but just to start, you can get > rid of 'len' and '== 0': > > if not [c for c in password if c in ascii_lowercase]: >return False > > will return False if there are no lowercase characters. An empty list > evaluates to False. With that in mind, you could just 'return [c for c in > password if c in ascii_lowercase]' if the calling code is written to handle > it. > > > > >> ___ >> Tutor maillist - Tutor@python.org >> To unsubscribe or change subscription options: >> http://mail.python.org/mailman/listinfo/tutor >> > > > > -- > Brian K. Jones > My Blog http://www.protocolostomy.com > Follow me http://twitter.com/bkjones > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] IDE for Python
On Thu, Oct 7, 2010 at 11:23 AM, Juan Jose Del Toro wrote: > Dear List; > > In your experience what is the best IDE for Python? > > I've used SPE and IDLE, I've also seen people using Eclipse but which one > do you recommend? > There is no 'best for Python'. IDEs are made to please people, not languages. I've been looking for a good Python IDE for a long time. Actually, I've been looking for the IDE kool-aid. I always felt like I was missing something because I was using vim while all the cool kids were using IDEs. So I've tried different ones, several times, over the past several years. While I still use vim quite a lot (with some plugins like a class browser), I've recently discovered that PyCharm fits my vim-altered brain quite well. It has by far the best vim emulation mode ever, it understands unit tests and will run them for you, good Git integration (and others, so I hear), and it generally does a good job of staying out of my way when I just want to dive down the rabbit hole and code. :) If it's too heavy for your taste, I used to use Komodo Edit when I wanted something more than vim. It's lighter (in features and process 'weight'), but it's pretty good. good luck. > -- > ¡Saludos! / Greetings! > Juan José Del Toro M. > jdeltoro1...@gmail.com > Guadalajara, Jalisco MEXICO > > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Stumped Again
On Sat, Oct 30, 2010 at 7:12 PM, Terry Green wrote: > *Am running this Script and cannot figure out how to close my files,* > > *Keep getting msg: Attribute Error: ‘_csv.writer’ object has no attribute > ‘close’* > > *Why?* > Because csv.writer objects don't have a close() method. Files do :) > testOutput = > csv.writer(open('c:/users/terry/downloads/tup1012k/tup1012.csv', 'w'), > See this line? testOutput is a csv.writer object, which you pass a file to. How about creating a file handle first, and passing that in, like this: testOutputFile = open('c:/users/terry/downloads/whatever.csv', 'w') testOutputCSV = csv.writer(testOutputFile)testOutputFile.close() You can also use the 'with' statement: with open('c:/users/terry/whatever.csv', 'w') as myfile: testOutputCSV = csv.writer(myfile) The 'with' statement is used with ContextManager objects, and files implement the ContextManager protocol which (among other things) means that the file will be closed in the above snippet without you having to explicitly call close() on it. hth. brian > delimiter=',', > > quotechar='"', quoting=csv.QUOTE_NONNUMERIC) > > csvreader = > csv.reader(open("c:/users/terry/downloads/tup1012k/tup1012x.drf","r"),delimiter=',') > > for row in csvreader: > > test=('test4') > > track=row[0] > > rdate=row[1] > > race=row[2] > > postPos=row[3] > > entry=row[4] > > distance=row[5] > > surface=row[6] > > Reserved=row[7] > > raceType=row[8] > > ageSex=row[9] > > todaysRaceClassification=row[10] > > purse=row[11] > > claimingPrice=row[12] > > jockey=row[32] > > jockeySts = int(row[34]) > > jockeyWins = int(row[35]) > > horseName=row[44] > > daysSinceLR=row[223] > > try: > > jockeyPct=round((jockeyWins/jockeySts)*100) > > except ZeroDivisionError: > > print > (track,race,postPos,horseName,jockey,jockeyWins,jockeySts,0) > > else: > > print > (track,race,postPos,horseName,jockey,jockeyWins,jockeySts,jockeyPct) > > > testOutput.writerow((track,race,postPos,horseName,jockey,jockeyWins,jockeySts,jockeyPct,'\n')) > > testOutput.close() > > csvreader.close() > > > > Please Help! > > > > Terry Green > > > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > http://mail.python.org/mailman/listinfo/tutor > > -- Brian K. Jones My Blog http://www.protocolostomy.com Follow me http://twitter.com/bkjones ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
[Tutor] oo design/interaction quandary
Hi all, I'm having a design issue that's really bothering me. The code I'm writing is fairly large by now, but I've written what I think is a decent example that illustrates my problem. My app launches threads that each consume messages from a queue, send them to a processor object, and then the processor needs to return the message to the main thread -- if the processing was successful -- and then the main thread puts the message into a different queue. Here's a shortened version of my code. class Processor(object): def __init__(self): self.qhost = 'localhost' self.qport = '5672' self.uname = 'guest' self.pwd = 'guest' self.ssl = false self.vhost = '/' self.exch_name = 'fooX' self.exch_type = 'direct' self.queue_name = 'fooQ' self.conn = amqp.Connection(userid=self.uname, password=self.pwd, host=self.qhost, virtual_host=self.vhost, ssl=self.ssl) self.chan = self.conn.channel() self.chan.exchange_declare(self.exch_name, type=self.exch_type) self.chan.queue_declare(self.qname) self.chan.queue_bind(self.qname, self.exch_name) def consume(self, callback): self.chan.basic_consume(self.qname, callback=callback) while True: self.chan.wait() class Munger(object): def munge(self,msg): if msg % 2 == 0: yield msg class Sender(object): def run(self): p = Processor() m = Munger() for msg in p.consume(m.munge): """ I know this doesn't work right now. This piece of the code should send 'msg' to another queue. """ pass if __name__ == '__main__': s = Sender() s.run() The problem might be obvious to you, but I'll quickly explain: The Sender object (a thread in the real code), is calling p.consume, which just wraps the basic_consume method in py-amqplib. The basic_consume method registers a consumer and a callback with the amqp server. The chan.wait() call blocks and waits for messages. When the messages arrive they are passed to the python callable we passed to the basic_consume method. Therein lies the problem. Messages go from my Processor object, to the Munger object, and this is all initiated by the Sender object, but I can't find a clean way to get messages successfully processed by Munger back to Sender, so that Sender can requeue the message to the new queue. I've thought about having a Queue object in Sender, or maybe registering Sender as an observer of (at different times) the Munger or Processor objects, but I'd like this to be as simple and understandable as possible by other developers, because I'm wanting to make the Processor and Munger objects pluggable (so the Processor can support different queuing protocols, Munger can do literally anything... etc). Ideas welcome. If I've failed to explain some aspect of my issue, give me a poke and I'll expand as best I can. brian Brian K. Jones Python Magazine http://www.pythonmagazine.com My Blog http://www.protocolostomy.com ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
[Tutor] send os.system() output to tarfile?
Hi all, I've been lurking on this list for some time. It's great. Thanks for all the help. I'm a sysadmin by trade, and have slowly started using Python more and more in my work. However, this is my first experience with using the tarfile module. I'm currently writing a script to backup a mysql database. On the cli, I'd do something like this: 'mysqldump dbname | gzip -9 > dbname-date.gz' Note that "gzip -9" could just as easily be "tar cvzf" for example. Anyway, what I'm trying to do is figure out the right way to redirect the output generated by "os.system(mysqldump dbname)" to my tarfile object for compressing. What I tried were a few variations on this: == #!/usr/bin/env python import os import time import tarfile import sys filename = time.strftime("%Y%m%d") + ".tgz" tarball = tarfile.open(filename, "w|gz", fileobj = sys.stdout) os.system( "ls -alrt" ) tarball.close() == I also played around with redirecting stdout to the tarfile object, and some other things that are probably stuff you'd expect from a python n00b. What's the right way to do this? I was hoping to not be forced down the road of creating temp files, compressing *those*, and then blowing them away at the end. Any help greatly appreciated. brian. -- Brian K. Jones Python Magazine http://www.pythonmagazine.com My Blog http://m0j0.wordpress.com ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor