drmaa-0.5/0000755000076500007650000000000011706215743013600 5ustar sirolaesirolae00000000000000drmaa-0.5/drmaa/0000755000076500007650000000000011706215743014664 5ustar sirolaesirolae00000000000000drmaa-0.5/drmaa/__init__.py0000644000076500007650000004716411531703560017004 0ustar sirolaesirolae00000000000000# ----------------------------------------------------------- # Copyright (C) 2008 StatPro Italia s.r.l. # # StatPro Italia # Via G. B. Vico 4 # I-20123 Milano # ITALY # # phone: +39 02 96875 1 # fax: +39 02 96875 605 # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the license for more details. # ----------------------------------------------------------- # # Author: Enrico Sirola """\ A python package for DRM job submission and control. This package is an implementation of the DRMAA 1.0 Python language binding specification (http://www.ogf.org/documents/GFD.143.pdf). See http://drmaa-python.googlecode.com for package info and download. """ __docformat__ = "restructuredtext en" import ctypes as _ct try: import namedtuple as _nt except ImportError: # pre 2.6 behaviour import nt as _nt __version__ = "$Revision$"[11:-2] import drmaa.const as _c from drmaa.const import JobState, JobControlAction, JobSubmissionState import drmaa.wrappers as _w import drmaa.helpers as _h from drmaa.errors import (AlreadyActiveSessionException, AuthorizationException, ConflictingAttributeValuesException, DefaultContactStringException, DeniedByDrmException, DrmCommunicationException, DrmsExitException, DrmsInitException, ExitTimeoutException, HoldInconsistentStateException, IllegalStateException, InternalException, InvalidAttributeFormatException, InvalidContactStringException, InvalidJobException, InvalidJobTemplateException, NoActiveSessionException, NoDefaultContactStringSelectedException, ReleaseInconsistentStateException, ResumeInconsistentStateException, SuspendInconsistentStateException, TryLaterException, UnsupportedAttributeException, InvalidArgumentException, InvalidAttributeValueException, OutOfMemoryException,) Version = _h.Version JobInfo = _nt.namedtuple("JobInfo", """jobId hasExited hasSignal terminatedSignal hasCoreDump wasAborted exitStatus resourceUsage""") # FileTransferMode = _nt.namedtuple("FileTransferMode", # """transferInputStream transferOutputStream # transferErrorStream""") class JobTemplate(object): """A job to be submitted to the DRM.""" HOME_DIRECTORY = '$drmaa_hd_ph$' """Home directory placeholder.""" WORKING_DIRECTORY = '$drmaa_wd_ph$' """Working directory placeholder.""" PARAMETRIC_INDEX = '$drmaa_incr_ph$' """Parametric index (for job arrays / bulk jobs) placeholder.""" @property def attributeNames(self): """\ The list of supported DRMAA scalar attribute names. This is apparently useless now, and should probably substituted by the list of attribute names of the JobTemplate instances. """ return list(_h.attribute_names_iterator()) # scalar attributes remoteCommand = _h.Attribute(_c.REMOTE_COMMAND ) """The command to be executed.""" jobSubmissionState = _h.Attribute(_c.JS_STATE ) """The job status.""" workingDirectory = _h.Attribute(_c.WD ) """The job working directory.""" jobCategory = _h.Attribute(_c.JOB_CATEGORY ) """The job category.""" nativeSpecification = _h.Attribute(_c.NATIVE_SPECIFICATION ) """\ A (DRM-dependant) opaque string to be passed to the DRM representing other directives. """ blockEmail = _h.Attribute( _c.BLOCK_EMAIL, type_converter=_h.BoolConverter(true='1', false='0')) """False id this job should send an email, True otherwise.""" startTime = _h.Attribute(_c.START_TIME ) """The job start time, a partial timestamp string.""" jobName = _h.Attribute(_c.JOB_NAME ) """The job Name.""" inputPath = _h.Attribute(_c.INPUT_PATH ) """The path to a file representing job's stdin.""" outputPath = _h.Attribute(_c.OUTPUT_PATH ) """The path to a file representing job's stdout.""" errorPath = _h.Attribute(_c.ERROR_PATH ) """The path to a file representing job's stderr.""" joinFiles = _h.Attribute( _c.JOIN_FILES, type_converter=_h.BoolConverter()) """True if stdin and stdout should be merged, False otherwise.""" # the following is available on ge6.2 only if enabled via cluster # configuration transferFiles = _h.Attribute(_c.TRANSFER_FILES ) """\ True if file transfer should be enabled, False otherwise. This option might require specific DRM configuration (it does on SGE). """ # the following are apparently not available on ge 6.2 # it will raise if you try to access these attrs deadlineTime = _h.Attribute(_c.DEADLINE_TIME ) """The job deadline time, a partial timestamp string.""" hardWallclockTimeLimit = _h.Attribute(_c.WCT_HLIMIT, _h.IntConverter) """\ 'Hard' Wallclock time limit, in seconds. The job will be killed by the DRM if it takes more than 'hardWallclockTimeLimit' to complete. """ softWallclockTimeLimit = _h.Attribute(_c.WCT_SLIMIT, _h.IntConverter) """\ 'Soft' Wallclock time limit, in seconds. The job will be signaled by the DRM if it takes more than 'hardWallclockTimeLimit' to complete. """ hardRunDurationLimit = _h.Attribute(_c.DURATION_HLIMIT, _h.IntConverter) """\ WRITE ME. """ softRunDurationLimit = _h.Attribute(_c.DURATION_SLIMIT, _h.IntConverter) """\ WRITE ME. """ # vector attributes email = _h.VectorAttribute(_c.V_EMAIL) """email addresses to whom send job completion info.""" args = _h.VectorAttribute(_c.V_ARGV) """The job's command argument list.""" # dict attributes jobEnvironment = _h.DictAttribute(_c.V_ENV) """The job's environment dict.""" _as_parameter_ = None def __init__(self, **kwargs): """\ Builds a JobTemplate instance. Attributes can be passed as keyword arguments.""" jt = _ct.pointer(_ct.POINTER(_w.drmaa_job_template_t)()) _h.c(_w.drmaa_allocate_job_template, jt) self._jt = self._as_parameter_ = jt.contents try: for aname in kwargs: setattr(self, aname, kwargs.get(aname)) except: self.delete() raise def delete(self): """Deallocate the underlying DRMAA job template.""" _h.c(_w.drmaa_delete_job_template, self) def __enter__(self): """context manager enter routine""" return self def __exit__(self, *_): """\ context manager exit routine. Stops communication with the DRM. """ self.delete() return False class Session(object): """\ The DRMAA Session. This class is the entry point for communicating with the DRM system """ TIMEOUT_WAIT_FOREVER = _c.TIMEOUT_WAIT_FOREVER TIMEOUT_NO_WAIT = _c.TIMEOUT_NO_WAIT JOB_IDS_SESSION_ANY = _c.JOB_IDS_SESSION_ANY JOB_IDS_SESSION_ALL = _c.JOB_IDS_SESSION_ALL contact = _h.SessionStringAttribute(_w.drmaa_get_contact) """\ a comma delimited string list containing the contact strings available from the default DRMAA implementation, one element per DRM system available. If called after initialize(), this method returns the contact String for the DRM system to which the session is attached. The returned strings are implementation dependent. """ drmsInfo = _h.SessionStringAttribute(_w.drmaa_get_DRM_system) """\ If called before initialize(), this method returns a comma delimited list of DRM systems, one element per DRM system implementation provided. If called after initialize(), this method returns the selected DRM system. The returned String is implementation dependent. """ drmaaImplementation = _h.SessionStringAttribute( _w.drmaa_get_DRMAA_implementation) """\ If called before initialize(), this method returns a comma delimited list of DRMAA implementations, one element for each DRMAA implementation provided. If called after initialize(), this method returns the selected DRMAA implementation. The returned String is implementation dependent and may contain the DRM system as a component. """ version = _h.SessionVersionAttribute() """\ a Version object containing the major and minor version numbers of the DRMAA library. For DRMAA 1.0, major is 1 and minor is 0. """ def __init__(self, contactString=None): self.contactString = contactString # no return value @staticmethod def initialize(contactString=None): """\ Used to initialize a DRMAA session for use. :Parameters: contactString : string or None implementation-dependent string that may be used to specify which DRM system to use This method must be called before any other DRMAA calls. If contactString is None, the default DRM system is used, provided there is only one DRMAA implementation available. If there is more than one DRMAA implementation available, initialize() throws a NoDefaultContactStringSelectedException. initialize() should be called only once, by only one of the threads. The main thread is recommended. A call to initialize() by another thread or additional calls to initialize() by the same thread with throw a SessionAlreadyActiveException. """ _w.init(contactString) # no return value @staticmethod def exit(): """\ Used to disengage from DRM. This routine ends the current DRMAA session but doesn't affect any jobs (e.g., queued and running jobs remain queued and running). exit() should be called only once, by only one of the threads. Additional calls to exit() beyond the first will throw a NoActiveSessionException. """ _w.exit() # returns JobTemplate instance @staticmethod def createJobTemplate(): """\ Allocates a new job template. The job template is used to set the environment for jobs to be submitted. Once the job template has been created, it should also be deleted (via deleteJobTemplate()) when no longer needed. Failure to do so may result in a memory leak. """ return JobTemplate() # takes JobTemplate instance, no return value @staticmethod def deleteJobTemplate(jobTemplate): """\ Deallocate a job template. :Parameters: jobTemplate : JobTemplate the job temptare to be deleted This routine has no effect on running jobs. """ jobTemplate.delete() # takes JobTemplate instance, returns string @staticmethod def runJob(jobTemplate): """\ Submit a job with attributes defined in the job template. :Parameters: jobTemplate : JobTemplate the template representing the job to be run The returned job identifier is a String identical to that returned from the underlying DRM system. """ jid = _ct.create_string_buffer(128) _h.c(_w.drmaa_run_job, jid, _ct.sizeof(jid), jobTemplate) return jid.value # takes JobTemplate instance and num values, returns string list @staticmethod def runBulkJobs(jobTemplate, beginIndex, endIndex, step): """\ Submit a set of parametric jobs, each with attributes defined in the job template. :Parameters: jobTemplate : JobTemplate the template representng jobs to be run beginIndex : int index of the first job endIndex : int index of the last job step : int the step between job ids The returned job identifiers are Strings identical to those returned from the underlying DRM system. The JobTemplate class defines a `JobTemplate.PARAMETRIC_INDEX` placeholder for use in specifying paths. This placeholder is used to represent the individual identifiers of the tasks submitted through this method. """ return list(_h.run_bulk_job(jobTemplate, beginIndex, endIndex, step)) # takes string and JobControlAction value, no return value @staticmethod def control(jobId, operation): """\ Used to hold, release, suspend, resume, or kill the job identified by jobId. :Parameters: jobId : string if jobId is `Session.JOB_IDS_SESSION_ALL` then this routine acts on all jobs submitted during this DRMAA session up to the moment control() is called. The legal values for action and their meanings are operation : string possible values are: `JobControlAction.SUSPEND` stop the job `JobControlAction.RESUME` (re)start the job `JobControlAction.HOLD` put the job on-hold `JobControlAction.RELEASE` release the hold on the job `JobControlAction.TERMINATE` kill the job To avoid thread races in multithreaded applications, the DRMAA implementation user should explicitly synchronize this call with any other job submission calls or control calls that may change the number of remote jobs. This method returns once the action has been acknowledged by the DRM system, but does not necessarily wait until the action has been completed. Some DRMAA implementations may allow this method to be used to control jobs submitted external to the DRMAA session, such as jobs submitted by other DRMAA session in other DRMAA implementations or jobs submitted via native utilities. """ _h.c(_w.drmaa_control, jobId, _c.string_to_control_action(operation)) # takes string list, num value and boolean, no return value @staticmethod def synchronize(jobIds, timeout=-1, dispose=False): """\ Waits until all jobs specified by jobList have finished execution. :Parameters: jobIds If jobIds contains `Session.JOB_IDS_SESSION_ALL`, then this method waits for all jobs submitted during this DRMAA session up to the moment synchronize() is called timeout : int maximum time (in seconds) to be waited for the completion of a job. The value `Session.TIMEOUT_WAIT_FOREVER` may be specified to wait indefinitely for a result. The value `Session.TIMEOUT_NO_WAIT` may be specified to return immediately if no result is available. dispose : bool specifies how to treat the reaping of the remote job's internal data record, which includes a record of the job's consumption of system resources during its execution and other statistical information. If set to True, the DRM will dispose of the job's data record at the end of the synchroniize() call. If set to False, the data record will be left for future access via the wait() method. To avoid thread race conditions in multithreaded applications, the DRMAA implementation user should explicitly synchronize this call with any other job submission calls or control calls that may change the number of remote jobs. If the call exits before the timeout has elapsed, all the jobs have been waited on or there was an interrupt. If the invocation exits on timeout, an ExitTimeoutException is thrown. The caller should check system time before and after this call in order to be sure of how much time has passed. """ if dispose: d = 1 else: d = 0 _h.c(_w.drmaa_synchronize, _h.string_vector(jobIds), timeout, d) # takes string and long, returns JobInfo instance @staticmethod def wait(jobId, timeout=-1): """\ Wait for a job with jobId to finish execution or fail. :Parameters: `jobId` : str The job id to wait completion for. If the special string, `Session.JOB_IDS_SESSION_ANY`, is provided as the jobId, this routine will wait for any job from the session `timeout` : float The timeout value is used to specify the desired behavior when a result is not immediately available. The value `Session.TIMEOUT_WAIT_FOREVER` may be specified to wait indefinitely for a result. The value `Session.TIMEOUT_NO_WAIT` may be specified to return immediately if no result is available. Alternatively, a number of seconds may be specified to indicate how long to wait for a result to become available This routine is modeled on the wait3 POSIX routine. If the call exits before timeout, either the job has been waited on successfully or there was an interrupt. If the invocation exits on timeout, an `ExitTimeoutException` is thrown. The caller should check system time before and after this call in order to be sure how much time has passed. The routine reaps job data records on a successful call, so any subsequent calls to wait() will fail, throwing an `InvalidJobException`, meaning that the job's data record has been already reaped. This exception is the same as if the job were unknown. (The only case where wait() can be successfully called on a single job more than once is when the previous call to wait() timed out before the job finished.) """ stat = _ct.c_int() jid_out = _ct.create_string_buffer(128) job_id_out = _ct.c_int() rusage = _ct.pointer(_ct.POINTER(_w.drmaa_attr_values_t)()) _h.c(_w.drmaa_wait, jobId, jid_out, _ct.sizeof(jid_out), _ct.byref(stat), timeout, rusage) res_usage = _h.adapt_rusage(rusage) exited = _ct.c_int() _h.c(_w.drmaa_wifexited, _ct.byref(exited), stat) aborted = _ct.c_int() _h.c(_w.drmaa_wifaborted, _ct.byref(aborted), stat) signaled = _ct.c_int() _h.c(_w.drmaa_wifsignaled, _ct.byref(signaled), stat) coredumped = _ct.c_int() _h.c(_w.drmaa_wcoredump, _ct.byref(coredumped), stat) exit_status = _ct.c_int() _h.c(_w.drmaa_wexitstatus, _ct.byref(exit_status), stat) term_signal = _ct.create_string_buffer(_c.SIGNAL_BUFFER) _h.c(_w.drmaa_wtermsig, term_signal, _ct.sizeof(term_signal), stat) return JobInfo(jid_out.value, bool(exited), bool(signaled), term_signal.value, bool(coredumped), bool(aborted), int(exit_status.value), res_usage) # takes string, returns JobState instance @staticmethod def jobStatus(jobName): """\ returns the program status of the job identified by jobId. The possible values returned from this method are: * `JobState.UNDETERMINED`: process status cannot be determined, * `JobState.QUEUED_ACTIVE`: job is queued and active, * `JobState.SYSTEM_ON_HOLD`: job is queued and in system hold, * `JobState.USER_ON_HOLD`: job is queued and in user hold, * `JobState.USER_SYSTEM_ON_HOLD`: job is queued and in user and system hold, * `JobState.RUNNING`: job is running, * `JobState.SYSTEM_SUSPENDED`: job is system suspended, * `JobState.USER_SUSPENDED`: job is user suspended, * `JobState.DONE`: job finished normally, and * `JobState.FAILED`: job finished, but failed. The DRMAA implementation should always get the status of the job from the DRM system unless the status has already been determined to be FAILED or DONE and the status has been successfully cached. Terminated jobs return a FAILED status. """ status = _ct.c_int() _h.c(_w.drmaa_job_ps, jobName, _ct.byref(status)) return _c.status_to_string(status.value) def __enter__(self): """Context manager enter function""" self.initialize(self.contactString) return self def __exit__(self, *_): """Context manager exit function.""" self.exit() return False drmaa-0.5/drmaa/const.py0000644000076500007650000001126111531703560016360 0ustar sirolaesirolae00000000000000# ----------------------------------------------------------- # Copyright (C) 2009 StatPro Italia s.r.l. # # StatPro Italia # Via G. B. Vico 4 # I-20123 Milano # ITALY # # phone: +39 02 96875 1 # fax: +39 02 96875 605 # # email: info@riskmap.net # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the license for more details. # ----------------------------------------------------------- # # Author: Enrico Sirola # $Id$ """drmaa constants""" # drmaa_get_attribute() ATTR_BUFFER = 1024 # drmaa_get_contact() CONTACT_BUFFER = 1024 # drmaa_get_DRM_system() DRM_SYSTEM_BUFFER = 1024 # drmaa_get_DRM_system() DRMAA_IMPLEMENTATION_BUFFER = 1024 # Agreed buffer length constants # these are recommended minimum values ERROR_STRING_BUFFER = 1024 JOBNAME_BUFFER = 1024 SIGNAL_BUFFER = 32 # Agreed constants TIMEOUT_WAIT_FOREVER = -1 TIMEOUT_NO_WAIT = 0 JOB_IDS_SESSION_ANY = "DRMAA_JOB_IDS_SESSION_ANY" JOB_IDS_SESSION_ALL = "DRMAA_JOB_IDS_SESSION_ALL" SUBMISSION_STATE_ACTIVE = "drmaa_active" SUBMISSION_STATE_HOLD = "drmaa_hold" # Agreed placeholder names PLACEHOLDER_INCR = "$drmaa_incr_ph$" PLACEHOLDER_HD = "$drmaa_hd_ph$" PLACEHOLDER_WD = "$drmaa_wd_ph$" # Agreed names of job template attributes REMOTE_COMMAND = "drmaa_remote_command" JS_STATE = "drmaa_js_state" WD = "drmaa_wd" JOB_CATEGORY = "drmaa_job_category" NATIVE_SPECIFICATION = "drmaa_native_specification" BLOCK_EMAIL = "drmaa_block_email" START_TIME = "drmaa_start_time" JOB_NAME = "drmaa_job_name" INPUT_PATH = "drmaa_input_path" OUTPUT_PATH = "drmaa_output_path" ERROR_PATH = "drmaa_error_path" JOIN_FILES = "drmaa_join_files" TRANSFER_FILES = "drmaa_transfer_files" DEADLINE_TIME = "drmaa_deadline_time" WCT_HLIMIT = "drmaa_wct_hlimit" WCT_SLIMIT = "drmaa_wct_slimit" DURATION_HLIMIT = "drmaa_duration_hlimit" DURATION_SLIMIT = "drmaa_duration_slimit" # names of job template vector attributes V_ARGV = "drmaa_v_argv" V_ENV = "drmaa_v_env" V_EMAIL = "drmaa_v_email" NO_MORE_ELEMENTS = 25 def job_state(code): return _JOB_PS[code] class JobState(object): UNDETERMINED='undetermined' QUEUED_ACTIVE='queued_active' SYSTEM_ON_HOLD='system_on_hold' USER_ON_HOLD='user_on_hold' USER_SYSTEM_ON_HOLD='user_system_on_hold' RUNNING='running' SYSTEM_SUSPENDED='system_suspended' USER_SUSPENDED='user_suspended' USER_SYSTEM_SUSPENDED='user_system_suspended' DONE='done' FAILED='failed' # Job control action class JobControlAction(object): SUSPEND='suspend' RESUME='resume' HOLD='hold' RELEASE='release' TERMINATE='terminate' _JOB_CONTROL = [ JobControlAction.SUSPEND, JobControlAction.RESUME, JobControlAction.HOLD, JobControlAction.RELEASE, JobControlAction.TERMINATE ] def string_to_control_action(operation): return _JOB_CONTROL.index(operation) def control_action_to_string(code): return _JOB_CONTROL[code] def status_to_string(status): return _JOB_PS[status] _JOB_PS = { 0x00: JobState.UNDETERMINED, 0x10: JobState.QUEUED_ACTIVE , 0x11: JobState.SYSTEM_ON_HOLD , 0x12: JobState.USER_ON_HOLD , 0x13: JobState.USER_SYSTEM_ON_HOLD , 0x20: JobState.RUNNING , 0x21: JobState.SYSTEM_SUSPENDED , 0x22: JobState.USER_SUSPENDED , 0x23: JobState.USER_SYSTEM_SUSPENDED, 0x30: JobState.DONE , 0x40: JobState.FAILED , } # State at submission time class JobSubmissionState(object): HOLD_STATE=SUBMISSION_STATE_HOLD ACTIVE_STATE=SUBMISSION_STATE_ACTIVE _SUBMISSION_STATE = [ JobSubmissionState.HOLD_STATE, JobSubmissionState.ACTIVE_STATE ] def submission_state(code): return _SUBMISSION_STATE[code] drmaa-0.5/drmaa/errors.py0000644000076500007650000001032111531703560016542 0ustar sirolaesirolae00000000000000# ----------------------------------------------------------- # Copyright (C) 2009 StatPro Italia s.r.l. # # StatPro Italia # Via G. B. Vico 4 # I-20123 Milano # ITALY # # phone: +39 02 96875 1 # fax: +39 02 96875 605 # # email: info@riskmap.net # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the license for more details. # ----------------------------------------------------------- # # Author: Enrico Sirola """drmaa errors""" from ctypes import create_string_buffer from drmaa.const import ERROR_STRING_BUFFER class DrmaaException(Exception): """A common ancestor to all DRMAA Error classes.""" pass class AlreadyActiveSessionException(DrmaaException): pass class AuthorizationException(DrmaaException): pass class ConflictingAttributeValuesException(DrmaaException, AttributeError): pass class DefaultContactStringException(DrmaaException): pass class DeniedByDrmException(DrmaaException): pass class DrmCommunicationException(DrmaaException): pass class DrmsExitException(DrmaaException): pass class DrmsInitException(DrmaaException): pass class ExitTimeoutException(DrmaaException): pass class HoldInconsistentStateException(DrmaaException): pass class IllegalStateException(DrmaaException): pass class InternalException(DrmaaException): pass class InvalidAttributeFormatException(DrmaaException, AttributeError): pass class InvalidContactStringException(DrmaaException): pass class InvalidJobException(DrmaaException): pass class InvalidJobTemplateException(DrmaaException): pass class NoActiveSessionException(DrmaaException): pass class NoDefaultContactStringSelectedException(DrmaaException): pass class ReleaseInconsistentStateException(DrmaaException): pass class ResumeInconsistentStateException(DrmaaException): pass class SuspendInconsistentStateException(DrmaaException): pass class TryLaterException(DrmaaException): pass class UnsupportedAttributeException(DrmaaException, AttributeError): pass class InvalidArgumentException(DrmaaException, AttributeError): pass class InvalidAttributeValueException(DrmaaException, AttributeError): pass class OutOfMemoryException(DrmaaException, MemoryError): pass error_buffer = create_string_buffer(ERROR_STRING_BUFFER) def error_check(code): if code == 0: return else: try: raise _ERRORS[code-1]("code %s: %s" % (code, error_buffer.value)) except IndexError: raise DrmaaException("code %s: %s" % (code, error_buffer.value)) # da vedere: NO_RUSAGE, NO_MORE_ELEMENTS _ERRORS = [ InternalException, DrmCommunicationException, AuthorizationException, InvalidArgumentException, NoActiveSessionException, OutOfMemoryException, InvalidContactStringException, DefaultContactStringException, NoDefaultContactStringSelectedException, DrmsInitException, AlreadyActiveSessionException, DrmsExitException, InvalidAttributeFormatException, InvalidAttributeValueException, ConflictingAttributeValuesException, TryLaterException, DeniedByDrmException, InvalidJobException, ResumeInconsistentStateException, SuspendInconsistentStateException, HoldInconsistentStateException, ReleaseInconsistentStateException, ExitTimeoutException, Exception, StopIteration ] drmaa-0.5/drmaa/helpers.py0000644000076500007650000001655611531703560016710 0ustar sirolaesirolae00000000000000# ----------------------------------------------------------- # Copyright (C) 2009 StatPro Italia s.r.l. # # StatPro Italia # Via G. B. Vico 4 # I-20123 Milano # ITALY # # phone: +39 02 96875 1 # fax: +39 02 96875 605 # # email: info@riskmap.net # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the license for more details. # ----------------------------------------------------------- # # Author: Enrico Sirola """internal helpers""" import ctypes as _ct from drmaa.wrappers import * from drmaa.errors import error_buffer import drmaa.const as const _BUFLEN=const.ATTR_BUFFER try: import namedtuple as _nt except ImportError: # pre 2.6 behaviour import nt as _nt class BoolConverter(object): """Helper class to convert to/from bool attributes.""" def __init__(self, true='y', false='n'): self.true=true self.false=false def to_drmaa(self, value): if value: return self.true else: return self.false def from_drmaa(self, value): if value == self.true: return True else: return False class IntConverter(object): """Helper class to convert to/from float attributes.""" @staticmethod def to_drmaa(value): return str(value) @staticmethod def from_drmaa(value): return int(value) class SessionStringAttribute(object): def __init__(self, drmaa_function): self._f = drmaa_function def __get__(self, *args): buf = _ct.create_string_buffer(_BUFLEN) c(self._f, buf, _ct.sizeof(buf)) return buf.value Version = _nt.namedtuple("Version", "major minor") Version.__str__ = lambda x: "%s.%s" % (x.major, x.minor) #Version.__doc__ = """\ #An object representing the DRMAA version. # #major and minor attributes are int. For DRMAA 1.0, major == 1 and minor == 0. #""" class SessionVersionAttribute(object): """A Version attribute.""" def __get__(self, *args): major = _ct.c_uint(10) minor = _ct.c_uint(10) c(drmaa_version, _ct.byref(major), _ct.byref(minor)) return Version(major.value, minor.value) class Attribute(object): """A DRMAA attribute, to be managed with scalar C DRMAA attribute management functions.""" def __init__(self, name, type_converter=None): """\ Attribute constructor. :Parameters: `name` : string name of the attribute to be managed, as seen by the underlying C DRMAA `type_converter` a converter to translate attribute values to/from the underlying implementation. See BoolConverter for an example. """ self.name = name self.converter = type_converter def __set__(self, instance, value): if self.converter: v = self.converter.to_drmaa(value) else: v = value c(drmaa_set_attribute, instance, self.name, v) def __get__(self, instance, _): attr_buffer = create_string_buffer(const.ATTR_BUFFER) c(drmaa_get_attribute, instance, self.name, attr_buffer, sizeof(attr_buffer)) if self.converter: return self.converter.from_drmaa(attr_buffer.value) else: return attr_buffer.value class VectorAttribute(object): """\ A DRMAA attribute representing a list. To be managed with vector C DRMAA attribute management functions.""" def __init__(self, name): self.name = name def __set__(self, instance, value): c(drmaa_set_vector_attribute, instance, self.name, string_vector(value)) def __get__(self, instance, _): return list(vector_attribute_iterator( instance, self.name)) class DictAttribute(object): """\ A DRMAA attribute representing a python dict. To be managed with vector C DRMAA attribute management functions.""" def __init__(self, name): self.name = name def __set__(self, instance, value): v = [ "%s=%s" % (k, v) for (k, v) in value.iteritems() ] c(drmaa_set_vector_attribute, instance, self.name, string_vector(v)) def __get__(self, instance, _): x = [ i.split('=', 1) for i in list(vector_attribute_iterator( instance, self.name)) ] return dict(x) def attributes_iterator(attributes): try: buf = create_string_buffer(const.ATTR_BUFFER) while drmaa_get_next_attr_value(attributes, buf, sizeof(buf))\ != const.NO_MORE_ELEMENTS: yield buf.value except: drmaa_release_attr_values(attributes) raise else: drmaa_release_attr_values(attributes) def adapt_rusage(rusage): "transform a rusage data structure into a dict" rv = dict() for attr in attributes_iterator(rusage.contents): k, v = attr.split('=') rv[k] = v return rv def vector_attribute_iterator(jt, attr_name): avalues = pointer(POINTER(drmaa_attr_values_t)()) c(drmaa_get_vector_attribute, jt, attr_name, avalues) return attributes_iterator(avalues.contents) def attribute_names_iterator(): attrn_p = pointer(POINTER(drmaa_attr_names_t)()) c(drmaa_get_attribute_names, attrn_p) try: name = create_string_buffer(_BUFLEN) while drmaa_get_next_attr_name(attrn_p.contents, name, _BUFLEN)\ != const.NO_MORE_ELEMENTS: yield name.value except: drmaa_release_attr_names(attrn_p.contents) raise else: drmaa_release_attr_names(attrn_p.contents) def vector_attribute_names_iterator(): attrn_p = pointer(POINTER(drmaa_attr_names_t)()) c(drmaa_get_vector_attribute_names, attrn_p) try: name = create_string_buffer(_BUFLEN) while drmaa_get_next_attr_name(attrn_p.contents, name, _BUFLEN)\ != const.NO_MORE_ELEMENTS: yield name.value except: drmaa_release_attr_names(attrn_p.contents) raise else: drmaa_release_attr_names(attrn_p.contents) def run_bulk_job(jt, start, end, incr=1): jids = pointer(POINTER(drmaa_job_ids_t)()) try: c(drmaa_run_bulk_jobs, jids, jt, start, end, incr) jid = create_string_buffer(_BUFLEN) while drmaa_get_next_job_id(jids.contents, jid, _BUFLEN)\ != const.NO_MORE_ELEMENTS: yield jid.value except: drmaa_release_job_ids(jids.contents) raise else: drmaa_release_job_ids(jids.contents) def c(f, *args): """An helper function wrapping calls to the C DRMAA functions with error managing code.""" return f(*(args + (error_buffer, sizeof(error_buffer)))) def string_vector(v): vlen = len(v) values = (STRING * (vlen + 1))() for i, el in enumerate(v): values[i] = STRING(el) values[vlen] = STRING() return values def attribute_setter(obj, attribute_name): "returns a drmaa attribute setter" def f(value): "setter for %s" % attribute_name c(drmaa_set_attribute, obj, attribute_name, value) f.__name__ = 'set_'+attribute_name return f def attribute_getter(obj, attribute_name): "returns a drmaa attribute setter" def f(): "getter for %s" % attribute_name attr_buffer = create_string_buffer(1024) c(drmaa_get_attribute, obj, attribute_name, attr_buffer, sizeof(attr_buffer)) return attr_buffer.value f.__name__ = 'get_'+attribute_name return f drmaa-0.5/drmaa/nt.py0000644000076500007650000001051011531703560015647 0ustar sirolaesirolae00000000000000 """named tuple implementation""" from operator import itemgetter as _itemgetter from keyword import iskeyword as _iskeyword import sys as _sys def namedtuple(typename, field_names, verbose=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', 'x y') >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessable by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22) """ # Parse and validate the field names. Validation serves two purposes, # generating informative error messages and preventing template injection attacks. if isinstance(field_names, basestring): field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas field_names = tuple(field_names) for name in (typename,) + field_names: if not min(c.isalnum() or c=='_' for c in name): raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) if _iskeyword(name): raise ValueError('Type names and field names cannot be a keyword: %r' % name) if name[0].isdigit(): raise ValueError('Type names and field names cannot start with a number: %r' % name) seen_names = set() for name in field_names: if name.startswith('_'): raise ValueError('Field names cannot start with an underscore: %r' % name) if name in seen_names: raise ValueError('Encountered duplicate field name: %r' % name) seen_names.add(name) # Create and fill-in the class template numfields = len(field_names) argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes reprtxt = ', '.join('%s=%%r' % name for name in field_names) dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) template = '''class %(typename)s(tuple): '%(typename)s(%(argtxt)s)' \n __slots__ = () \n _fields = %(field_names)r \n def __new__(cls, %(argtxt)s): return tuple.__new__(cls, (%(argtxt)s)) \n @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): 'Make a new %(typename)s object from a sequence or iterable' result = new(cls, iterable) if len(result) != %(numfields)d: raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) return result \n def __repr__(self): return '%(typename)s(%(reprtxt)s)' %% self \n def _asdict(t): 'Return a new dict which maps field names to their values' return {%(dicttxt)s} \n def _replace(self, **kwds): 'Return a new %(typename)s object replacing specified fields with new values' result = self._make(map(kwds.pop, %(field_names)r, self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) return result \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = property(itemgetter(%d))\n' % (name, i) if verbose: print template # Execute the template string in a temporary namespace namespace = dict(itemgetter=_itemgetter) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template) result = namespace[typename] # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in enviroments where # sys._getframe is not defined (Jython for example). if hasattr(_sys, '_getframe'): result.__module__ = _sys._getframe(1).f_globals['__name__'] return result drmaa-0.5/drmaa/wrappers.py0000644000076500007650000002026011706215642017077 0ustar sirolaesirolae00000000000000# ----------------------------------------------------------- # Copyright (C) 2009 StatPro Italia s.r.l. # # StatPro Italia # Via G. B. Vico 4 # I-20123 Milano # ITALY # # phone: +39 02 96875 1 # fax: +39 02 96875 605 # # email: info@riskmap.net # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the license for more details. # ----------------------------------------------------------- # # Author: Enrico Sirola """DRMAA C library function wrappers""" from ctypes import * from ctypes.util import find_library from drmaa.errors import error_check, error_buffer import os # the name of the OS environment variable optionally # containing the full path to the drmaa library _drmaa_lib_env_name = 'DRMAA_LIBRARY_PATH' if _drmaa_lib_env_name in os.environ: libpath = os.environ[_drmaa_lib_env_name] else: libpath = find_library('drmaa') if libpath is None: errmsg = ' '.join(('could not find drmaa library.', 'Please specify its full', 'path using the environment variable', _drmaa_lib_env_name)) raise RuntimeError(errmsg) _lib = CDLL(libpath, mode=RTLD_GLOBAL) STRING = c_char_p size_t = c_ulong ptrdiff_t = c_int drmaa_init = _lib.drmaa_init drmaa_init.restype = error_check drmaa_init.argtypes = [STRING, STRING, size_t] drmaa_exit = _lib.drmaa_exit drmaa_exit.restype = error_check drmaa_exit.argtypes = [STRING, size_t] def init(contact=None): return _lib.drmaa_init(contact, error_buffer, sizeof(error_buffer)) _lib.drmaa_exit.argtypes = [c_char_p, c_size_t] _lib.drmaa_init.restype = error_check def exit(): return _lib.drmaa_exit(error_buffer, sizeof(error_buffer)) # structures class drmaa_job_template_s(Structure): pass drmaa_job_template_t = drmaa_job_template_s drmaa_job_template_s._fields_ = [ ] class drmaa_attr_names_s(Structure): pass drmaa_attr_names_t = drmaa_attr_names_s drmaa_attr_names_s._fields_ = [ ] class drmaa_attr_values_s(Structure): pass drmaa_attr_values_t = drmaa_attr_values_s drmaa_attr_values_s._fields_ = [ ] class drmaa_job_ids_s(Structure): pass drmaa_job_ids_t = drmaa_job_ids_s drmaa_job_ids_s._fields_ = [ ] drmaa_get_contact = _lib.drmaa_get_contact drmaa_get_contact.restype = error_check drmaa_get_contact.argtypes = [STRING, size_t, STRING, size_t] drmaa_version = _lib.drmaa_version drmaa_version.restype = error_check drmaa_version.argtypes = [POINTER(c_uint), POINTER(c_uint), STRING, size_t] drmaa_get_DRM_system = _lib.drmaa_get_DRM_system drmaa_get_DRM_system.restype = error_check drmaa_get_DRM_system.argtypes = [STRING, size_t, STRING, size_t] drmaa_get_DRMAA_implementation = _lib.drmaa_get_DRMAA_implementation drmaa_get_DRMAA_implementation.restype = error_check drmaa_get_DRMAA_implementation.argtypes = [STRING, size_t, STRING, size_t] drmaa_allocate_job_template = _lib.drmaa_allocate_job_template drmaa_allocate_job_template.restype = error_check drmaa_allocate_job_template.argtypes = [POINTER(POINTER(drmaa_job_template_t)), STRING, size_t] drmaa_delete_job_template = _lib.drmaa_delete_job_template drmaa_delete_job_template.restype = error_check drmaa_delete_job_template.argtypes = [POINTER(drmaa_job_template_t), STRING, size_t] drmaa_set_attribute = _lib.drmaa_set_attribute drmaa_set_attribute.restype = error_check drmaa_set_attribute.argtypes = [POINTER(drmaa_job_template_t), STRING, STRING, STRING, size_t] drmaa_get_attribute = _lib.drmaa_get_attribute drmaa_get_attribute.restype = error_check drmaa_get_attribute.argtypes = [POINTER(drmaa_job_template_t), STRING, STRING, size_t, STRING, size_t] drmaa_get_next_attr_name = _lib.drmaa_get_next_attr_name drmaa_get_next_attr_name.restype = c_int drmaa_get_next_attr_name.argtypes = [POINTER(drmaa_attr_names_t), STRING, size_t] drmaa_get_next_attr_value = _lib.drmaa_get_next_attr_value drmaa_get_next_attr_value.restype = c_int drmaa_get_next_attr_value.argtypes = [POINTER(drmaa_attr_values_t), STRING, size_t] drmaa_get_next_job_id = _lib.drmaa_get_next_job_id drmaa_get_next_job_id.restype = error_check drmaa_get_next_job_id.argtypes = [POINTER(drmaa_job_ids_t), STRING, size_t] drmaa_release_attr_names = _lib.drmaa_release_attr_names drmaa_release_attr_names.restype = None drmaa_release_attr_names.argtypes = [POINTER(drmaa_attr_names_t)] drmaa_release_attr_values = _lib.drmaa_release_attr_values drmaa_release_attr_values.restype = None drmaa_release_attr_values.argtypes = [POINTER(drmaa_attr_values_t)] drmaa_release_job_ids = _lib.drmaa_release_job_ids drmaa_release_job_ids.restype = None drmaa_release_job_ids.argtypes = [POINTER(drmaa_job_ids_t)] drmaa_set_vector_attribute = _lib.drmaa_set_vector_attribute drmaa_set_vector_attribute.restype = error_check drmaa_set_vector_attribute.argtypes = [POINTER(drmaa_job_template_t), STRING, POINTER(STRING), STRING, size_t] drmaa_get_vector_attribute = _lib.drmaa_get_vector_attribute drmaa_get_vector_attribute.restype = error_check drmaa_get_vector_attribute.argtypes = [POINTER(drmaa_job_template_t), STRING, POINTER(POINTER(drmaa_attr_values_t)), STRING, size_t] drmaa_get_attribute_names = _lib.drmaa_get_attribute_names drmaa_get_attribute_names.restype = error_check drmaa_get_attribute_names.argtypes = [POINTER(POINTER(drmaa_attr_names_t)), STRING, size_t] drmaa_get_vector_attribute_names = _lib.drmaa_get_vector_attribute_names drmaa_get_vector_attribute_names.restype = error_check drmaa_get_vector_attribute_names.argtypes = [POINTER(POINTER(drmaa_attr_names_t)), STRING, size_t] try: drmaa_get_num_attr_names = _lib.drmaa_get_num_attr_names drmaa_get_num_attr_names.restype = c_int drmaa_get_num_attr_names.argtypes = [POINTER(drmaa_attr_names_t), POINTER(c_int)] drmaa_get_num_attr_values = _lib.drmaa_get_num_attr_values drmaa_get_num_attr_values.restype = c_int drmaa_get_num_attr_values.argtypes = [POINTER(drmaa_attr_values_t), POINTER(c_int)] except AttributeError: # the above are present from 1.0 onward only pass drmaa_run_job = _lib.drmaa_run_job drmaa_run_job.restype = error_check drmaa_run_job.argtypes = [STRING, size_t, POINTER(drmaa_job_template_t), STRING, size_t] drmaa_run_bulk_jobs = _lib.drmaa_run_bulk_jobs drmaa_run_bulk_jobs.restype = error_check drmaa_run_bulk_jobs.argtypes = [POINTER(POINTER(drmaa_job_ids_t)), POINTER(drmaa_job_template_t), c_int, c_int, c_int, STRING, size_t] drmaa_control = _lib.drmaa_control drmaa_control.restype = error_check drmaa_control.argtypes = [STRING, c_int, STRING, size_t] drmaa_synchronize = _lib.drmaa_synchronize drmaa_synchronize.restype = error_check drmaa_synchronize.argtypes = [POINTER(STRING), c_long, c_int, STRING, size_t] drmaa_wait = _lib.drmaa_wait drmaa_wait.restype = error_check drmaa_wait.argtypes = [STRING, STRING, size_t, POINTER(c_int), c_long, POINTER(POINTER(drmaa_attr_values_t)), STRING, size_t] drmaa_wifexited = _lib.drmaa_wifexited drmaa_wifexited.restype = error_check drmaa_wifexited.argtypes = [POINTER(c_int), c_int, STRING, size_t] drmaa_wexitstatus = _lib.drmaa_wexitstatus drmaa_wexitstatus.restype = error_check drmaa_wexitstatus.argtypes = [POINTER(c_int), c_int, STRING, size_t] drmaa_wifsignaled = _lib.drmaa_wifsignaled drmaa_wifsignaled.restype = error_check drmaa_wifsignaled.argtypes = [POINTER(c_int), c_int, STRING, size_t] drmaa_wtermsig = _lib.drmaa_wtermsig drmaa_wtermsig.restype = error_check drmaa_wtermsig.argtypes = [STRING, size_t, c_int, STRING, size_t] drmaa_wcoredump = _lib.drmaa_wcoredump drmaa_wcoredump.restype = error_check drmaa_wcoredump.argtypes = [POINTER(c_int), c_int, STRING, size_t] drmaa_wifaborted = _lib.drmaa_wifaborted drmaa_wifaborted.restype = error_check drmaa_wifaborted.argtypes = [POINTER(c_int), c_int, STRING, size_t] drmaa_job_ps = _lib.drmaa_job_ps drmaa_job_ps.restype = error_check drmaa_job_ps.argtypes = [STRING, POINTER(c_int), STRING, size_t] drmaa_strerror = _lib.drmaa_strerror drmaa_strerror.restype = STRING drmaa_strerror.argtypes = [c_int] drmaa-0.5/drmaa.egg-info/0000755000076500007650000000000011706215743016356 5ustar sirolaesirolae00000000000000drmaa-0.5/drmaa.egg-info/dependency_links.txt0000644000076500007650000000000111706215743022424 0ustar sirolaesirolae00000000000000 drmaa-0.5/drmaa.egg-info/PKG-INFO0000644000076500007650000000144011706215743017452 0ustar sirolaesirolae00000000000000Metadata-Version: 1.0 Name: drmaa Version: 0.5 Summary: a python DRMAA library Home-page: http://drmaa-python.googlecode.com Author: Enrico Sirola Author-email: enrico.sirola@gmail.com License: BSD Download-URL: http://code.google.com/p/drmaa-python/downloads/list Description: UNKNOWN Keywords: python grid hpc drmaa Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Intended Audience :: System Administrators Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 2 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Distributed Computing drmaa-0.5/drmaa.egg-info/SOURCES.txt0000644000076500007650000000034611706215743020245 0ustar sirolaesirolae00000000000000setup.cfg setup.py drmaa/__init__.py drmaa/const.py drmaa/errors.py drmaa/helpers.py drmaa/nt.py drmaa/wrappers.py drmaa.egg-info/PKG-INFO drmaa.egg-info/SOURCES.txt drmaa.egg-info/dependency_links.txt drmaa.egg-info/top_level.txtdrmaa-0.5/drmaa.egg-info/top_level.txt0000644000076500007650000000000611706215743021104 0ustar sirolaesirolae00000000000000drmaa drmaa-0.5/PKG-INFO0000644000076500007650000000144011706215743014674 0ustar sirolaesirolae00000000000000Metadata-Version: 1.0 Name: drmaa Version: 0.5 Summary: a python DRMAA library Home-page: http://drmaa-python.googlecode.com Author: Enrico Sirola Author-email: enrico.sirola@gmail.com License: BSD Download-URL: http://code.google.com/p/drmaa-python/downloads/list Description: UNKNOWN Keywords: python grid hpc drmaa Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Intended Audience :: System Administrators Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 2 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Distributed Computing drmaa-0.5/setup.cfg0000644000076500007650000000021211706215743015414 0ustar sirolaesirolae00000000000000[egg_info] tag_svn_revision = 0 tag_build = tag_date = 0 [aliases] release = egg_info --no-svn-revision --tag-build '' sdist bdist_egg drmaa-0.5/setup.py0000644000076500007650000000374011706215704015313 0ustar sirolaesirolae00000000000000# ----------------------------------------------------------- # Copyright (C) 2009 StatPro Italia s.r.l. # # StatPro Italia # Via G. B. Vico 4 # I-20123 Milano # ITALY # # phone: +39 02 96875 1 # fax: +39 02 96875 605 # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the license for more details. # ----------------------------------------------------------- # # Author: Enrico Sirola from setuptools import setup, find_packages packages = find_packages() package_data = dict([ (x, ['test/*.py']) for x in packages]) setup( name="drmaa", version="0.5", packages=packages, package_data=package_data, author="Enrico Sirola", author_email="enrico.sirola@gmail.com", description="a python DRMAA library", license="BSD", keywords="python grid hpc drmaa", url="http://drmaa-python.googlecode.com", download_url="http://code.google.com/p/drmaa-python/downloads/list", tests_require='nose', test_suite='nose.collector', classifiers="""\ Development Status :: 4 - Beta Operating System :: OS Independent Intended Audience :: System Administrators Intended Audience :: Developers Intended Audience :: Science/Research License :: OSI Approved :: BSD License Programming Language :: Python :: 2 Topic :: Software Development :: Libraries :: Python Modules Topic :: System :: Distributed Computing""".split('\n'), )