Python poem generator(s)

(Skip to the code)

Example Output

# linedpoem(getsourcetext(sourcetext, sourcetext, 3, " "),2,4)

Single day.
Silhouette but
the stair
texting hide

more. East
halfway used
you on
some catch,

or there
listens the
everything, attempt
across a

red something
at a
dims.


# linedpoem(getsourcetext(sourcetext, sourcetext, 4, " "),4,2)

They do try car.
Wine she actually hands.

An hand reaches whether
expensive enough. And louder

toward that door Staged
hide no lover by

any professional road, coming
i whether us ought

join at neighborhood the
space ever, quite, and

must forever we call
he because these tomorrow.


# linedpoem(getsourcetext(sourcetext, sourcetext, 4, "d"),4,2)

Dance she disappointed dashing
to do dim to

do day, or drunk
dryer delicately desperately. The

three divide it dear
that domestic dance divide

those dive divide another
dim-lit divide the dodging

day divide dozen to
that dropped dive, dodging

dance dims. They do
depressed drinking divide divide

dozen displayed, domestic dust
decided divide a down

dozen, Deary Dark, what
divide Deary divide another

domestic director dust or
day divide Deary, least

dim-lit divide dance, dashing
to dryer Deary, director

divide Doctorate two and
divide least their divide

to those depth but
depressed door. Deary down

declines decided to do
director divide his day.


# linedpoem(getsourcetext(sourcetext, sourcetext, 3, "an"),5,2)

Anticipation and a anticipation anyone
angled younger. She would finish

and- any louder anybody anyway
with anymore. Anticipation when feel

she.


# haiku(sourcetext)

Alto from sure unit
Seams chairs between
Overhead texts


# haiku(sourcetext)

This belt by Music
He dear connected with him refrigerator
That track on Economical

# progressive(sourcetext)

Party action scene.
Party action scene is wooden, and-.
Dance introduce such Party action scene.
What Party action scene.


# progressive(sourcetext)

Good time.
Good time is ten, serious.
Fuck sleep spiral-staircase Good time.
What Good time.


# station_at_the_metro(sourcetext)

The couch without any takes like the picnic
Shouts if another claw-footed spiral-staircase kneels


# station_at_the_metro(sourcetext)

The tile over a says between a purveyor
Roommates before these ironical cold heads


# linedpoem(randomtextgetter(sourcetext,5),4,4)

JOHN: I let her
stay in my guest
room when she comes
over to drink. Running

feet on the steps.
JOHN: Like I said,
you're trying to do
too much. Teller breaks

into a run. JOHN:
Like I said, you're
trying to do too
much.


# linedpoem(randomtextgetter(sourcetext,3),5,2)

BEN: The party's winding down
and Sam's asking me if

I want to go home
with him. The couple having

sex behind the sheet even
stop, their little heads poking

out to look. Call it
Jazz: smoky alto singer, tenor

sax.


# gridpoem(sourcetext, 80, 4, 2)

THESE               SPACE               ENGAGING            COUPLE
UP                  THIS                STAGE               BASICALLY
CHECK               HIS                 ASLEEP              CLOTHESLINES
REPRESENTING        OUT                 TO                  TALK
HUES                IN                  A                   PANTING


# gridpoem(sourcetext, 98, 5, 2)

RAILING         UNDERSTAND      ANOTHER         THIS            TOILET
LIES            AUBURN          NO              SIDE-STAGE      DEGREES
OUT                             THOSE           DANCE


Code



    # README

    'sourctext' is a TextBlob made from a text file of my first workshop story for
     this semester.

    The code was written in such a way as to be flexible, and to be used both in
    discreet units and in combinations. The original goal was to have the poems
    pull syntax from the works and use those, too, but it became very clear that,
    at the very least with my sample text, things like verb forms and other syntax
    were going to get pretty rowdy, as seen in some of the examples below.

    To that end, I also created a generator that would just pull sentences from the
    text at random, which you can then pass into some of the other poem forms. I
    did my best to keep the actual source text gathering and the poem generation as
    separate as possible, to keep things modular and allow play.

    There's still (obviously) a lot that could be done with this project. An
    obivous next step would be to try and replace chunks of syntax at a time, to
    hopefully overcome some of the weirdness that results from brute-force
    replacement.

    It would also be neat to try and work some forms that relate more closely to the
    subject of the text itself, but for now, I think these provide some interest.

    The poems themselves have been unedited to better show the raw output.

    The hash (#) indicates the line of code used to generate the poem.


  # Import the important things
  from textblob import TextBlob
  import random
  import re

  # borrow syllable counter "sylco"

  # code taken from https://eayd.in/?p=232
  # "Counting Syllables in the English Language Using Python"
  # couldn't find a license on that guy -- somebody asked in the comments,
  # so I'll just make a nod here nad acknowledge that I'm taking this word-for word

  def sylco(word) :

      word = word.lower()

      # exception_add are words that need extra syllables
      # exception_del are words that need less syllables

      exception_add = ['serious','crucial']
      exception_del = ['fortunately','unfortunately']

      co_one = ['cool','coach','coat','coal','count','coin','coarse','coup','coif','cook','coign','coiffe','coof','court']
      co_two = ['coapt','coed','coinci']

      pre_one = ['preach']

      syls = 0 #added syllable number
      disc = 0 #discarded syllable number

      #1) if letters < 3 : return 1
      if len(word) <= 3 :
          syls = 1
          return syls

      #2) if doesn't end with "ted" or "tes" or "ses" or "ied" or "ies", discard "es" and "ed" at the end.
      # if it has only 1 vowel or 1 set of consecutive vowels, discard. (like
       "speed", "fled" etc.)

      if word[-2:] == "es" or word[-2:] == "ed" :
          doubleAndtripple_1 = len(re.findall(r'[eaoui][eaoui]',word))
          if doubleAndtripple_1 > 1 or len(re.findall(r'[eaoui][^eaoui]',word)) > 1 :
              if word[-3:] == "ted" or word[-3:] == "tes" or word[-3:] == "ses" or word[-3:] == "ied" or word[-3:] == "ies" :
                  pass
              else :
                  disc+=1

      #3) discard trailing "e", except where ending is "le"

      le_except = ['whole','mobile','pole','male','female','hale','pale','tale','sale',
      'aisle','whale','while']

      if word[-1:] == "e" :
          if word[-2:] == "le" and word not in le_except :
              pass

          else :
              disc+=1

      #4) check if consecutive vowels exists, triplets or pairs, count them
       as one.

      doubleAndtripple = len(re.findall(r'[eaoui][eaoui]',word))
      tripple = len(re.findall(r'[eaoui][eaoui][eaoui]',word))
      disc+=doubleAndtripple + tripple

      #5) count remaining vowels in word.
      numVowels = len(re.findall(r'[eaoui]',word))

      #6) add one if starts with "mc"
      if word[:2] == "mc" :
          syls+=1

      #7) add one if ends with "y" but is not surrouned by vowel
      if word[-1:] == "y" and word[-2] not in "aeoui" :
          syls +=1

      #8) add one if "y" is surrounded by non-vowels and is not in the last
       word.

      for i,j in enumerate(word) :
          if j == "y" :
              if (i != 0) and (i != len(word)-1) :
                  if word[i-1] not in "aeoui" and word[i+1] not in "aeoui" :
                      syls+=1

      #9) if starts with "tri-" or "bi-" and is followed by a vowel, add one.

      if word[:3] == "tri" and word[3] in "aeoui" :
          syls+=1

      if word[:2] == "bi" and word[2] in "aeoui" :
          syls+=1

      #10) if ends with "-ian", should be counted as two syllables, except
       for "-tian" and "-cian"

      if word[-3:] == "ian" :
      #and (word[-4:] != "cian" or word[-4:] != "tian") :
          if word[-4:] == "cian" or word[-4:] == "tian" :
              pass
          else :
              syls+=1

      #11) if starts with "co-" and is followed by a vowel, check if exists
       in the double syllable dictionary, if not, check if in single
        dictionary and act accordingly.

      if word[:2] == "co" and word[2] in 'eaoui' :

          if word[:4] in co_two or word[:5] in co_two or word[:6] in co_two :
              syls+=1
          elif word[:4] in co_one or word[:5] in co_one or word[:6] in co_one :
              pass
          else :
              syls+=1

      #12) if starts with "pre-" and is followed by a vowel, check if exists
       in the double syllable dictionary, if not, check if in single dictionary and act accordingly.

      if word[:3] == "pre" and word[3] in 'eaoui' :
          if word[:6] in pre_one :
              pass
          else :
              syls+=1

      #13) check for "-n't" and cross match with dictionary to add syllable.

      negative = ["doesn't", "isn't", "shouldn't", "couldn't","wouldn't"]

      if word[-3:] == "n't" :
          if word in negative :
              syls+=1
          else :
              pass

      #14) Handling the exceptional words.

      if word in exception_del :
          disc+=1

      if word in exception_add :
          syls+=1

      # calculate the output
      return numVowels - disc + syls

  # -------------------------------------------#
  #            Get things we need              #
  # -------------------------------------------#

  # add a function to make a textblob for whatever source you're using. returns a textblob
  def createblob(textfile):
      source = open(textfile) # open the source
      s = source.read() # read the source
      sourceblob = TextBlob(s) # make a textblob
      source.close() # be clean, close the source
      return sourceblob

  # make a dictionary of word/pos-tag pairs. returns a dictionary.
  def makedictionary(sourceblob):
      dictionary = {}
      for word, tag in sourceblob.tags:
          #dictionary[str(word.lemmatize())] = str(tag) # with lemmatize
          dictionary[str(word)] = str(tag) #without lemmatize

      removelist = ['ca', 'ma', 'wo', 'ade', 'fa'] # some common fragements,
       we'll just remove
      for fragment in removelist:
          if fragment.lower() in dictionary:
              del dictionary[fragment]

      for noun_phrase in sourceblob.noun_phrases:
          dictionary[str(noun_phrase)] = "noun_phrase"

      return dictionary

  # pull some syntax out of the blob. returns a random sentence structure
   (note: currenlty only remembers commas and periods.)

  def getsyntax(sourceblob):
      structures = list()
      for sentence in sourceblob.sentences:
          clauses = sentence.split(',')
          clauselist = list(clauses)
          sentencestructure = []

          for i in range(0,len(clauselist)):
              if i < (len(clauselist) - 1):
                  sentencestructure.append(clauselist[i])
                  sentencestructure.append(',')
              else:
                  sentencestructure.append(clauselist[i])
                  sentencestructure.append('.')

          taggedsentencestructure = []

          for clause in sentencestructure:
              if clause == ',' or clause == '.':
                  taggedsentencestructure.append(clause)
              else:
                  clauseblob = TextBlob(str(clause))
                  for word, tag in clauseblob.tags:
                      taggedsentencestructure.append(tag)
          sentenceForm = " ".join(taggedsentencestructure)
          structures.append(sentenceForm)
      return structures[random.randint(0,len(structures)-1)]

  # function to pull a random word given a part of speech from the
   dictionary. used below.

  def getword(POStag, dictionary):
      import random
      collect = []
      if POStag in dictionary.values():
          if POStag == 'NNP' or POStag == 'NNPS':
              for x in dictionary:
                  if dictionary[x] == POStag:
                      collect.append(x.capitalize()) #capitalize proper names
          else:
              for x in dictionary:
                  if dictionary[x] == POStag:
                      collect.append(x.lower())
          return collect[random.randint(0,len(collect)-1)]
      else: # this should never really happen, but
          return "There aren't any words with that tag in this dictionary."

  # replace structural elements from a sentence structure with random words
   from the same part of speech from a dictionary. returns a wordlist /
    sentence

  def replacewords(sentenceStructure, sourceblob, fun_parameter):

      # how to use "fun_parameter":
      # if you input a string, that will work as a "dumb" alliterator,
      # and will priorizie words beginning with the letters from the string in the output.
      #
      # if you enter in an integeer, it will only select words less than or
      # equal to that character length.

      # build  dictionaries
      full_dictionary = makedictionary(sourceblob)
      alliterate_dictionary = {}

      # pull the alliterations into a new dicdtionary
      if type(fun_parameter) is str:
          for x in full_dictionary:
              if x[:(len(fun_parameter))].lower() == fun_parameter.lower():
                  alliterate_dictionary[x] = full_dictionary[x]

      POSlist = sentenceStructure.split(' ')
      sentence = []
      for pos in POSlist:
          if pos == ",":
              sentence.append(pos)
          elif pos == ".":
              sentence.append(pos)
          else:
              x = (getword(pos, alliterate_dictionary))
              if x == "There aren't any words with that tag in this dictionary.":
                  word = getword(pos, full_dictionary)
                  if type(fun_parameter) is int:
                      if len(word) <= fun_parameter:
                          sentence.append(word)
                      else:
                          word = getword(pos, full_dictionary)

                  else:
                      if word == "There aren't any words with that tag in this dictionary.":
                          sentence.append(pos) # this enables us to "plant" words in a sentence structure
                                               # yes, I know this is kind of inelegant.
                      else:
                          sentence.append(word)
              else:
                  sentence.append(getword(pos, alliterate_dictionary))

      sentence[0] = sentence[0].capitalize() # makes it look like a sentence
      return " ".join(sentence)

  # the returned sentence above might be kind of ugly. this function cleans
   it up a lot. returns a cleaner string

  def grammarcleans(string):

      newstring = string
      # clean up the punctuation a little.
      if re.findall(' , ', newstring) != []:
          newstring = newstring.replace(" , ", ", ")
      if re.findall(' ; ', newstring) != []:
          newstring = newstring.replace(" ; ", "; ")
      if re.findall(' . ', newstring) != []:
          newstring = newstring.replace(" . ", ". ")
      if re.findall(' .', newstring) != []:
          newstring = newstring.replace(" .", ". ")


      # clean up apostrophe phrases
      if re.findall(" 's ", newstring) != []:
          newstring = newstring.replace(" 's ", " is ")
      if re.findall(" 're ", newstring) != []:
          newstring = newstring.replace(" 're ", " are ")
      if re.findall(" 've ", newstring) != []:
          newstring = newstring.replace(" 've ", " have ")
      if re.findall(" n't ", newstring) != []:
          newstring = newstring.replace(" n't ", " not ")
      if re.findall(" 'd ", newstring) != []:
          newstring = newstring.replace(" 'd ", " had ")
      if re.findall(" ' ", newstring) != []:
          newstring = newstring.replace(" ' ", " ")
      if re.findall(" 'm ", newstring) != []:
          newstring = newstring.replace(" 'm ", " am ")
      if re.findall(" 'll ", newstring) != []:
          newstring = newstring.replace(" 'll ", " will ")

      # why not make the syntax weird? Reaplce all "an"s with "a"
      if re.findall(" an ", newstring) != []:
          newstring = newstring.replace(" an ", " a ")

      # also going to put a placehodler for common poor-replaces here...

      return newstring

  # now, let's combine all the above to get some text to play with for our
   poems.

  def getsourcetext(sourceblob_syntax, sourceblob_dictionary, length, alliteration):
      text = []
      for i in range(0, length):
          text.append(grammarcleans(replacewords(getsyntax(sourceblob_syntax), sourceblob_dictionary, alliteration)))
      return "".join(text) # don't need a space becasue that's already built
       into the sentences

  # this is also fun to play with, when you want real grammar and just want
   to mix things up:

  def randomtextgetter(sourceblob, length): # length in sentences
      text = []
      for i in range(0,length):
          text.append(str(sourceblob.sentences[random.randint(0,len(sourceblob.sentences)-1)]))

      return " ".join(text)

  # break syllables!

  def breaksyllables(string, sylbreak):
      words = string.split(" ")
      line = []
      leftover = []
      i = 0
      for i in range(0,len(words)):
          if sylco(" ".join(words[:i])) < sylbreak:
              line.append(words[i])

      leftover = " ".join(words[len(line):])

      print " ".join(line)
      if len(leftover) != 0:
          breaksyllables(leftover, sylbreak)

  # -------------------------------------------#
  #              Poetry Forms!                 #
  # -------------------------------------------#

  # finally getting into some text-generation, this function spits out a poem
   based upon a sample text, line lenght, and stanza length, all of which will
    remain consistent for the purposes of this function, until the sample text
     runs out of words.

  def linedpoem(cleanedstring, wordsinline, linesinstanza):
      # gather the words and things
      poemtext = cleanedstring.split(" ")

      stanzas = -(-len(poemtext)// (wordsinline*linesinstanza)) # borrow fancy rounding trick via Stack Exchange
      wordsinstanza = wordsinline*linesinstanza

      for s in range (0,stanzas): # for each stanza
          stanzatext = poemtext[s*wordsinstanza:wordsinstanza*(s+1)]
          lines = (len(stanzatext) / wordsinline)

          for i in range(0,lines):
              line = " ".join(stanzatext[wordsinline*i:wordsinline*(i+1)])
              if len(line) > 0 and line[0] == " ":
                  line = line[1:]
              print line

          if lines*wordsinline < len(poemtext):
              line = str(" ".join(stanzatext[lines*wordsinline:len(stanzatext)]))
              if len(line) > 0 and line[0] == " ":
                  line = line[1:]
              print line

  def haiku(sourceblob):
      # pick a haiku template, borrowed from examples from wikipeida

      haikuforms = [['NN IN JJ NN', 'NN NNS IN', 'NN POS NN'],['DT JJ JJ NN',
       'RB DT NN VBZ TO VB', 'DT JJ NN IN NN'],['DT NN IN NNP', 'PRP VBP VBN IN PRP NN',
        'DT NN IN NNP'], ['IN NNP POS NN', 'WRB JJ NNS VBD PRP VB',
         'NN'], ['DT NN IN NNP', 'PRP VBP VBN IN PRP NN', 'DT NN IN NNP']]

      a = random.randint(0,len(haikuforms)-1)
      form = haikuforms[a]

      for line in form:
          text = replacewords(line, sourceblob, "")
          text = grammarcleans(text)
          print text

  def station_at_the_metro(sourceblob):
      # copping pound

      form = ['The NN IN DT VBZ IN DT NN', 'NNS IN DT JJ JJ NNS']

      for line in form:
          text = replacewords(line, sourceblob, '' )
          text = grammarcleans(text)
          print text

  def progressive(sourceblob):
      # this is just something I'm playing with. Poorly titled function.

      form = ["noun_phrase.", "noun_phrase is JJ , JJ .", "NN VB JJ noun_phrase.", "What noun_phrase."]

      subject = replacewords("noun_phrase", sourceblob, " ")

      for line in form:
          line = line.replace("noun_phrase", subject)
          line = replacewords(line, sourceblob, " ")
          if re.findall(' .', line) != []:
              line = line.replace(" .", ".")
          if re.findall(' ,', line) != []:
              line = line.replace(" ,", ",")
          print line

  def gridpoem(sourceblob, gridwidth, number_words_in_line, sentence_length):

      wordspace = gridwidth / number_words_in_line

      if wordspace <= 6: # hedge against weird formating
          print "This poem isn't going to work as well as you want it to.\n\nTry something else."

      # grab a thing

      text = list(getsourcetext(sourceblob, sourceblob, sentence_length , wordspace).split(" "))

      spaced_text = []
      for word in text:
          spaced_text.append(word + " "*(wordspace-len(word)))

      lined_text = zip(*[iter(spaced_text)]*number_words_in_line)
      # prints a perfect grid

      for line in lined_text:
          newstring = "".join(line).upper()
          if re.findall(',', newstring) != []:
              newstring = newstring.replace(",", " ")
          if re.findall(';', newstring) != []:
              newstring = newstring.replace(";", " ")
          if re.findall('.', newstring) != []:
              newstring = newstring.replace(".", " ")
          print newstring

      leftovers = spaced_text[(len(lined_text)*number_words_in_line):]
      # collects the leftovers that didn't fit in the grid

      # now make things pretty... or at least do something somewhat interesting for now
      leftoverline = []
      for i in range(0,len(leftovers)):
          if i == 0:
              leftoverline.append(leftovers[i] + " "*wordspace)
          else:
              leftoverline.append(leftovers[i])

      newstring = "".join(leftoverline).upper()
      if re.findall(',', newstring) != []:
          newstring = newstring.replace(",", " ")
      if re.findall(';', newstring) != []:
          newstring = newstring.replace(";", " ")
      if re.findall('.', newstring) != []:
          newstring = newstring.replace(".", " ")
      print newstring

  def wcw_poem(source):
      words = source.split(" ")
      line = []
      leftover = []
      for i in range(0,len(words)):
          if sylco(" ".join(words[:i])) < 4:
              line.append(words[i])
      leftover = words[len(line):]

      line2 = []
      for i in range(0,len(leftover)):
          if sylco(" ".join(leftover[:i])) < 2:
              line2.append(leftover[i])
      leftover2 = leftover[len(line2):]

      print " ".join(line)
      print " ".join(line2) + "\n"
      if len(leftover2) != 0:
          wcw_poem(" ".join(leftover2))

    

go back