Ravenbrook / Projects / Perforce Defect Tracking Integration

Perforce Defect Tracking Integration Project


Python interface to tTrack

Gareth Rees, Ravenbrook Limited, 2000-08-08

1. Introduction

This document describes a Python extension that provides an interface to the TeamShare API, and specifically to the tTrack defect tracking system.

The purpose of this document is to make it possible for people to maintain the extension, and to use the Python interface.

The intended readership is anyone working on the p4dti project.

2. Interface reference

2.1. Overview

The interface defines one module, ttrack (use import ttrack). There are two classes, representing tTrack servers and records from the tTrack database. These classes don't have names in Python: the only way to make a server object is to connect to a server using ttrack.connect, and the only way to make record objects (at present) is to query the server.

2.2. Identifiers in the ttrack module

2.2.1. ttrack.error

The Python error object for errors generated by the TeamShare interface or by the TeamShare API. It is always associated with a message. For example:

try:
  # do ttrack stuff
except ttrack.error, message:
  print message

The ttrack module can throw other exceptions, notably KeyError (when a field name is not found in a record).

2.2.2. ttrack.tables

A dictionary that maps the name of a table in the tTrack database (minus the initial TS_) to its table identifier (a small integer). For example

ttrack.tables['CASES']

is the table identifier for the TS_CASES table.

2.2.3. ttrack.connect(user, password, hostname)

Attempts to connect to a TeamShare server on hostname (use the format "host:8080" to specify a non-default port number) with the specified userid and password. If it succeeds, it returns a server object representing the connection. If it fails, it raises a ttrack.error exception. For example:

server = ttrack.connect('joe', '', 'sandpiper')

2.3. Server methods

2.3.1. read_record(table_id, record_id)

Attempts to read the record from the specified table (which must be one of the table identifiers specified in ttrack.table) with the specified record identifier. If it succeeds, it returns a record object representing the record. If it fails, it raises a ttrack.error exception. For example, the call

record = server.read_record(ttrack.tables['CASES'], 17)

is roughly equivalent to the SQL query

SELECT * FROM TS_CASES WHERE TS_ID = 17

2.3.2. query(table_id, where_clause)

Executes an SQL query on the specified table (which must be one of the table identifiers specified in ttrack.table) of the form

SELECT * FROM table

(if where_clause is the empty string), or

SELECT * FROM table WHERE where_clause

(otherwise). If it is successful, the records matching the query are returned as list of record objects. If it fails, it raises a ttrack.error exception.

Remember to use the right field names in the where clause: the returned record may contain a field called foo, but the database field is probably TS_FOO. See [TeamShare 2000-01-20] for details of the TeamShare database.

2.4. Record methods

Records present (part of) the Python dictionary interface. To look up a field in a record object, index the record object with the field name. For example:

# Get the title of case 17
record = server.read_record(ttrack.tables['CASES'], 17)
title = record['TITLE']

To update a field in a record object, assign to the index expression. For example record['TITLE'] = 'Foo'.

As for ordinary Python dictionaries, the has_key method determines if a field is present in the record, and the keys method returns a list of names of fields in the record.

2.5. Examples

To get all the cases which have changed in the last hour:

import ttrack, time
s = ttrack.connect('joe', '', 'sandpiper')
t = time.time() - 3600
q = 'TS_ID IN (SELECT TS_CASEID FROM TS_CHANGES WHERE TS_TIME>%d)' % t
cases = s.query(ttrack.tables['CASES'], q)

3. Notes on the extension

Python extension modules are described in [Lutz 1996, 14]. Additional details with respect to building Python extensions using Visual C++ on Windows are given in [Hammond 2000, 22].

The TeamShare interface to tTrack is described in [TeamShare 2000-01-19].

3.1. Building the extension

I have only built the extension under Windows NT and Windows 2000. I believe it should build and run anywhere that Python and the TeamShare API run.

TeamShare provide two versions of their library: tsapi.lib and TSApiWin32.dll. I can build extensions using the former but not using the latter. I guess that the former is suitable for console applications and that latter for MFC applications.

There are three places where I have used Windows-specific code. In all cases the code is protected by #if defined(WIN32) ... #endif.

The file ttrackpython.h is a wrapper around Python.h that makes sure that the constant _DEBUG is not defined when Python.h is compiled. This is because Python.h contains a pair of #pragma statements that indicate to Visual C++ which library should be linked with (pythonnn.lib or pythonnn_d.lib for debugging). The problem with this is that Python extensions have to be linked with the same Python library that the Python interpreter and all other extensions are linked with. The Python distribution doesn't come with a debugging version of Python or any of its extensions, so we'd have to build all of these ourselves. It's better for us just to link with the release version of the Python library regardless. This is explained in [Hammond 2000, 22].

Sockets on Windows need to be initialized. The TeamShare API provides the function TSInitializeWinsock to do this. I call this from initttrack in ttrackmodule.cpp.

Functions that are exported from a DLL need either to have the declarator __declspec(dllexport) or to be mentioned in a /DLLEXPORT:foo compiler option. We use the former method, defining the macro TTRACK_EXPORTED for this purpose. The only exported function is inittrack in ttrackmodule.cpp.

3.2. Reference counting

Reference count management is briefly introduced in [Lutz 1996, page 585], but there's a much better account in [van Rossum 1999-04-13, 1.10].

I've commented each use of Py_DECREF with the new owner of the object, or "Destroy" if the intention is to destroy the object. Where a Py_DECREF would be expected (because the object has been passed to a new owner) but is not needed because the new owner does not increment the reference count, I have added a note to say so. This applied to objects passed to PyList_SetItem and PyTuple_SetItem (I guess that these functions are optimized for the case where a newly-created object is added to the structure). See [van Rossum 1999-04-13, 1.10.2].

3.3. Problems with the TeamShare API

A. References

[Hammond 2000] "Python Programming on Win32"; Mark Hammond and Andy Robinson; OReilly; 2000-01; ISBN 1-56592-621-8.
[Lutz 1996] "Programming Python"; Mark Lutz; O'Reilly; 1996-10; ISBN 1-56592-197-6.
[TeamShare 2000-01-19] "TeamShare API"; TeamShare; 2000-01-19.
[TeamShare 2000-01-20] "TeamTrack Database Schema (Database Version: 21)"; TeamShare; 2000-01-20.
[van Rossum 1999-04-13] "Extending and Embedding the Python Interpreter (release 1.5.2)"; Guido van Rossum; 1999-04-13.

B. Document History

2000-08-08 GDR Created

Copyright © 2000 Ravenbrook Limited. This document is provided "as is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this document. You may make and distribute verbatim copies of this document provided that you do not charge a fee for this document or for its distribution.

$Id: //info.ravenbrook.com/project/p4dti/version/0.1/ttrack/ttrack.html#2 $

Ravenbrook / Projects / Perforce Defect Tracking Integration