Tag: Python

Python – Generators

Do you know the difference between the following lines in Python?

square_numbers_1 = [x*x for x in [1,2,3,4,5,6]]
square_numbers_2 = (x*x for x in [1,2,3,4,5,6])

The first line will create a list of squares for each element of [1,2,3,4,5,6] in memory which means you can access it by using square brackets:

# This will return 1
print square_numbers_1[0]

The second line will return a Generator object which means you cannot use the square brackets.
You can access the values using a for loop or the next() method.

# This will print all items:
# 1
# 4
# 9
# 16
# 25
# 36
for number in square_numbers_2:
    print number 

# This will return one item each time.
# The following line will return 1.
print next(square_numbers_2)

You need to call ‘next’ whenever you need an item and you will receive an exception (StopIteration) when you call it more than the number of items. More than six times for square_numbers_2.

You can also create a Generator by calling yield as in the following example:

def square_numbers(numbers):
    for n in numbers:
        yield (n*n)

square_numbers_generator = square_numbers([1,2,3,4,5,6])

for number in square_numbers_generator:
    print number

Why is Generators useful?

  • Reduce the amount of memory used as it will only allocate memory when you retrieve an item
  • Run faster because it stops when it gets to the first yield in the above example

Python – httplib

You might want to use the requests module instead which is easier to use.
If you need to use httplib, this article is for you.

Here is a basic example on how to use httplib to access TeamCity REST API using just GET and POST:

Global variables:

TEAMCITY_SERVER="<teamcity-server>/httpAuth/app/rest/"
TEAMCITY_USER="user"
TEAMCITY_PASSWD="password"

Method for the HTTP request with basic HTTP authentication.
It returns a tuple with the following fields: status, reason and body:

def teamcity_api_request(url, data=None):
    """
    Send a TeamCity request using REST API
    """
    try:
        verb = 'POST' if data else 'GET'
        print "Sending HTTP " + verb + ": " + TEAMCITY_SERVER + url
        conn = httplib.HTTPConnection(TEAMCITY_SERVER)
        user_and_pass = b64encode(TEAMCITY_USER + ":" + TEAMCITY_PASSWD).decode("ascii")
        headers = {'Authorization':'Basic %s' %  user_and_pass, "Content-Type": "application/xml", "Accept": "application/json"}
        if data:
            print "HTTP Message Body: " + data
            conn.request(verb, url, data, headers=headers)
        else:
            conn.request(verb, url, headers=headers)
        res = conn.getresponse()
    except:
        print "Unexpected error:"
        print sys.exc_info()[0]
        print sys.exc_info()[1]
        print sys.exc_info()[2]
        print "Error while retrieving TeamCity information!"
        sys.exit(1)

    res_status = res.status
    res_reason = res.reason
    res_body = res.read()

    # Dumping out request
    print "--------------- HTTP response --------------------"
    print "Status: " + str(res_status)
    print "Reason: " + res_reason
    print "Body: "   + res_body
    print "--------------------------------------------------"

    # Stop execution if HTTP code is different than 200
    if res_status != 200:
        print "HTTP code is different than 200. Aborting..."
        sys.exit(1)

    request = collections.namedtuple('Request', ['status', 'reason', 'body'])
    # Convert body to json if request was successful
    if res_status == 200:
        res_body = json.loads(res_body)
    req = request(status=res_status, reason=res_reason, body=res_body)
    conn.close()
    return req

How to use it:

# GET - get a list of builds
request = teamcity_api_request("/builds")
print request.status

# POST - cancel a build
request = teamcity_api_request("/buildQueue/id:<build ID>", "<buildCancelRequest comment='<comment>' readdIntoQueue='true' />")
print request.status