String handling and the percent operator

T

Tom Plunket

I have some code to autogenerate some boilerplate code so that I don't
need to do the tedious setup stuff when I want to create a new module.

So, my script prompts the user for the module name, then opens two
files and those files each get the contents of one of these functions:

def GetPyContents(module):
boilerplate = \
"""
class %s:
pass

if __name__ == '__main__':
import unittest
unittest.main('%s_t')
"""

return boilerplate % ((module,) * 2)

def GetTestContents(module):
boilerplate = \
"""from %s import *
import unittest

class Test%s(unittest.TestCase):
def testConstruction(self):
self.failUnless(%s())

def testWriteMoreTests(self):
self.fail('This test should fail.')

if __name__ == '__main__':
unittest.main()
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

thx.
-tom!
 
S

Simon Forman

Tom said:
I have some code to autogenerate some boilerplate code so that I don't
need to do the tedious setup stuff when I want to create a new module.

So, my script prompts the user for the module name, then opens two
files and those files each get the contents of one of these functions:

def GetPyContents(module):
boilerplate = \
"""
class %s:
pass

if __name__ == '__main__':
import unittest
unittest.main('%s_t')
"""

return boilerplate % ((module,) * 2)

def GetTestContents(module):
boilerplate = \
"""from %s import *
import unittest

class Test%s(unittest.TestCase):
def testConstruction(self):
self.failUnless(%s())

def testWriteMoreTests(self):
self.fail('This test should fail.')

if __name__ == '__main__':
unittest.main()
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

thx.
-tom!


strings have a count() method.

Since you know that you won't have things like '%%s' in your
boilerplate, it's perfectly reasonable to use:

return boilerplate % ((module,) * boilerplate.count('%s'))

in your code.

Peace,
~Simon

return boilerplate % ((module,) * 3)
 
T

Tom Plunket

Simon said:
strings have a count() method.

thanks!

For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?
-tom!
 
E

Erik Max Francis

Tom said:
For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?

Use tuple(SentenceGenerator()). A generator is just another object, so
using it with the % operator tries to substitute it at one value. (Even
with this fix, though, your message didn't have enough formatters.)
 
J

Justin Azoff

Tom said:
boilerplate = \
""" [big string]
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

thx.
-tom!

Of course..
.... http://docs.%(lang)s.org/lib/%(page)s""" % stuff
I should read the python documentation at
http://docs.python.org/lib/typesseq-strings.html
 
B

Brett g Porter

Justin said:
Tom said:
boilerplate = \
""" [big string]
"""

return boilerplate % ((module,) * 3)
[deletia...]

Of course..
... http://docs.%(lang)s.org/lib/%(page)s""" % stuff
I should read the python documentation at
http://docs.python.org/lib/typesseq-strings.html

....or perhaps cooler:
http://docs.python.org/lib/node109.html

something like:

from string import Template

def GetPyContents(module):
boilerplate = Template( \
"""
class $moduleName:
pass

if __name__ == '__main__':
import unittest
unittest.main('${moduleName}_t')
""")

return boilerplate.substitute(moduleName=module)
 
S

Simon Forman

Tom said:
Simon said:
strings have a count() method.

thanks!

For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?
-tom!

If you're asking if there's some way that the generator can know how
many formatting fields are in message then the straightforward answer
is no. At least not without passing the message to the generator for
it to call count() itself, and then you're better off just doing it
without the generator.

(Actually, IIRC, this was discussed quite recently on this list, and
(again IIRC) I think there is some deep voodoo that can do this, but
you're much better off without it.)

FWIW, in your shoes I would use the trick Justin Azoff posted, i.e.:

boiler = 'foo %(modname)s bar %(modname)s baz'

message = boiler % {'modname': modname}


Peace,
~Simon
 
B

Bruno Desthuilliers

Tom said:
I have some code to autogenerate some boilerplate code so that I don't
need to do the tedious setup stuff when I want to create a new module.

So, my script prompts the user for the module name, then opens two
files and those files each get the contents of one of these functions:

def GetPyContents(module):
boilerplate = \
"""
class %s:
pass

if __name__ == '__main__':
import unittest
unittest.main('%s_t')
"""

return boilerplate % ((module,) * 2)

def GetTestContents(module):
boilerplate = \
"""from %s import *
import unittest

class Test%s(unittest.TestCase):
def testConstruction(self):
self.failUnless(%s())

def testWriteMoreTests(self):
self.fail('This test should fail.')

if __name__ == '__main__':
unittest.main()
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

Python's string formatting comes in two flavours : positional (the one
you used in your above example), and keywords:

tpls = [
"%(name1)s is %(name1)s and %(name2)s is %(name2)s",
"what about %(name2)s for %(name1)s ?",
"Now we only deal with %(name1)s",
]

data = {'name1' : 'parrot', 'name2': 'dead'}

for tpl in tpls:
print tpl % data

As you can see, the keyword flavour doesn't care about positions nor
repetitions.

You may also want to look at more featured templating solutions like
empy, cheetah, etc...
 
E

Erik Max Francis

Tom said:
Excellent. Thanks. Has this been around long? I "learned" Python in
the 1.6 days iirc, but haven't done much except simple scripting with
it since...

Yep. Been around since at least 1.5.x.
 
T

Tom Plunket

Erik said:
For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?

Use tuple(SentenceGenerator()). A generator is just another object, so
using it with the % operator tries to substitute it at one value. (Even
with this fix, though, your message didn't have enough formatters.)

I know that the message didn't have enough formatters, that's why I
asked. (Although I would have assumed that the generator would get
automatically converted to a sequence that was consumable by the
interpolation operator...)

When using a generator via .next(), there's no requirement that it is
used until it is exhausted. E.g.

def NewId():
v = 0
while 1:
yield v
v += 1

NextId = NewId().next

BUTTON_OK = NextId()
BUTTON_CANCEL = NextId()
BUTTON_YAY = NextId()

Hence my question being "something like" rather than "something
equivalent to". Alas, little did I know that the answer I was looking
for was not even up the same path.

-tom!
 
E

Erik Max Francis

Tom said:
I know that the message didn't have enough formatters, that's why I
asked. (Although I would have assumed that the generator would get
automatically converted to a sequence that was consumable by the
interpolation operator...)

That's because::

aFormatString % anObject

is a shortcut for::

aFormatString % (anObject,)

so things like::

print "Your birthday is on %s" % date

are allowed. So when the object is an iterator, it's just treated as a
single value in a 1-tuple, rather than iterated over to get its own tuple.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top